/*
* Print functions for system call tracing.
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"

#include "/sys/src/libc/9syscall/sys.h"

// WE ARE OVERRUNNING SOMEHOW
static void
fmtrwdata(Fmt* f, char* a, int n, char* suffix)
{
       int i;
       char *t;

       if(a == nil){
               fmtprint(f, "0x0%s", suffix);
               return;
       }
       validaddr((uintptr)a, n, 0);
       t = smalloc(n+1);
       t[n] = 0;
       for(i = 0; i < n; i++)
               if(a[i] > 0x20 && a[i] < 0x7f)  /* printable ascii? */
                       t[i] = a[i];
               else
                       t[i] = '.';

       fmtprint(f, " %#p/\"%s\"%s", a, t, suffix);
       free(t);
}

static void
fmtuserstring(Fmt* f, char* a, char* suffix)
{
       char *t, *e;
       int n;

       if(a == nil){
               fmtprint(f, "0/\"\"%s", suffix);
               return;
       }
       validaddr((uintptr)a, 1, 0);
       n = 1<<16;
       e = vmemchr(a, 0, n);
       if(e != nil)
               n = e - a;
       t = smalloc(n+1);
       memmove(t, a, n);
       t[n] = 0;
       fmtprint(f, "%#p/\"%s\"%s", a, t, suffix);
       free(t);
}

