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 * 22521fa314SDavid van Moolenbroek find_handler(endpoint_t endpt, int call_nr) 23521fa314SDavid van Moolenbroek { 24*b58e161cSDavid 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 31*b58e161cSDavid van Moolenbroek if ((unsigned int)call_nr < call_table[i]->base) 32521fa314SDavid van Moolenbroek continue; 33521fa314SDavid van Moolenbroek 34*b58e161cSDavid 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 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 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 101521fa314SDavid van Moolenbroek put_value(proc, "m_type", "%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 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 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 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 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 * 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 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 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 * 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 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 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 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 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 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 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 * 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 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