drop from segments physcopy/vircopy invocations
[minix3.git] / servers / pm / trace.c
blob522a401f0f003c622dd34dded1fa479427962685
1 /* This file handles the process manager's part of debugging, using the
2 * ptrace system call. Most of the commands are passed on to the system
3 * task for completion.
5 * The debugging commands available are:
6 * T_STOP stop the process
7 * T_OK enable tracing by parent for this process
8 * T_GETINS return value from instruction space
9 * T_GETDATA return value from data space
10 * T_GETUSER return value from user process table
11 * T_SETINS set value in instruction space
12 * T_SETDATA set value in data space
13 * T_SETUSER set value in user process table
14 * T_RESUME resume execution
15 * T_EXIT exit
16 * T_STEP set trace bit
17 * T_SYSCALL trace system call
18 * T_ATTACH attach to an existing process
19 * T_DETACH detach from a traced process
20 * T_SETOPT set trace options
21 * T_GETRANGE get range of values
22 * T_SETRANGE set range of values
24 * The T_OK, T_ATTACH, T_EXIT, and T_SETOPT commands are handled here, and the
25 * T_RESUME, T_STEP, T_SYSCALL, and T_DETACH commands are partially handled
26 * here and completed by the system task. The rest are handled entirely by the
27 * system task.
30 #include "pm.h"
31 #include <minix/com.h>
32 #include <sys/ptrace.h>
33 #include <signal.h>
34 #include "mproc.h"
35 #include "param.h"
37 /*===========================================================================*
38 * do_trace *
39 *===========================================================================*/
40 int do_trace()
42 register struct mproc *child;
43 struct ptrace_range pr;
44 int i, r, req;
45 message m;
47 req = m_in.request;
49 /* The T_OK call is made by the child fork of the debugger before it execs
50 * the process to be traced. The T_ATTACH call is made by the debugger itself
51 * to attach to an existing process.
53 switch (req) {
54 case T_OK: /* enable tracing by parent for this proc */
55 if (mp->mp_tracer != NO_TRACER) return(EBUSY);
57 mp->mp_tracer = mp->mp_parent;
58 mp->mp_reply.reply_trace = 0;
59 return(OK);
61 case T_ATTACH: /* attach to an existing process */
62 if ((child = find_proc(m_in.pid)) == NULL) return(ESRCH);
63 if (child->mp_flags & EXITING) return(ESRCH);
65 /* For non-root processes, user and group ID must match. */
66 if (mp->mp_effuid != SUPER_USER &&
67 (mp->mp_effuid != child->mp_effuid ||
68 mp->mp_effgid != child->mp_effgid ||
69 child->mp_effuid != child->mp_realuid ||
70 child->mp_effgid != child->mp_realgid)) return(EPERM);
72 /* Only root may trace system servers. */
73 if (mp->mp_effuid != SUPER_USER && (child->mp_flags & PRIV_PROC))
74 return(EPERM);
76 /* System servers may not trace anyone. They can use sys_trace(). */
77 if (mp->mp_flags & PRIV_PROC) return(EPERM);
79 /* Can't trace self, PM or VM. */
80 if (child == mp || child->mp_endpoint == PM_PROC_NR ||
81 child->mp_endpoint == VM_PROC_NR) return(EPERM);
83 /* Can't trace a process that is already being traced. */
84 if (child->mp_tracer != NO_TRACER) return(EBUSY);
86 child->mp_tracer = who_p;
87 child->mp_trace_flags = TO_NOEXEC;
89 sig_proc(child, SIGSTOP, TRUE /*trace*/, FALSE /* ksig */);
91 mp->mp_reply.reply_trace = 0;
92 return(OK);
94 case T_DUMPCORE:
95 if ((child = find_proc(m_in.pid)) == NULL) return(ESRCH);
97 /* Allow dumpcore only if traced! */
98 if (child->mp_tracer != who_p) return(EPERM);
100 /* Tell VFS to dump the core. */
101 m.m_type = PM_DUMPCORE;
102 m.PM_PROC = mp->mp_endpoint;
103 m.PM_TRACED_PROC = child->mp_endpoint;
104 /* Note that m.PM_PROC != m.PM_TRACED_PROC
105 * (we use this to differentiate between a VFS core dump reply for a
106 * an exiting process and the one for a traced process) */
108 m.PM_TERM_SIG = child->mp_sigstatus;
109 m.PM_PATH = child->mp_name;
111 tell_vfs(mp, &m);
113 return(SUSPEND); /* Suspend the process until we receive reply from VFS */
115 case T_STOP: /* stop the process */
116 /* This call is not exposed to user programs, because its effect can be
117 * achieved better by sending the traced process a signal with kill(2).
119 return(EINVAL);
121 case T_READB_INS: /* special hack for reading text segments */
122 if (mp->mp_effuid != SUPER_USER) return(EPERM);
123 if ((child = find_proc(m_in.pid)) == NULL) return(ESRCH);
124 if (child->mp_flags & EXITING) return(ESRCH);
126 r = sys_trace(req, child->mp_endpoint, m_in.PMTRACE_ADDR, &m_in.data);
127 if (r != OK) return(r);
129 mp->mp_reply.reply_trace = m_in.data;
130 return(OK);
132 case T_WRITEB_INS: /* special hack for patching text segments */
133 if (mp->mp_effuid != SUPER_USER) return(EPERM);
134 if ((child = find_proc(m_in.pid)) == NULL) return(ESRCH);
135 if (child->mp_flags & EXITING) return(ESRCH);
137 #if 0
138 /* Should check for shared text */
140 /* Make sure the text segment is not used as a source for shared
141 * text.
143 child->mp_ino = 0;
144 child->mp_dev = 0;
145 child->mp_ctime = 0;
146 #endif
148 r = sys_trace(req, child->mp_endpoint, m_in.PMTRACE_ADDR, &m_in.data);
149 if (r != OK) return(r);
151 mp->mp_reply.reply_trace = m_in.data;
152 return(OK);
155 /* All the other calls are made by the tracing process to control execution
156 * of the child. For all these calls, the child must be stopped.
158 if ((child = find_proc(m_in.pid)) == NULL) return(ESRCH);
159 if (child->mp_flags & EXITING) return(ESRCH);
160 if (child->mp_tracer != who_p) return(ESRCH);
161 if (!(child->mp_flags & STOPPED)) return(EBUSY);
163 switch (req) {
164 case T_EXIT: /* exit */
165 child->mp_flags |= TRACE_EXIT;
167 /* Defer the exit if the traced process has an VFS call pending. */
168 if (child->mp_flags & VFS_CALL)
169 child->mp_exitstatus = (int) m_in.data; /* save for later */
170 else
171 exit_proc(child, (int) m_in.data, FALSE /*dump_core*/);
173 /* Do not reply to the caller until VFS has processed the exit
174 * request.
176 return(SUSPEND);
178 case T_SETOPT: /* set trace options */
179 child->mp_trace_flags = m_in.data;
181 mp->mp_reply.reply_trace = 0;
182 return(OK);
184 case T_GETRANGE:
185 case T_SETRANGE: /* get/set range of values */
186 r = sys_datacopy(who_e, (vir_bytes) m_in.PMTRACE_ADDR,
187 SELF, (vir_bytes) &pr, (phys_bytes) sizeof(pr));
188 if (r != OK) return(r);
190 if (pr.pr_space != TS_INS && pr.pr_space != TS_DATA) return(EINVAL);
191 if (pr.pr_size == 0 || pr.pr_size > LONG_MAX) return(EINVAL);
193 if (req == T_GETRANGE)
194 r = sys_vircopy(child->mp_endpoint, (vir_bytes) pr.pr_addr,
195 who_e, (vir_bytes) pr.pr_ptr,
196 (phys_bytes) pr.pr_size);
197 else
198 r = sys_vircopy(who_e, (vir_bytes) pr.pr_ptr,
199 child->mp_endpoint, (vir_bytes) pr.pr_addr,
200 (phys_bytes) pr.pr_size);
202 if (r != OK) return(r);
204 mp->mp_reply.reply_trace = 0;
205 return(OK);
207 case T_DETACH: /* detach from traced process */
208 if (m_in.data < 0 || m_in.data >= _NSIG) return(EINVAL);
210 child->mp_tracer = NO_TRACER;
212 /* Let all tracer-pending signals through the filter. */
213 for (i = 1; i < _NSIG; i++) {
214 if (sigismember(&child->mp_sigtrace, i)) {
215 sigdelset(&child->mp_sigtrace, i);
216 check_sig(child->mp_pid, i, FALSE /* ksig */);
220 if (m_in.data > 0) { /* issue signal */
221 sig_proc(child, (int) m_in.data, TRUE /*trace*/,
222 FALSE /* ksig */);
225 /* Resume the child as if nothing ever happened. */
226 child->mp_flags &= ~STOPPED;
227 child->mp_trace_flags = 0;
229 check_pending(child);
231 break;
233 case T_RESUME:
234 case T_STEP:
235 case T_SYSCALL: /* resume execution */
236 if (m_in.data < 0 || m_in.data >= _NSIG) return(EINVAL);
238 if (m_in.data > 0) { /* issue signal */
239 sig_proc(child, (int) m_in.data, FALSE /*trace*/,
240 FALSE /* ksig */);
243 /* If there are any other signals waiting to be delivered,
244 * feign a successful resumption.
246 for (i = 1; i < _NSIG; i++) {
247 if (sigismember(&child->mp_sigtrace, i)) {
248 mp->mp_reply.reply_trace = 0;
249 return(OK);
253 child->mp_flags &= ~STOPPED;
255 check_pending(child);
257 break;
259 r = sys_trace(req, child->mp_endpoint, m_in.PMTRACE_ADDR, &m_in.data);
260 if (r != OK) return(r);
262 mp->mp_reply.reply_trace = m_in.data;
263 return(OK);
266 /*===========================================================================*
267 * stop_proc *
268 *===========================================================================*/
269 void stop_proc(rmp, signo)
270 register struct mproc *rmp;
271 int signo;
273 /* A traced process got a signal so stop it. */
275 register struct mproc *rpmp = mproc + rmp->mp_tracer;
276 int r;
278 r = sys_trace(T_STOP, rmp->mp_endpoint, 0L, (long *) 0);
279 if (r != OK) panic("sys_trace failed: %d", r);
281 rmp->mp_flags |= STOPPED;
282 if (wait_test(rpmp, rmp)) {
283 sigdelset(&rmp->mp_sigtrace, signo);
285 rpmp->mp_flags &= ~WAITING; /* parent is no longer waiting */
286 rpmp->mp_reply.reply_res2 = 0177 | (signo << 8);
287 setreply(rmp->mp_tracer, rmp->mp_pid);