void
syscallfmt(ulong syscallno, uintptr pc, va_list list)
{
       long l;
       Fmt fmt;
       void *v;
       vlong vl;
       uintptr p;
       int i[2], len;
       char *a, **argv;

       fmtstrinit(&fmt);
       fmtprint(&fmt, "%uld %s ", up->pid, up->text);

       if(syscallno >= nsyscall)
               fmtprint(&fmt, " %uld ", syscallno);
       else
               fmtprint(&fmt, "%s ", sysctab[syscallno]?
                       sysctab[syscallno]: "huh?");

       fmtprint(&fmt, "%p ", pc);
       switch(syscallno){
       case SYSR1:
               p = va_arg(list, uintptr);
               fmtprint(&fmt, "%#p", p);
               break;
       case _ERRSTR:                                   /* deprecated */
       case CHDIR:
       case EXITS:
       case REMOVE:
               a = va_arg(list, char*);
               fmtuserstring(&fmt, a, "");
               break;
       case BIND:
               a = va_arg(list, char*);
               fmtuserstring(&fmt, a, " ");
               a = va_arg(list, char*);
               fmtuserstring(&fmt, a, " ");
               i[0] = va_arg(list, int);
               fmtprint(&fmt, "%#ux",  i[0]);
               break;
       case CLOSE:
       case NOTED:
               i[0] = va_arg(list, int);
               fmtprint(&fmt, "%d", i[0]);
               break;
       case DUP:
               i[0] = va_arg(list, int);
               i[1] = va_arg(list, int);
               fmtprint(&fmt, "%d %d", i[0], i[1]);
               break;
       case ALARM:
               l = va_arg(list, unsigned long);
               fmtprint(&fmt, "%#lud ", l);
               break;
       case EXEC:
               a = va_arg(list, char*);
               fmtuserstring(&fmt, a, "");
               argv = va_arg(list, char**);
               evenaddr((uintptr)argv);
               for(;;){
                       validaddr((uintptr)argv, sizeof(char**), 0);
                       a = *(char **)argv;
                       if(a == nil)
                               break;
                       fmtprint(&fmt, " ");
                       fmtuserstring(&fmt, a, "");
                       argv++;
               }
               break;
       case _FSESSION:                                 /* deprecated */
       case _FSTAT:                                    /* deprecated */
       case _FWSTAT:                                   /* obsolete */
               i[0] = va_arg(list, int);
               a = va_arg(list, char*);
               fmtprint(&fmt, "%d %#p", i[0], a);
               break;
       case FAUTH:
               i[0] = va_arg(list, int);
               a = va_arg(list, char*);
               fmtprint(&fmt, "%d", i[0]);
               fmtuserstring(&fmt, a, "");
               break;
       case SEGBRK:
       case RENDEZVOUS:
               v = va_arg(list, void*);
               fmtprint(&fmt, "%#p ", v);
               v = va_arg(list, void*);
               fmtprint(&fmt, "%#p", v);
               break;
       case _MOUNT:                                    /* deprecated */
               i[0] = va_arg(list, int);
               fmtprint(&fmt, "%d ", i[0]);
               a = va_arg(list, char*);
               fmtuserstring(&fmt, a, " ");
               i[0] = va_arg(list, int);
               fmtprint(&fmt, "%#ux ", i[0]);
               a = va_arg(list, char*);
               fmtuserstring(&fmt, a, "");
               break;
       case OPEN:
               a = va_arg(list, char*);
               fmtuserstring(&fmt, a, " ");
               i[0] = va_arg(list, int);
               fmtprint(&fmt, "%#ux", i[0]);
               break;
       case OSEEK:                                     /* deprecated */
               i[0] = va_arg(list, int);
               l = va_arg(list, long);
               i[1] = va_arg(list, int);
               fmtprint(&fmt, "%d %ld %d", i[0], l, i[1]);
               break;
       case SLEEP:
               l = va_arg(list, long);
               fmtprint(&fmt, "%ld", l);
               break;
       case _STAT:                                     /* obsolete */
       case _WSTAT:                                    /* obsolete */
               a = va_arg(list, char*);
               fmtuserstring(&fmt, a, " ");
               a = va_arg(list, char*);
               fmtprint(&fmt, "%#p", a);
               break;
       case RFORK:
               i[0] = va_arg(list, int);
               fmtprint(&fmt, "%#ux", i[0]);
               break;
       case PIPE:
       case BRK_:
               v = va_arg(list, int*);
               fmtprint(&fmt, "%#p", v);
               break;
       case CREATE:
               a = va_arg(list, char*);
               fmtuserstring(&fmt, a, " ");
               i[0] = va_arg(list, int);
               i[1] = va_arg(list, int);
               fmtprint(&fmt, "%#ux %#ux", i[0], i[1]);
               break;
       case FD2PATH:
       case FSTAT:
       case FWSTAT:
               i[0] = va_arg(list, int);
               a = va_arg(list, char*);
               l = va_arg(list, unsigned long);
               fmtprint(&fmt, "%d %#p %lud", i[0], a, l);
               break;
       case NOTIFY:
       case SEGDETACH:
       case _WAIT:                                     /* deprecated */
               v = va_arg(list, void*);
               fmtprint(&fmt, "%#p", v);
               break;
       case SEGATTACH:
               i[0] = va_arg(list, int);
               fmtprint(&fmt, "%d ", i[0]);
               a = va_arg(list, char*);
               fmtuserstring(&fmt, a, " ");
               /*FALLTHROUGH*/
       case SEGFREE:
       case SEGFLUSH:
               v = va_arg(list, void*);
               l = va_arg(list, unsigned long);
               fmtprint(&fmt, "%#p %lud", v, l);
               break;
       case UNMOUNT:
               a = va_arg(list, char*);
               fmtuserstring(&fmt, a, " ");
               a = va_arg(list, char*);
               fmtuserstring(&fmt, a, "");
               break;
       case SEMACQUIRE:
       case SEMRELEASE:
               v = va_arg(list, int*);
               i[0] = va_arg(list, int);
               fmtprint(&fmt, "%#p %d", v, i[0]);
               break;
       case TSEMACQUIRE:
               v = va_arg(list, int*);
               l = va_arg(list, ulong);
               fmtprint(&fmt, "%#p %ld", v, l);
               break;
       case SEEK:
               v = va_arg(list, vlong*);
               i[0] = va_arg(list, int);
               vl = va_arg(list, vlong);
               i[1] = va_arg(list, int);
               fmtprint(&fmt, "%#p %d %#llux %d", v, i[0], vl, i[1]);
               break;
       case FVERSION:
               i[0] = va_arg(list, int);
               i[1] = va_arg(list, int);
               fmtprint(&fmt, "%d %d ", i[0], i[1]);
               a = va_arg(list, char*);
               fmtuserstring(&fmt, a, " ");
               l = va_arg(list, unsigned long);
               fmtprint(&fmt, "%lud", l);
               break;
       case WSTAT:
       case STAT:
               a = va_arg(list, char*);
               fmtuserstring(&fmt, a, " ");
               /*FALLTHROUGH*/
       case ERRSTR:
       case AWAIT:
               a = va_arg(list, char*);
               l = va_arg(list, unsigned long);
               fmtprint(&fmt, "%#p %lud", a, l);
               break;
       case MOUNT:
               i[0] = va_arg(list, int);
               i[1] = va_arg(list, int);
               fmtprint(&fmt, "%d %d ", i[0], i[1]);
               a = va_arg(list, char*);
               fmtuserstring(&fmt, a, " ");
               i[0] = va_arg(list, int);
               fmtprint(&fmt, "%#ux ", i[0]);
               a = va_arg(list, char*);
               fmtuserstring(&fmt, a, "");
               break;
       case _READ:                                     /* deprecated */
       case PREAD:
               i[0] = va_arg(list, int);
               v = va_arg(list, void*);
               l = va_arg(list, long);
               fmtprint(&fmt, "%d %#p %ld", i[0], v, l);
               if(syscallno == PREAD){
                       vl = va_arg(list, vlong);
                       fmtprint(&fmt, " %lld", vl);
               }
               break;
       case _WRITE:                                    /* deprecated */
       case PWRITE:
               i[0] = va_arg(list, int);
               v = va_arg(list, void*);
               l = va_arg(list, long);
               fmtprint(&fmt, "%d ", i[0]);
               len = MIN(l, 64);
               fmtrwdata(&fmt, v, len, " ");
               fmtprint(&fmt, "%ld", l);
               if(syscallno == PWRITE){
                       vl = va_arg(list, vlong);
                       fmtprint(&fmt, " %lld", vl);
               }
               break;
       case _NSEC:
               if(sizeof(uintptr) == sizeof(vlong))
                       break;
               v = va_arg(list, vlong*);
               fmtprint(&fmt, "%#p", v);
               break;
       }

       a = fmtstrflush(&fmt);
       qlock(&up->debug);
       free(up->syscalltrace);
       up->syscalltrace = a;
       qunlock(&up->debug);
}

