#include "c.h"

static char rcsid[] = "$Id: trace.nw,v 2.5 1997/05/02 20:31:12 drh Exp $";

static char *fmt, *fp, *fmtend; /* format string, current & limit pointer */
static Tree args;         /* printf arguments */
static Symbol frameno;    /* local holding frame number */

/* appendstr - append str to the evolving format string, expanding it if necessary */
static void appendstr(char *str) {
  do
    if (fp == fmtend)
      if (fp) {
        char *s = allocate(2*(fmtend - fmt), FUNC);
        strncpy(s, fmt, fmtend - fmt);
        fp = s + (fmtend - fmt);
        fmtend = s + 2*(fmtend - fmt);
        fmt = s;
      } else {
        fp = fmt = allocate(80, FUNC);
        fmtend = fmt + 80;
      }
  while ((*fp++ = *str++) != 0);
  fp--;
}

/* tracevalue - append format and argument to print the value of e */
static void tracevalue(Tree e, int lev) {
  Type ty = unqual(e->type);

  switch (ty->op) {
  case INT:
    if (ty == chartype)
      appendstr("'\\x%02x'");
    else if (ty == longtype)
      appendstr("0x%ld");
    else
      appendstr("0x%d");
    break;
  case UNSIGNED:
    if (ty == unsignedchar)
      appendstr("'\\x%02x'");
    else if (ty == unsignedlong)
      appendstr("0x%lx");
    else
      appendstr("0x%x");
    break;
  case FLOAT:
    if (ty == longdouble)
      appendstr("%Lg");
    else
      appendstr("%g");
    break;
  case POINTER:
    if (unqual(ty->type) == chartype) {
      static Symbol null;
      if (null == NULL)
        null = mkstr("(null)");
      tracevalue(cast(e, unsignedtype), lev + 1);
      appendstr(" \"%.30s\"");
      e = condtree(e, e, pointer(idtree(null->u.c.loc)));
    } else {
      appendstr("("); appendstr(typestring(ty, "")); appendstr(")0x%x");
    }
    break;
  case STRUCT: {
    Field q;
    appendstr("("); appendstr(typestring(ty, "")); appendstr("){");
    for (q = ty->u.sym->u.s.flist; q; q = q->link) {
      appendstr(q->name); appendstr("=");
      tracevalue(field(addrof(e), q->name), lev + 1);
      if (q->link)
        appendstr(",");
    }
    appendstr("}");
    return;
    }
  case UNION:
    appendstr("("); appendstr(typestring(ty, "")); appendstr("){...}");
    return;
  case ARRAY:
    if (lev && ty->type->size > 0) {
      int i;
      e = pointer(e);
      appendstr("{");
      for (i = 0; i < ty->size/ty->type->size; i++) {
        Tree p = (*optree['+'])(ADD, e, consttree(i, inttype));
        if (isptr(p->type) && isarray(p->type->type))
          p = retype(p, p->type->type);
        else
          p = rvalue(p);
        if (i)
          appendstr(",");
        tracevalue(p, lev + 1);
      }
      appendstr("}");
    } else
      appendstr(typestring(ty, ""));
    return;
  default:
    assert(0);
  }
  e = cast(e, promote(ty));
  args = tree(mkop(ARG,e->type), e->type, e, args);
}

/* tracefinis - complete & generate the trace call to print */
static void tracefinis(Symbol printer) {
  Tree *ap;
  Symbol p;

  *fp = 0;
  p = mkstr(string(fmt));
  for (ap = &args; *ap; ap = &(*ap)->kids[1])
    ;
  *ap = tree(ARG+P, ptr(chartype), pointer(idtree(p->u.c.loc)), 0);
  walk(calltree(pointer(idtree(printer)), freturn(printer->type), args, NULL), 0, 0);
  args = 0;
  fp = fmtend = 0;
}

/* tracecall - generate code to trace entry to f */
static void tracecall(Symbol printer, Symbol f) {
  int i;
  Symbol counter = genident(STATIC, inttype, GLOBAL);

  defglobal(counter, BSS);
  (*IR->space)(counter->type->size);
  frameno = genident(AUTO, inttype, level);
  addlocal(frameno);
  appendstr(f->name); appendstr("#");
  tracevalue(asgn(frameno, incr(INCR, idtree(counter), consttree(1, inttype))), 0);
  appendstr("(");
  for (i = 0; f->u.f.callee[i]; i++) {
    if (i)
      appendstr(",");
    appendstr(f->u.f.callee[i]->name); appendstr("=");
    tracevalue(idtree(f->u.f.callee[i]), 0);
  }
  if (variadic(f->type))
    appendstr(",...");
  appendstr(") called\n");
  tracefinis(printer);
}

/* tracereturn - generate code to trace return e */
static void tracereturn(Symbol printer, Symbol f, Tree e) {
  appendstr(f->name); appendstr("#");
  tracevalue(idtree(frameno), 0);
  appendstr(" returned");
  if (freturn(f->type) != voidtype && e) {
    appendstr(" ");
    tracevalue(e, 0);
  }
  appendstr("\n");
  tracefinis(printer);
}

/* trace_init - initialize for tracing */
void trace_init(int argc, char *argv[]) {
  int i;
  static int inited;

  if (inited)
    return;
  inited = 1;
  type_init(argc, argv);
  if (IR)
    for (i = 1; i < argc; i++)
      if (strncmp(argv[i], "-t", 2) == 0 &&  strchr(argv[i], '=') == NULL) {
        Symbol printer = mksymbol(EXTERN,
          argv[i][2] ? &argv[i][2] : "printf",
        ftype(inttype, ptr(qual(CONST, chartype))));
        printer->defined = 0;
        attach((Apply)tracecall,   printer, &events.entry);
        attach((Apply)tracereturn, printer, &events.returns);
        break;
      }
}
