1521fa314SDavid van Moolenbroek
2521fa314SDavid van Moolenbroek #include "inc.h"
3521fa314SDavid van Moolenbroek
4521fa314SDavid van Moolenbroek #include <minix/com.h>
5521fa314SDavid van Moolenbroek #include <minix/callnr.h>
6521fa314SDavid van Moolenbroek #include <minix/endpoint.h>
7521fa314SDavid van Moolenbroek
8521fa314SDavid van Moolenbroek static const struct calls *call_table[] = {
9521fa314SDavid van Moolenbroek &pm_calls,
10521fa314SDavid van Moolenbroek &vfs_calls,
11521fa314SDavid van Moolenbroek &rs_calls,
12e4e21ee1SDavid van Moolenbroek &mib_calls,
13521fa314SDavid van Moolenbroek &vm_calls,
14521fa314SDavid van Moolenbroek &ipc_calls,
15521fa314SDavid van Moolenbroek };
16521fa314SDavid van Moolenbroek
17521fa314SDavid van Moolenbroek /*
18521fa314SDavid van Moolenbroek * Find a call handler for the given endpoint, call number pair. Return NULL
19521fa314SDavid van Moolenbroek * if no call handler for this call exists.
20521fa314SDavid van Moolenbroek */
21521fa314SDavid van Moolenbroek static const struct call_handler *
find_handler(endpoint_t endpt,int call_nr)22521fa314SDavid van Moolenbroek find_handler(endpoint_t endpt, int call_nr)
23521fa314SDavid van Moolenbroek {
24b58e161cSDavid van Moolenbroek unsigned int i, index;
25521fa314SDavid van Moolenbroek
26521fa314SDavid van Moolenbroek for (i = 0; i < COUNT(call_table); i++) {
27521fa314SDavid van Moolenbroek if (call_table[i]->endpt != ANY &&
28521fa314SDavid van Moolenbroek call_table[i]->endpt != endpt)
29521fa314SDavid van Moolenbroek continue;
30521fa314SDavid van Moolenbroek
31b58e161cSDavid van Moolenbroek if ((unsigned int)call_nr < call_table[i]->base)
32521fa314SDavid van Moolenbroek continue;
33521fa314SDavid van Moolenbroek
34b58e161cSDavid van Moolenbroek index = (unsigned int)call_nr - call_table[i]->base;
35521fa314SDavid van Moolenbroek
36521fa314SDavid van Moolenbroek if (index >= call_table[i]->count)
37521fa314SDavid van Moolenbroek continue;
38521fa314SDavid van Moolenbroek
39521fa314SDavid van Moolenbroek if (call_table[i]->map[index].outfunc == NULL)
40521fa314SDavid van Moolenbroek continue;
41521fa314SDavid van Moolenbroek
42521fa314SDavid van Moolenbroek return &call_table[i]->map[index];
43521fa314SDavid van Moolenbroek }
44521fa314SDavid van Moolenbroek
45521fa314SDavid van Moolenbroek return NULL;
46521fa314SDavid van Moolenbroek }
47521fa314SDavid van Moolenbroek
48521fa314SDavid van Moolenbroek /*
49521fa314SDavid van Moolenbroek * Print an endpoint.
50521fa314SDavid van Moolenbroek */
51521fa314SDavid van Moolenbroek void
put_endpoint(struct trace_proc * proc,const char * name,endpoint_t endpt)52521fa314SDavid van Moolenbroek put_endpoint(struct trace_proc * proc, const char * name, endpoint_t endpt)
53521fa314SDavid van Moolenbroek {
54521fa314SDavid van Moolenbroek const char *text = NULL;
55521fa314SDavid van Moolenbroek
56521fa314SDavid van Moolenbroek if (!valuesonly) {
57521fa314SDavid van Moolenbroek switch (endpt) {
58521fa314SDavid van Moolenbroek TEXT(ASYNCM);
59521fa314SDavid van Moolenbroek TEXT(IDLE);
60521fa314SDavid van Moolenbroek TEXT(CLOCK);
61521fa314SDavid van Moolenbroek TEXT(SYSTEM);
62521fa314SDavid van Moolenbroek TEXT(KERNEL);
63521fa314SDavid van Moolenbroek TEXT(PM_PROC_NR);
64521fa314SDavid van Moolenbroek TEXT(VFS_PROC_NR);
65521fa314SDavid van Moolenbroek TEXT(RS_PROC_NR);
66521fa314SDavid van Moolenbroek TEXT(MEM_PROC_NR);
67521fa314SDavid van Moolenbroek TEXT(SCHED_PROC_NR);
68521fa314SDavid van Moolenbroek TEXT(TTY_PROC_NR);
69521fa314SDavid van Moolenbroek TEXT(DS_PROC_NR);
70e4e21ee1SDavid van Moolenbroek TEXT(MIB_PROC_NR);
71521fa314SDavid van Moolenbroek TEXT(VM_PROC_NR);
72521fa314SDavid van Moolenbroek TEXT(PFS_PROC_NR);
73521fa314SDavid van Moolenbroek TEXT(ANY);
74521fa314SDavid van Moolenbroek TEXT(NONE);
75521fa314SDavid van Moolenbroek TEXT(SELF);
76521fa314SDavid van Moolenbroek }
77521fa314SDavid van Moolenbroek }
78521fa314SDavid van Moolenbroek
79521fa314SDavid van Moolenbroek if (text != NULL)
80521fa314SDavid van Moolenbroek put_field(proc, name, text);
81521fa314SDavid van Moolenbroek else
82521fa314SDavid van Moolenbroek put_value(proc, name, "%d", endpt);
83521fa314SDavid van Moolenbroek }
84521fa314SDavid van Moolenbroek
85521fa314SDavid van Moolenbroek /*
86521fa314SDavid van Moolenbroek * Print a message structure. The source field will be printed only if the
87521fa314SDavid van Moolenbroek * PF_ALT flag is given.
88521fa314SDavid van Moolenbroek */
89521fa314SDavid van Moolenbroek static void
put_message(struct trace_proc * proc,const char * name,int flags,vir_bytes addr)90521fa314SDavid van Moolenbroek put_message(struct trace_proc * proc, const char * name, int flags,
91521fa314SDavid van Moolenbroek vir_bytes addr)
92521fa314SDavid van Moolenbroek {
93521fa314SDavid van Moolenbroek message m;
94521fa314SDavid van Moolenbroek
95521fa314SDavid van Moolenbroek if (!put_open_struct(proc, name, flags, addr, &m, sizeof(m)))
96521fa314SDavid van Moolenbroek return;
97521fa314SDavid van Moolenbroek
98521fa314SDavid van Moolenbroek if (flags & PF_ALT)
99521fa314SDavid van Moolenbroek put_endpoint(proc, "m_source", m.m_source);
100521fa314SDavid van Moolenbroek
101*c38dbb97SDavid van Moolenbroek put_value(proc, "m_type", "0x%x", m.m_type);
102521fa314SDavid van Moolenbroek
103521fa314SDavid van Moolenbroek put_close_struct(proc, FALSE /*all*/);
104521fa314SDavid van Moolenbroek }
105521fa314SDavid van Moolenbroek
106521fa314SDavid van Moolenbroek /*
107521fa314SDavid van Moolenbroek * Print the call's equals sign, which also implies that the parameters part of
108521fa314SDavid van Moolenbroek * the call has been fully printed and the corresponding closing parenthesis
109521fa314SDavid van Moolenbroek * may have to be printed, if it has not been printed already.
110521fa314SDavid van Moolenbroek */
111521fa314SDavid van Moolenbroek void
put_equals(struct trace_proc * proc)112521fa314SDavid van Moolenbroek put_equals(struct trace_proc * proc)
113521fa314SDavid van Moolenbroek {
114521fa314SDavid van Moolenbroek
115521fa314SDavid van Moolenbroek /*
116521fa314SDavid van Moolenbroek * Do not allow multiple equals signs on a single line. This check is
117521fa314SDavid van Moolenbroek * protection against badly written handlers. It does not work for the
118521fa314SDavid van Moolenbroek * no-return type, but such calls are rare and less error prone anyway.
119521fa314SDavid van Moolenbroek */
120521fa314SDavid van Moolenbroek assert((proc->call_flags & (CF_DONE | CF_NORETURN)) != CF_DONE);
121521fa314SDavid van Moolenbroek
122521fa314SDavid van Moolenbroek /*
123521fa314SDavid van Moolenbroek * We allow (and in fact force) handlers to call put_equals in order to
124521fa314SDavid van Moolenbroek * indicate that the call's parameters block has ended, so we must end
125521fa314SDavid van Moolenbroek * the block here, if we hadn't done so before.
126521fa314SDavid van Moolenbroek */
127521fa314SDavid van Moolenbroek if (!(proc->call_flags & CF_DONE)) {
128521fa314SDavid van Moolenbroek put_close(proc, ") ");
129521fa314SDavid van Moolenbroek
130521fa314SDavid van Moolenbroek proc->call_flags |= CF_DONE;
131521fa314SDavid van Moolenbroek }
132521fa314SDavid van Moolenbroek
133521fa314SDavid van Moolenbroek put_align(proc);
134521fa314SDavid van Moolenbroek put_text(proc, "= ");
135521fa314SDavid van Moolenbroek
136521fa314SDavid van Moolenbroek format_set_sep(proc, NULL);
137521fa314SDavid van Moolenbroek }
138521fa314SDavid van Moolenbroek
139521fa314SDavid van Moolenbroek /*
140521fa314SDavid van Moolenbroek * Print the primary result of a call, after the equals sign. It is always
141521fa314SDavid van Moolenbroek * possible that this is an IPC-level or other low-level error, in which case
142521fa314SDavid van Moolenbroek * this takes precedence, which is why this function must be called to print
143521fa314SDavid van Moolenbroek * the result if the call failed in any way at all; it may or may not be used
144521fa314SDavid van Moolenbroek * if the call succeeded. For regular call results, default MINIX3/POSIX
145521fa314SDavid van Moolenbroek * semantics are used: if the return value is negative, the actual call failed
146521fa314SDavid van Moolenbroek * with -1 and the negative return value is the call's error code. The caller
147521fa314SDavid van Moolenbroek * may consider other cases a failure (e.g., waitpid() returning 0), but
148521fa314SDavid van Moolenbroek * negative return values *not* signifying an error are currently not supported
149521fa314SDavid van Moolenbroek * since they are not present in MINIX3.
150521fa314SDavid van Moolenbroek */
151521fa314SDavid van Moolenbroek void
put_result(struct trace_proc * proc)152521fa314SDavid van Moolenbroek put_result(struct trace_proc * proc)
153521fa314SDavid van Moolenbroek {
154521fa314SDavid van Moolenbroek const char *errname;
155521fa314SDavid van Moolenbroek int value;
156521fa314SDavid van Moolenbroek
157521fa314SDavid van Moolenbroek /* This call should always be preceded by a put_equals call. */
158521fa314SDavid van Moolenbroek assert(proc->call_flags & CF_DONE);
159521fa314SDavid van Moolenbroek
160521fa314SDavid van Moolenbroek /*
161521fa314SDavid van Moolenbroek * If we failed to copy in the result register or message, print a
162521fa314SDavid van Moolenbroek * basic error and nothing else.
163521fa314SDavid van Moolenbroek */
164521fa314SDavid van Moolenbroek if (proc->call_flags & (CF_REG_ERR | CF_MSG_ERR)) {
165521fa314SDavid van Moolenbroek put_text(proc, "<fault>");
166521fa314SDavid van Moolenbroek
167521fa314SDavid van Moolenbroek return;
168521fa314SDavid van Moolenbroek }
169521fa314SDavid van Moolenbroek
170521fa314SDavid van Moolenbroek /*
171521fa314SDavid van Moolenbroek * If we are printing a system call rather than an IPC call, and an
172521fa314SDavid van Moolenbroek * error occurred at the IPC level, prefix the output with "<ipc>" to
173521fa314SDavid van Moolenbroek * indicate the IPC failure. If we are printing an IPC call, an IPC-
174521fa314SDavid van Moolenbroek * level result is implied, so we do not print this.
175521fa314SDavid van Moolenbroek */
176521fa314SDavid van Moolenbroek if (proc->call_handler != NULL && (proc->call_flags & CF_IPC_ERR))
177521fa314SDavid van Moolenbroek put_text(proc, "<ipc> ");
178521fa314SDavid van Moolenbroek
179521fa314SDavid van Moolenbroek value = proc->call_result;
180521fa314SDavid van Moolenbroek
181521fa314SDavid van Moolenbroek if (value >= 0)
182521fa314SDavid van Moolenbroek put_fmt(proc, "%d", value);
183521fa314SDavid van Moolenbroek else if (!valuesonly && (errname = get_error_name(-value)) != NULL)
184521fa314SDavid van Moolenbroek put_fmt(proc, "-1 [%s]", errname);
185521fa314SDavid van Moolenbroek else
186521fa314SDavid van Moolenbroek put_fmt(proc, "-1 [%d]", -value);
187521fa314SDavid van Moolenbroek
188521fa314SDavid van Moolenbroek format_set_sep(proc, " ");
189521fa314SDavid van Moolenbroek }
190521fa314SDavid van Moolenbroek
191521fa314SDavid van Moolenbroek /*
192521fa314SDavid van Moolenbroek * The default enter-call (out) printer, which prints no parameters and is thus
193521fa314SDavid van Moolenbroek * immediately done with printing parameters.
194521fa314SDavid van Moolenbroek */
195521fa314SDavid van Moolenbroek int
default_out(struct trace_proc * __unused proc,const message * __unused m_out)196521fa314SDavid van Moolenbroek default_out(struct trace_proc * __unused proc, const message * __unused m_out)
197521fa314SDavid van Moolenbroek {
198521fa314SDavid van Moolenbroek
199521fa314SDavid van Moolenbroek return CT_DONE;
200521fa314SDavid van Moolenbroek }
201521fa314SDavid van Moolenbroek
202521fa314SDavid van Moolenbroek /*
203521fa314SDavid van Moolenbroek * The default leave-call (in) printer, which simply prints the call result,
204521fa314SDavid van Moolenbroek * possibly preceded by an equals sign if none was printed yet. For obvious
205521fa314SDavid van Moolenbroek * reasons, if the handler's out printer returned CT_NOTDONE, this default
206521fa314SDavid van Moolenbroek * printer must not be used.
207521fa314SDavid van Moolenbroek */
208521fa314SDavid van Moolenbroek void
default_in(struct trace_proc * proc,const message * __unused m_out,const message * __unused m_in,int __unused failed)209521fa314SDavid van Moolenbroek default_in(struct trace_proc * proc, const message * __unused m_out,
210521fa314SDavid van Moolenbroek const message * __unused m_in, int __unused failed)
211521fa314SDavid van Moolenbroek {
212521fa314SDavid van Moolenbroek
213521fa314SDavid van Moolenbroek if ((proc->call_flags & (CF_DONE | CF_NORETURN)) != CF_DONE)
214521fa314SDavid van Moolenbroek put_equals(proc);
215521fa314SDavid van Moolenbroek put_result(proc);
216521fa314SDavid van Moolenbroek }
217521fa314SDavid van Moolenbroek
218521fa314SDavid van Moolenbroek /*
219521fa314SDavid van Moolenbroek * Prepare a sendrec call, by copying in the request message, determining
220521fa314SDavid van Moolenbroek * whether it is one of the calls that the tracing engine should know about,
221521fa314SDavid van Moolenbroek * searching for a handler for the call, and returning a name for the call.
222521fa314SDavid van Moolenbroek */
223521fa314SDavid van Moolenbroek static const char *
sendrec_prepare(struct trace_proc * proc,endpoint_t endpt,vir_bytes addr,int * trace_class)224521fa314SDavid van Moolenbroek sendrec_prepare(struct trace_proc * proc, endpoint_t endpt, vir_bytes addr,
225521fa314SDavid van Moolenbroek int * trace_class)
226521fa314SDavid van Moolenbroek {
227521fa314SDavid van Moolenbroek const char *name;
228521fa314SDavid van Moolenbroek int r;
229521fa314SDavid van Moolenbroek
230521fa314SDavid van Moolenbroek r = mem_get_data(proc->pid, addr, &proc->m_out, sizeof(proc->m_out));
231521fa314SDavid van Moolenbroek
232521fa314SDavid van Moolenbroek if (r == 0) {
233521fa314SDavid van Moolenbroek if (endpt == PM_PROC_NR) {
234521fa314SDavid van Moolenbroek if (proc->m_out.m_type == PM_EXEC)
235521fa314SDavid van Moolenbroek *trace_class = TC_EXEC;
236521fa314SDavid van Moolenbroek else if (proc->m_out.m_type == PM_SIGRETURN)
237521fa314SDavid van Moolenbroek *trace_class = TC_SIGRET;
238521fa314SDavid van Moolenbroek }
239521fa314SDavid van Moolenbroek
240521fa314SDavid van Moolenbroek proc->call_handler = find_handler(endpt, proc->m_out.m_type);
241521fa314SDavid van Moolenbroek } else
242521fa314SDavid van Moolenbroek proc->call_handler = NULL;
243521fa314SDavid van Moolenbroek
244521fa314SDavid van Moolenbroek if (proc->call_handler != NULL) {
245521fa314SDavid van Moolenbroek if (proc->call_handler->namefunc != NULL)
246521fa314SDavid van Moolenbroek name = proc->call_handler->namefunc(&proc->m_out);
247521fa314SDavid van Moolenbroek else
248521fa314SDavid van Moolenbroek name = proc->call_handler->name;
249521fa314SDavid van Moolenbroek
250521fa314SDavid van Moolenbroek assert(name != NULL);
251521fa314SDavid van Moolenbroek } else
252521fa314SDavid van Moolenbroek name = "ipc_sendrec";
253521fa314SDavid van Moolenbroek
254521fa314SDavid van Moolenbroek return name;
255521fa314SDavid van Moolenbroek }
256521fa314SDavid van Moolenbroek
257521fa314SDavid van Moolenbroek /*
258521fa314SDavid van Moolenbroek * Print the outgoing (request) part of a sendrec call. If we found a call
259521fa314SDavid van Moolenbroek * handler for the call, let the handler generate output. Otherwise, print the
260521fa314SDavid van Moolenbroek * sendrec call at the kernel IPC level. Return the resulting call flags.
261521fa314SDavid van Moolenbroek */
262521fa314SDavid van Moolenbroek static unsigned int
sendrec_out(struct trace_proc * proc,endpoint_t endpt,vir_bytes addr)263521fa314SDavid van Moolenbroek sendrec_out(struct trace_proc * proc, endpoint_t endpt, vir_bytes addr)
264521fa314SDavid van Moolenbroek {
265521fa314SDavid van Moolenbroek
266521fa314SDavid van Moolenbroek if (proc->call_handler != NULL) {
267521fa314SDavid van Moolenbroek return proc->call_handler->outfunc(proc, &proc->m_out);
268521fa314SDavid van Moolenbroek } else {
269521fa314SDavid van Moolenbroek put_endpoint(proc, "src_dest", endpt);
270521fa314SDavid van Moolenbroek /*
271521fa314SDavid van Moolenbroek * We have already copied in the message, but if we used m_out
272521fa314SDavid van Moolenbroek * and PF_LOCADDR here, a copy failure would cause "&.." to be
273521fa314SDavid van Moolenbroek * printed rather than the actual message address.
274521fa314SDavid van Moolenbroek */
275521fa314SDavid van Moolenbroek put_message(proc, "m_ptr", 0, addr);
276521fa314SDavid van Moolenbroek
277521fa314SDavid van Moolenbroek return CT_DONE;
278521fa314SDavid van Moolenbroek }
279521fa314SDavid van Moolenbroek }
280521fa314SDavid van Moolenbroek
281521fa314SDavid van Moolenbroek /*
282521fa314SDavid van Moolenbroek * Print the incoming (reply) part of a sendrec call. Copy in the reply
283521fa314SDavid van Moolenbroek * message, determine whether the call is considered to have failed, and let
284521fa314SDavid van Moolenbroek * the call handler do the rest. If no call handler was found, print an
285521fa314SDavid van Moolenbroek * IPC-level result.
286521fa314SDavid van Moolenbroek */
287521fa314SDavid van Moolenbroek static void
sendrec_in(struct trace_proc * proc,int failed)288521fa314SDavid van Moolenbroek sendrec_in(struct trace_proc * proc, int failed)
289521fa314SDavid van Moolenbroek {
290521fa314SDavid van Moolenbroek message m_in;
291521fa314SDavid van Moolenbroek
292521fa314SDavid van Moolenbroek if (failed) {
293521fa314SDavid van Moolenbroek /* The call failed at the IPC level. */
294521fa314SDavid van Moolenbroek memset(&m_in, 0, sizeof(m_in)); /* not supposed to be used */
295521fa314SDavid van Moolenbroek assert(proc->call_flags & CF_IPC_ERR);
296521fa314SDavid van Moolenbroek } else if (mem_get_data(proc->pid, proc->m_addr, &m_in,
297521fa314SDavid van Moolenbroek sizeof(m_in)) != 0) {
298521fa314SDavid van Moolenbroek /* The reply message is somehow unavailable to us. */
299521fa314SDavid van Moolenbroek memset(&m_in, 0, sizeof(m_in)); /* not supposed to be used */
300521fa314SDavid van Moolenbroek proc->call_result = EGENERIC; /* not supposed to be used */
301521fa314SDavid van Moolenbroek proc->call_flags |= CF_MSG_ERR;
302521fa314SDavid van Moolenbroek failed = PF_FAILED;
303521fa314SDavid van Moolenbroek } else {
304521fa314SDavid van Moolenbroek /* The result is for the actual call. */
305521fa314SDavid van Moolenbroek proc->call_result = m_in.m_type;
306521fa314SDavid van Moolenbroek failed = (proc->call_result < 0) ? PF_FAILED : 0;
307521fa314SDavid van Moolenbroek }
308521fa314SDavid van Moolenbroek
309521fa314SDavid van Moolenbroek if (proc->call_handler != NULL)
310521fa314SDavid van Moolenbroek proc->call_handler->infunc(proc, &proc->m_out, &m_in, failed);
311521fa314SDavid van Moolenbroek else
312521fa314SDavid van Moolenbroek put_result(proc);
313521fa314SDavid van Moolenbroek }
314521fa314SDavid van Moolenbroek
315521fa314SDavid van Moolenbroek /*
316521fa314SDavid van Moolenbroek * Perform preparations for printing a system call. Return two things: the
317521fa314SDavid van Moolenbroek * name to use for the call, and the trace class of the call.
318521fa314SDavid van Moolenbroek * special treatment).
319521fa314SDavid van Moolenbroek */
320521fa314SDavid van Moolenbroek static const char *
call_prepare(struct trace_proc * proc,reg_t reg[3],int * trace_class)321521fa314SDavid van Moolenbroek call_prepare(struct trace_proc * proc, reg_t reg[3], int * trace_class)
322521fa314SDavid van Moolenbroek {
323521fa314SDavid van Moolenbroek
324521fa314SDavid van Moolenbroek switch (proc->call_type) {
325521fa314SDavid van Moolenbroek case SENDREC:
326521fa314SDavid van Moolenbroek return sendrec_prepare(proc, (endpoint_t)reg[1],
327521fa314SDavid van Moolenbroek (vir_bytes)reg[2], trace_class);
328521fa314SDavid van Moolenbroek
329521fa314SDavid van Moolenbroek case SEND:
330521fa314SDavid van Moolenbroek return "ipc_send";
331521fa314SDavid van Moolenbroek
332521fa314SDavid van Moolenbroek case SENDNB:
333521fa314SDavid van Moolenbroek return "ipc_sendnb";
334521fa314SDavid van Moolenbroek
335521fa314SDavid van Moolenbroek case RECEIVE:
336521fa314SDavid van Moolenbroek return "ipc_receive";
337521fa314SDavid van Moolenbroek
338521fa314SDavid van Moolenbroek case NOTIFY:
339521fa314SDavid van Moolenbroek return "ipc_notify";
340521fa314SDavid van Moolenbroek
341521fa314SDavid van Moolenbroek case SENDA:
342521fa314SDavid van Moolenbroek return "ipc_senda";
343521fa314SDavid van Moolenbroek
344521fa314SDavid van Moolenbroek case MINIX_KERNINFO:
345521fa314SDavid van Moolenbroek return "minix_kerninfo";
346521fa314SDavid van Moolenbroek
347521fa314SDavid van Moolenbroek default:
348521fa314SDavid van Moolenbroek /*
349521fa314SDavid van Moolenbroek * It would be nice to include the call number here, but we
350521fa314SDavid van Moolenbroek * must return a string that will last until the entire call is
351521fa314SDavid van Moolenbroek * finished. Adding another buffer to the trace_proc structure
352521fa314SDavid van Moolenbroek * is an option, but it seems overkill..
353521fa314SDavid van Moolenbroek */
354521fa314SDavid van Moolenbroek return "ipc_unknown";
355521fa314SDavid van Moolenbroek }
356521fa314SDavid van Moolenbroek }
357521fa314SDavid van Moolenbroek
358521fa314SDavid van Moolenbroek /*
359521fa314SDavid van Moolenbroek * Print the outgoing (request) part of a system call. Return the resulting
360521fa314SDavid van Moolenbroek * call flags.
361521fa314SDavid van Moolenbroek */
362521fa314SDavid van Moolenbroek static unsigned int
call_out(struct trace_proc * proc,reg_t reg[3])363521fa314SDavid van Moolenbroek call_out(struct trace_proc * proc, reg_t reg[3])
364521fa314SDavid van Moolenbroek {
365521fa314SDavid van Moolenbroek
366521fa314SDavid van Moolenbroek switch (proc->call_type) {
367521fa314SDavid van Moolenbroek case SENDREC:
368521fa314SDavid van Moolenbroek proc->m_addr = (vir_bytes)reg[2];
369521fa314SDavid van Moolenbroek
370521fa314SDavid van Moolenbroek return sendrec_out(proc, (endpoint_t)reg[1],
371521fa314SDavid van Moolenbroek (vir_bytes)reg[2]);
372521fa314SDavid van Moolenbroek
373521fa314SDavid van Moolenbroek case SEND:
374521fa314SDavid van Moolenbroek case SENDNB:
375521fa314SDavid van Moolenbroek put_endpoint(proc, "dest", (endpoint_t)reg[1]);
376521fa314SDavid van Moolenbroek put_message(proc, "m_ptr", 0, (vir_bytes)reg[2]);
377521fa314SDavid van Moolenbroek
378521fa314SDavid van Moolenbroek return CT_DONE;
379521fa314SDavid van Moolenbroek
380521fa314SDavid van Moolenbroek case RECEIVE:
381521fa314SDavid van Moolenbroek proc->m_addr = (vir_bytes)reg[2];
382521fa314SDavid van Moolenbroek
383521fa314SDavid van Moolenbroek put_endpoint(proc, "src", (endpoint_t)reg[1]);
384521fa314SDavid van Moolenbroek
385521fa314SDavid van Moolenbroek return CT_NOTDONE;
386521fa314SDavid van Moolenbroek
387521fa314SDavid van Moolenbroek case NOTIFY:
388521fa314SDavid van Moolenbroek put_endpoint(proc, "dest", (endpoint_t)reg[1]);
389521fa314SDavid van Moolenbroek
390521fa314SDavid van Moolenbroek return CT_DONE;
391521fa314SDavid van Moolenbroek
392521fa314SDavid van Moolenbroek case SENDA:
393521fa314SDavid van Moolenbroek put_ptr(proc, "table", (vir_bytes)reg[2]);
394521fa314SDavid van Moolenbroek put_value(proc, "count", "%zu", (size_t)reg[1]);
395521fa314SDavid van Moolenbroek
396521fa314SDavid van Moolenbroek return CT_DONE;
397521fa314SDavid van Moolenbroek
398521fa314SDavid van Moolenbroek case MINIX_KERNINFO:
399521fa314SDavid van Moolenbroek default:
400521fa314SDavid van Moolenbroek return CT_DONE;
401521fa314SDavid van Moolenbroek }
402521fa314SDavid van Moolenbroek }
403521fa314SDavid van Moolenbroek
404521fa314SDavid van Moolenbroek /*
405521fa314SDavid van Moolenbroek * Print the incoming (reply) part of a call.
406521fa314SDavid van Moolenbroek */
407521fa314SDavid van Moolenbroek static void
call_in(struct trace_proc * proc,int failed)408521fa314SDavid van Moolenbroek call_in(struct trace_proc * proc, int failed)
409521fa314SDavid van Moolenbroek {
410521fa314SDavid van Moolenbroek
411521fa314SDavid van Moolenbroek switch (proc->call_type) {
412521fa314SDavid van Moolenbroek case SENDREC:
413521fa314SDavid van Moolenbroek sendrec_in(proc, failed);
414521fa314SDavid van Moolenbroek
415521fa314SDavid van Moolenbroek break;
416521fa314SDavid van Moolenbroek
417521fa314SDavid van Moolenbroek case RECEIVE:
418521fa314SDavid van Moolenbroek /* Print the source as well. */
419521fa314SDavid van Moolenbroek put_message(proc, "m_ptr", failed | PF_ALT, proc->m_addr);
420521fa314SDavid van Moolenbroek put_equals(proc);
421521fa314SDavid van Moolenbroek put_result(proc);
422521fa314SDavid van Moolenbroek
423521fa314SDavid van Moolenbroek break;
424521fa314SDavid van Moolenbroek
425521fa314SDavid van Moolenbroek case MINIX_KERNINFO:
426521fa314SDavid van Moolenbroek /*
427521fa314SDavid van Moolenbroek * We do not have a platform-independent means to access the
428521fa314SDavid van Moolenbroek * secondary IPC return value, so we cannot print the receive
429521fa314SDavid van Moolenbroek * status or minix_kerninfo address.
430521fa314SDavid van Moolenbroek */
431521fa314SDavid van Moolenbroek /* FALLTHROUGH */
432521fa314SDavid van Moolenbroek default:
433521fa314SDavid van Moolenbroek put_result(proc);
434521fa314SDavid van Moolenbroek
435521fa314SDavid van Moolenbroek break;
436521fa314SDavid van Moolenbroek }
437521fa314SDavid van Moolenbroek }
438521fa314SDavid van Moolenbroek
439521fa314SDavid van Moolenbroek /*
440521fa314SDavid van Moolenbroek * Determine whether to skip printing the given call, based on its name.
441521fa314SDavid van Moolenbroek */
442521fa314SDavid van Moolenbroek static int
call_hide(const char * __unused name)443521fa314SDavid van Moolenbroek call_hide(const char * __unused name)
444521fa314SDavid van Moolenbroek {
445521fa314SDavid van Moolenbroek
446521fa314SDavid van Moolenbroek /*
447521fa314SDavid van Moolenbroek * TODO: add support for such filtering, with an strace-like -e command
448521fa314SDavid van Moolenbroek * line option. For now, we filter nothing, although calls may still
449521fa314SDavid van Moolenbroek * be hidden as the result of a register retrieval error.
450521fa314SDavid van Moolenbroek */
451521fa314SDavid van Moolenbroek return FALSE;
452521fa314SDavid van Moolenbroek }
453521fa314SDavid van Moolenbroek
454521fa314SDavid van Moolenbroek /*
455521fa314SDavid van Moolenbroek * The given process entered a system call. Return the trace class of the
456521fa314SDavid van Moolenbroek * call: TC_EXEC for an execve() call, TC_SIGRET for a sigreturn() call, or
457521fa314SDavid van Moolenbroek * TC_NORMAL for a call that requires no exceptions in the trace engine.
458521fa314SDavid van Moolenbroek */
459521fa314SDavid van Moolenbroek int
call_enter(struct trace_proc * proc,int show_stack)460521fa314SDavid van Moolenbroek call_enter(struct trace_proc * proc, int show_stack)
461521fa314SDavid van Moolenbroek {
462521fa314SDavid van Moolenbroek const char *name;
463521fa314SDavid van Moolenbroek reg_t reg[3];
464521fa314SDavid van Moolenbroek int trace_class, type;
465521fa314SDavid van Moolenbroek
466521fa314SDavid van Moolenbroek /* Get the IPC-level type and parameters of the system call. */
467521fa314SDavid van Moolenbroek if (kernel_get_syscall(proc->pid, reg) < 0) {
468521fa314SDavid van Moolenbroek /*
469521fa314SDavid van Moolenbroek * If obtaining the details of the system call failed, even
470521fa314SDavid van Moolenbroek * though we know the process is stopped on a system call, we
471521fa314SDavid van Moolenbroek * are going to assume that the process got killed somehow.
472521fa314SDavid van Moolenbroek * Thus, the best we can do is ignore the system call entirely,
473521fa314SDavid van Moolenbroek * and hope that the next thing we hear about this process is
474521fa314SDavid van Moolenbroek * its termination. At worst, we ignore a serious error..
475521fa314SDavid van Moolenbroek */
476521fa314SDavid van Moolenbroek proc->call_flags = CF_HIDE;
477521fa314SDavid van Moolenbroek
478521fa314SDavid van Moolenbroek return FALSE;
479521fa314SDavid van Moolenbroek }
480521fa314SDavid van Moolenbroek
481521fa314SDavid van Moolenbroek /*
482521fa314SDavid van Moolenbroek * Obtain the call name that is to be used for this call, and decide
483521fa314SDavid van Moolenbroek * whether we want to print this call at all.
484521fa314SDavid van Moolenbroek */
485521fa314SDavid van Moolenbroek proc->call_type = (int)reg[0];
486521fa314SDavid van Moolenbroek trace_class = TC_NORMAL;
487521fa314SDavid van Moolenbroek
488521fa314SDavid van Moolenbroek name = call_prepare(proc, reg, &trace_class);
489521fa314SDavid van Moolenbroek
490521fa314SDavid van Moolenbroek proc->call_name = name;
491521fa314SDavid van Moolenbroek
492521fa314SDavid van Moolenbroek if (call_hide(name)) {
493521fa314SDavid van Moolenbroek proc->call_flags = CF_HIDE;
494521fa314SDavid van Moolenbroek
495521fa314SDavid van Moolenbroek return trace_class;
496521fa314SDavid van Moolenbroek }
497521fa314SDavid van Moolenbroek
498521fa314SDavid van Moolenbroek /* Only print a stack trace if we are printing the call itself. */
499521fa314SDavid van Moolenbroek if (show_stack)
500521fa314SDavid van Moolenbroek kernel_put_stacktrace(proc);
501521fa314SDavid van Moolenbroek
502521fa314SDavid van Moolenbroek /*
503521fa314SDavid van Moolenbroek * Start a new line, start recording, and print the call name and
504521fa314SDavid van Moolenbroek * opening parenthesis.
505521fa314SDavid van Moolenbroek */
506521fa314SDavid van Moolenbroek put_newline();
507521fa314SDavid van Moolenbroek
508521fa314SDavid van Moolenbroek format_reset(proc);
509521fa314SDavid van Moolenbroek
510521fa314SDavid van Moolenbroek record_start(proc);
511521fa314SDavid van Moolenbroek
512521fa314SDavid van Moolenbroek put_text(proc, name);
513521fa314SDavid van Moolenbroek put_open(proc, NULL, PF_NONAME, "(", ", ");
514521fa314SDavid van Moolenbroek
515521fa314SDavid van Moolenbroek /*
516521fa314SDavid van Moolenbroek * Print the outgoing part of the call, that is, some or all of its
517521fa314SDavid van Moolenbroek * parameters. This call returns flags indicating how far printing
518521fa314SDavid van Moolenbroek * got, and may be one of the following combinations:
519521fa314SDavid van Moolenbroek * - CT_NOTDONE (0) if printing parameters is not yet complete; after
520521fa314SDavid van Moolenbroek * the call split, the in handler must print the rest itself;
521521fa314SDavid van Moolenbroek * - CT_DONE (CF_DONE) if printing parameters is complete, and we
522521fa314SDavid van Moolenbroek * should now print the closing parenthesis and equals sign;
523521fa314SDavid van Moolenbroek * - CT_NORETURN (CF_DONE|CF_NORETURN) if printing parameters is
524521fa314SDavid van Moolenbroek * complete, but we should not print the equals sign, because the
525521fa314SDavid van Moolenbroek * call is expected not to return (the no-return call type).
526521fa314SDavid van Moolenbroek */
527521fa314SDavid van Moolenbroek type = call_out(proc, reg);
528521fa314SDavid van Moolenbroek assert(type == CT_NOTDONE || type == CT_DONE || type == CT_NORETURN);
529521fa314SDavid van Moolenbroek
530521fa314SDavid van Moolenbroek /*
531521fa314SDavid van Moolenbroek * Print whatever the handler told us to print for now.
532521fa314SDavid van Moolenbroek */
533521fa314SDavid van Moolenbroek if (type & CF_DONE) {
534521fa314SDavid van Moolenbroek if (type & CF_NORETURN) {
535521fa314SDavid van Moolenbroek put_close(proc, ")");
536521fa314SDavid van Moolenbroek
537521fa314SDavid van Moolenbroek put_space(proc);
538521fa314SDavid van Moolenbroek
539521fa314SDavid van Moolenbroek proc->call_flags |= type;
540521fa314SDavid van Moolenbroek } else {
541521fa314SDavid van Moolenbroek /*
542521fa314SDavid van Moolenbroek * The equals sign is printed implicitly for the
543521fa314SDavid van Moolenbroek * CT_DONE type only. For CT_NORETURN and CT_NOTDONE,
544521fa314SDavid van Moolenbroek * the "in" handler has to do it explicitly.
545521fa314SDavid van Moolenbroek */
546521fa314SDavid van Moolenbroek put_equals(proc);
547521fa314SDavid van Moolenbroek }
548521fa314SDavid van Moolenbroek } else {
549521fa314SDavid van Moolenbroek /*
550521fa314SDavid van Moolenbroek * If at least one parameter was printed, print the separator
551521fa314SDavid van Moolenbroek * now. We know that another parameter will follow (otherwise
552521fa314SDavid van Moolenbroek * the caller would have returned CT_DONE), and this way the
553521fa314SDavid van Moolenbroek * output looks better.
554521fa314SDavid van Moolenbroek */
555521fa314SDavid van Moolenbroek format_push_sep(proc);
556521fa314SDavid van Moolenbroek }
557521fa314SDavid van Moolenbroek
558521fa314SDavid van Moolenbroek /*
559521fa314SDavid van Moolenbroek * We are now at the call split; further printing will be done once the
560521fa314SDavid van Moolenbroek * call returns, through call_leave. Stop recording; if the call gets
561521fa314SDavid van Moolenbroek * suspended and later resumed, we should replay everything up to here.
562521fa314SDavid van Moolenbroek */
563521fa314SDavid van Moolenbroek #if DEBUG
564521fa314SDavid van Moolenbroek put_text(proc, "|"); /* warning, this may push a space */
565521fa314SDavid van Moolenbroek #endif
566521fa314SDavid van Moolenbroek
567521fa314SDavid van Moolenbroek record_stop(proc);
568521fa314SDavid van Moolenbroek
569521fa314SDavid van Moolenbroek output_flush();
570521fa314SDavid van Moolenbroek
571521fa314SDavid van Moolenbroek return trace_class;
572521fa314SDavid van Moolenbroek }
573521fa314SDavid van Moolenbroek
574521fa314SDavid van Moolenbroek /*
575521fa314SDavid van Moolenbroek * The given process left a system call, or if skip is set, the leave phase of
576521fa314SDavid van Moolenbroek * the current system call should be ended.
577521fa314SDavid van Moolenbroek */
578521fa314SDavid van Moolenbroek void
call_leave(struct trace_proc * proc,int skip)579521fa314SDavid van Moolenbroek call_leave(struct trace_proc * proc, int skip)
580521fa314SDavid van Moolenbroek {
581521fa314SDavid van Moolenbroek reg_t retreg;
582521fa314SDavid van Moolenbroek int hide, failed;
583521fa314SDavid van Moolenbroek
584521fa314SDavid van Moolenbroek /* If the call is skipped, it must be a no-return type call. */
585521fa314SDavid van Moolenbroek assert(!skip || (proc->call_flags & (CF_NORETURN | CF_HIDE)));
586521fa314SDavid van Moolenbroek
587521fa314SDavid van Moolenbroek /*
588521fa314SDavid van Moolenbroek * Start by replaying the current call, if necessary. If the call was
589521fa314SDavid van Moolenbroek * suspended and we are about to print the "in" part, this is obviously
590521fa314SDavid van Moolenbroek * needed. If the call is hidden, replaying will be a no-op, since
591521fa314SDavid van Moolenbroek * nothing was recorded for this call. The special case is a skipped
592521fa314SDavid van Moolenbroek * call (which, as established above, must be a no-return call, e.g.
593521fa314SDavid van Moolenbroek * exec), for which replaying has the effect that if the call was
594521fa314SDavid van Moolenbroek * previously suspended, it will now be replayed, without suspension:
595521fa314SDavid van Moolenbroek *
596521fa314SDavid van Moolenbroek * 2| execve("./test", ["./test"], [..(12)]) <..>
597521fa314SDavid van Moolenbroek * 3| sigsuspend([]) = <..>
598521fa314SDavid van Moolenbroek * [A] 2| execve("./test", ["./test"], [..(12)])
599521fa314SDavid van Moolenbroek * 2| ---
600521fa314SDavid van Moolenbroek * 2| Tracing test (pid 2)
601521fa314SDavid van Moolenbroek *
602521fa314SDavid van Moolenbroek * The [A] line is the result of replaying the skipped call.
603521fa314SDavid van Moolenbroek */
604521fa314SDavid van Moolenbroek call_replay(proc);
605521fa314SDavid van Moolenbroek
606521fa314SDavid van Moolenbroek hide = (proc->call_flags & CF_HIDE);
607521fa314SDavid van Moolenbroek
608521fa314SDavid van Moolenbroek if (!hide && !skip) {
609521fa314SDavid van Moolenbroek /* Get the IPC-level result of the call. */
610521fa314SDavid van Moolenbroek if (kernel_get_retreg(proc->pid, &retreg) < 0) {
611521fa314SDavid van Moolenbroek /* This should never happen. Deal with it anyway. */
612521fa314SDavid van Moolenbroek proc->call_flags |= CF_REG_ERR;
613521fa314SDavid van Moolenbroek failed = PF_FAILED;
614521fa314SDavid van Moolenbroek } else if ((proc->call_result = (int)retreg) < 0) {
615521fa314SDavid van Moolenbroek proc->call_flags |= CF_IPC_ERR;
616521fa314SDavid van Moolenbroek failed = PF_FAILED;
617521fa314SDavid van Moolenbroek } else
618521fa314SDavid van Moolenbroek failed = 0;
619521fa314SDavid van Moolenbroek
620521fa314SDavid van Moolenbroek /*
621521fa314SDavid van Moolenbroek * Print the incoming part of the call, that is, possibly some
622521fa314SDavid van Moolenbroek * or all of its parameters and the call's closing parenthesis
623521fa314SDavid van Moolenbroek * (if CT_NOTDONE), and the equals sign (if not CT_DONE), then
624521fa314SDavid van Moolenbroek * the call result.
625521fa314SDavid van Moolenbroek */
626521fa314SDavid van Moolenbroek call_in(proc, failed);
627521fa314SDavid van Moolenbroek }
628521fa314SDavid van Moolenbroek
629521fa314SDavid van Moolenbroek if (!hide) {
630521fa314SDavid van Moolenbroek /*
631521fa314SDavid van Moolenbroek * The call is complete now, so clear the recording. This also
632521fa314SDavid van Moolenbroek * implies that no suspension marker will be printed anymore.
633521fa314SDavid van Moolenbroek */
634521fa314SDavid van Moolenbroek record_clear(proc);
635521fa314SDavid van Moolenbroek
636521fa314SDavid van Moolenbroek put_newline();
637521fa314SDavid van Moolenbroek }
638521fa314SDavid van Moolenbroek
639521fa314SDavid van Moolenbroek /*
640521fa314SDavid van Moolenbroek * For calls not of the no-return type, an equals sign must have been
641521fa314SDavid van Moolenbroek * printed by now. This is protection against badly written handlers.
642521fa314SDavid van Moolenbroek */
643521fa314SDavid van Moolenbroek assert(proc->call_flags & CF_DONE);
644521fa314SDavid van Moolenbroek
645521fa314SDavid van Moolenbroek proc->call_name = NULL;
646521fa314SDavid van Moolenbroek proc->call_flags = 0;
647521fa314SDavid van Moolenbroek }
648521fa314SDavid van Moolenbroek
649521fa314SDavid van Moolenbroek /*
650521fa314SDavid van Moolenbroek * Replay the recorded text, if any, for the enter phase of the given process.
651521fa314SDavid van Moolenbroek * If there is no recorded text, start a new line anyway.
652521fa314SDavid van Moolenbroek */
653521fa314SDavid van Moolenbroek void
call_replay(struct trace_proc * proc)654521fa314SDavid van Moolenbroek call_replay(struct trace_proc * proc)
655521fa314SDavid van Moolenbroek {
656521fa314SDavid van Moolenbroek
657521fa314SDavid van Moolenbroek /*
658521fa314SDavid van Moolenbroek * We get TRUE if the recorded call should be replayed, but the
659521fa314SDavid van Moolenbroek * recorded text for the call did not fit in the recording buffer.
660521fa314SDavid van Moolenbroek * In that case, we have to come up with a replacement text for the
661521fa314SDavid van Moolenbroek * call up to the call split.
662521fa314SDavid van Moolenbroek */
663521fa314SDavid van Moolenbroek if (record_replay(proc) == TRUE) {
664521fa314SDavid van Moolenbroek /*
665521fa314SDavid van Moolenbroek * We basically place a "<..>" suspension marker in the
666521fa314SDavid van Moolenbroek * parameters part of the call, and use its call name and flags
667521fa314SDavid van Moolenbroek * for the rest. There is a trailing space in all cases.
668521fa314SDavid van Moolenbroek */
669521fa314SDavid van Moolenbroek put_fmt(proc, "%s(<..>%s", proc->call_name,
670521fa314SDavid van Moolenbroek !(proc->call_flags & CF_DONE) ? "," :
671521fa314SDavid van Moolenbroek ((proc->call_flags & CF_NORETURN) ? ")" : ") ="));
672521fa314SDavid van Moolenbroek put_space(proc);
673521fa314SDavid van Moolenbroek }
674521fa314SDavid van Moolenbroek }
675521fa314SDavid van Moolenbroek
676521fa314SDavid van Moolenbroek /*
677521fa314SDavid van Moolenbroek * Return the human-readable name of the call currently being made by the given
678521fa314SDavid van Moolenbroek * process. The process is guaranteed to be in a call, although the call may
679521fa314SDavid van Moolenbroek * be hidden. Under no circumstances may this function return a NULL pointer.
680521fa314SDavid van Moolenbroek */
681521fa314SDavid van Moolenbroek const char *
call_name(struct trace_proc * proc)682521fa314SDavid van Moolenbroek call_name(struct trace_proc * proc)
683521fa314SDavid van Moolenbroek {
684521fa314SDavid van Moolenbroek
685521fa314SDavid van Moolenbroek assert(proc->call_name != NULL);
686521fa314SDavid van Moolenbroek
687521fa314SDavid van Moolenbroek return proc->call_name;
688521fa314SDavid van Moolenbroek }
689e4e21ee1SDavid van Moolenbroek
690e4e21ee1SDavid van Moolenbroek /*
691e4e21ee1SDavid van Moolenbroek * Return whether the current call failed due to an error at the system call
692e4e21ee1SDavid van Moolenbroek * level, and if so, return the error code as well. May be called during the
693e4e21ee1SDavid van Moolenbroek * leave phase of a call only.
694e4e21ee1SDavid van Moolenbroek */
695e4e21ee1SDavid van Moolenbroek int
call_errno(struct trace_proc * proc,int * err)696e4e21ee1SDavid van Moolenbroek call_errno(struct trace_proc * proc, int * err)
697e4e21ee1SDavid van Moolenbroek {
698e4e21ee1SDavid van Moolenbroek
699e4e21ee1SDavid van Moolenbroek if (proc->call_flags & (CF_REG_ERR | CF_MSG_ERR | CF_IPC_ERR))
700e4e21ee1SDavid van Moolenbroek return FALSE;
701e4e21ee1SDavid van Moolenbroek
702e4e21ee1SDavid van Moolenbroek if (proc->call_result >= 0)
703e4e21ee1SDavid van Moolenbroek return FALSE;
704e4e21ee1SDavid van Moolenbroek
705e4e21ee1SDavid van Moolenbroek *err = -proc->call_result;
706e4e21ee1SDavid van Moolenbroek return TRUE;
707e4e21ee1SDavid van Moolenbroek }
708