tools/llvm: Do not build with symbols
[minix3.git] / minix / servers / pm / trace.c
blob6d11a4762fc15e98f932837bdbdd7baeb8bfe03b
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 <minix/callnr.h>
33 #include <sys/ptrace.h>
34 #include <sys/wait.h>
35 #include <signal.h>
36 #include "mproc.h"
38 /*===========================================================================*
39 * do_trace *
40 *===========================================================================*/
41 int do_trace()
43 register struct mproc *child;
44 struct ptrace_range pr;
45 int i, r, req;
47 req = m_in.m_lc_pm_ptrace.req;
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.m_pm_lc_ptrace.data = 0;
59 return(OK);
61 case T_ATTACH: /* attach to an existing process */
62 if ((child = find_proc(m_in.m_lc_pm_ptrace.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.m_pm_lc_ptrace.data = 0;
92 return(OK);
94 case T_STOP: /* stop the process */
95 /* This call is not exposed to user programs, because its effect can be
96 * achieved better by sending the traced process a signal with kill(2).
98 return(EINVAL);
100 case T_READB_INS: /* special hack for reading text segments */
101 if (mp->mp_effuid != SUPER_USER) return(EPERM);
102 if ((child = find_proc(m_in.m_lc_pm_ptrace.pid)) == NULL) return(ESRCH);
103 if (child->mp_flags & EXITING) return(ESRCH);
105 r = sys_trace(req, child->mp_endpoint, m_in.m_lc_pm_ptrace.addr,
106 &m_in.m_lc_pm_ptrace.data);
107 if (r != OK) return(r);
109 mp->mp_reply.m_pm_lc_ptrace.data = m_in.m_lc_pm_ptrace.data;
110 return(OK);
112 case T_WRITEB_INS: /* special hack for patching text segments */
113 if (mp->mp_effuid != SUPER_USER) return(EPERM);
114 if ((child = find_proc(m_in.m_lc_pm_ptrace.pid)) == NULL) return(ESRCH);
115 if (child->mp_flags & EXITING) return(ESRCH);
117 #if 0
118 /* Should check for shared text */
120 /* Make sure the text segment is not used as a source for shared
121 * text.
123 child->mp_ino = 0;
124 child->mp_dev = 0;
125 child->mp_ctime = 0;
126 #endif
128 r = sys_trace(req, child->mp_endpoint, m_in.m_lc_pm_ptrace.addr,
129 &m_in.m_lc_pm_ptrace.data);
130 if (r != OK) return(r);
132 mp->mp_reply.m_pm_lc_ptrace.data = m_in.m_lc_pm_ptrace.data;
133 return(OK);
136 /* All the other calls are made by the tracing process to control execution
137 * of the child. For all these calls, the child must be stopped.
139 if ((child = find_proc(m_in.m_lc_pm_ptrace.pid)) == NULL) return(ESRCH);
140 if (child->mp_flags & EXITING) return(ESRCH);
141 if (child->mp_tracer != who_p) return(ESRCH);
142 if (!(child->mp_flags & TRACE_STOPPED)) return(EBUSY);
144 switch (req) {
145 case T_EXIT: /* exit */
146 child->mp_flags |= TRACE_EXIT;
148 /* Defer the exit if the traced process has an VFS call pending. */
149 if (child->mp_flags & VFS_CALL)
150 child->mp_exitstatus = m_in.m_lc_pm_ptrace.data; /* save it */
151 else
152 exit_proc(child, m_in.m_lc_pm_ptrace.data,
153 FALSE /*dump_core*/);
155 /* Do not reply to the caller until VFS has processed the exit
156 * request.
158 return(SUSPEND);
160 case T_SETOPT: /* set trace options */
161 child->mp_trace_flags = m_in.m_lc_pm_ptrace.data;
163 mp->mp_reply.m_pm_lc_ptrace.data = 0;
164 return(OK);
166 case T_GETRANGE:
167 case T_SETRANGE: /* get/set range of values */
168 r = sys_datacopy(who_e, m_in.m_lc_pm_ptrace.addr, SELF, (vir_bytes)&pr,
169 (phys_bytes)sizeof(pr));
170 if (r != OK) return(r);
172 if (pr.pr_space != TS_INS && pr.pr_space != TS_DATA) return(EINVAL);
173 if (pr.pr_size == 0 || pr.pr_size > LONG_MAX) return(EINVAL);
175 if (req == T_GETRANGE)
176 r = sys_vircopy(child->mp_endpoint, (vir_bytes) pr.pr_addr,
177 who_e, (vir_bytes) pr.pr_ptr,
178 (phys_bytes) pr.pr_size, 0);
179 else
180 r = sys_vircopy(who_e, (vir_bytes) pr.pr_ptr,
181 child->mp_endpoint, (vir_bytes) pr.pr_addr,
182 (phys_bytes) pr.pr_size, 0);
184 if (r != OK) return(r);
186 mp->mp_reply.m_pm_lc_ptrace.data = 0;
187 return(OK);
189 case T_DETACH: /* detach from traced process */
190 if (m_in.m_lc_pm_ptrace.data < 0 || m_in.m_lc_pm_ptrace.data >= _NSIG)
191 return(EINVAL);
193 child->mp_tracer = NO_TRACER;
195 /* Let all tracer-pending signals through the filter. */
196 for (i = 1; i < _NSIG; i++) {
197 if (sigismember(&child->mp_sigtrace, i)) {
198 sigdelset(&child->mp_sigtrace, i);
199 check_sig(child->mp_pid, i, FALSE /* ksig */);
203 if (m_in.m_lc_pm_ptrace.data > 0) { /* issue signal */
204 sig_proc(child, m_in.m_lc_pm_ptrace.data, TRUE /*trace*/,
205 FALSE /* ksig */);
208 /* Resume the child as if nothing ever happened. */
209 child->mp_flags &= ~TRACE_STOPPED;
210 child->mp_trace_flags = 0;
212 check_pending(child);
214 break;
216 case T_RESUME:
217 case T_STEP:
218 case T_SYSCALL: /* resume execution */
219 if (m_in.m_lc_pm_ptrace.data < 0 || m_in.m_lc_pm_ptrace.data >= _NSIG)
220 return(EINVAL);
222 if (m_in.m_lc_pm_ptrace.data > 0) { /* issue signal */
223 sig_proc(child, m_in.m_lc_pm_ptrace.data, FALSE /*trace*/,
224 FALSE /* ksig */);
227 /* If there are any other signals waiting to be delivered,
228 * feign a successful resumption.
230 for (i = 1; i < _NSIG; i++) {
231 if (sigismember(&child->mp_sigtrace, i)) {
232 mp->mp_reply.m_pm_lc_ptrace.data = 0;
233 return(OK);
237 child->mp_flags &= ~TRACE_STOPPED;
239 check_pending(child);
241 break;
243 r = sys_trace(req, child->mp_endpoint, m_in.m_lc_pm_ptrace.addr,
244 &m_in.m_lc_pm_ptrace.data);
245 if (r != OK) return(r);
247 mp->mp_reply.m_pm_lc_ptrace.data = m_in.m_lc_pm_ptrace.data;
248 return(OK);
251 /*===========================================================================*
252 * trace_stop *
253 *===========================================================================*/
254 void trace_stop(rmp, signo)
255 register struct mproc *rmp;
256 int signo;
258 /* A traced process got a signal so stop it. */
260 register struct mproc *rpmp = mproc + rmp->mp_tracer;
261 int r;
263 r = sys_trace(T_STOP, rmp->mp_endpoint, 0L, (long *) 0);
264 if (r != OK) panic("sys_trace failed: %d", r);
266 rmp->mp_flags |= TRACE_STOPPED;
267 if (wait_test(rpmp, rmp)) {
268 sigdelset(&rmp->mp_sigtrace, signo);
270 rpmp->mp_flags &= ~WAITING; /* parent is no longer waiting */
271 rpmp->mp_reply.m_pm_lc_waitpid.status = W_STOPCODE(signo);
272 reply(rmp->mp_tracer, rmp->mp_pid);