void
sysretfmt(ulong syscallno, va_list list, uintptr ret, uvlong start, uvlong stop)
{
       long l;
       void* v;
       Fmt fmt;
       vlong vl;
       int i, len;
       char *a, *errstr;

       fmtstrinit(&fmt);

       errstr = "\"\"";
       switch(syscallno){
       case EXEC:
       case SEGBRK:
       case SEGATTACH:
       case RENDEZVOUS:
               if((void *)ret == (void*)-1)
                       errstr = up->syserrstr;
               fmtprint(&fmt, " = %#p", (void *)ret);
               break;
       case AWAIT:
               a = va_arg(list, char*);
               l = va_arg(list, unsigned long);
               if(ret > 0){
                       fmtuserstring(&fmt, a, " ");
                       fmtprint(&fmt, "%lud = %ld", l, (long)ret);
               }
               else{
                       fmtprint(&fmt, "%#p/\"\" %lud = %ld", a, l, (long)ret);
                       errstr = up->syserrstr;
               }
               break;
       case _ERRSTR:
       case ERRSTR:
               a = va_arg(list, char*);
               if(syscallno == _ERRSTR)
                       l = 64;
               else
                       l = va_arg(list, unsigned long);
               if(ret > 0){
                       fmtuserstring(&fmt, a, " ");
                       fmtprint(&fmt, "%lud = %ld", l, (long)ret);
               }
               else{
                       fmtprint(&fmt, "\"\" %lud = %ld", l, (long)ret);
                       errstr = up->syserrstr;
               }
               break;
       case FD2PATH:
               i = va_arg(list, int);
               USED(i);
               a = va_arg(list, char*);
               l = va_arg(list, unsigned long);
               if(ret > 0){
                       fmtuserstring(&fmt, a, " ");
                       fmtprint(&fmt, "%lud = %ld", l, (long)ret);
               }
               else{
                       fmtprint(&fmt, "\"\" %lud = %ld", l, (long)ret);
                       errstr = up->syserrstr;
               }
               break;
       case _READ:
       case PREAD:
               i = va_arg(list, int);
               USED(i);
               v = va_arg(list, void*);
               l = va_arg(list, long);
               if(ret > 0){
                       len = MIN(ret, 64);
                       fmtrwdata(&fmt, v, len, "");
               }
               else{
                       fmtprint(&fmt, "/\"\"");
                       errstr = up->syserrstr;
               }
               fmtprint(&fmt, " %ld", l);
               if(syscallno == PREAD){
                       vl = va_arg(list, vlong);
                       fmtprint(&fmt, " %lld", vl);
               }
               fmtprint(&fmt, " = %ld", (long)ret);
               break;
       case _NSEC:
               if(sizeof(uintptr) == sizeof(vlong)){
                       fmtprint(&fmt, " = %lld", (vlong)ret);
                       break;
               }
               /* wet floor */
       case ALARM:
       case _WRITE:
       case PWRITE:
       default:
               if((long)ret == -1)
                       errstr = up->syserrstr;
               fmtprint(&fmt, " = %ld", (long)ret);
               break;
       }
       fmtprint(&fmt, " %s %#llud %#llud\n", errstr, start, stop);

       a = fmtstrflush(&fmt);
       qlock(&up->debug);
       free(up->syscalltrace);
       up->syscalltrace = a;
       qunlock(&up->debug);
}