#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>
#define Extern extern
#include "mips.h"

/*
* Print value v as name[+offset] and then the string s.
*/
void
psymoff(ulong v, int type, char *str)
{
       Symbol s;
       int t;
       int r;
       int delta;

       switch(type) {
       case SEGREGS:
               Bprint(bioout, "%%#%lux", v);
               Bprint(bioout, str);
               return;
       case SEGANY:
               t = CANY;
               break;
       case SEGDATA:
               t = CDATA;
               break;
       case SEGTEXT:
               t = CTEXT;
               break;
       case SEGNONE:
       default:
               return;
       }
       r = 0;
       delta = 0x1000;
       if (v) {
               r = findsym(v, t, &s);
               if (r)
                       delta = v-s.value;
       }
       if (v == 0 || r == 0 || delta >= 0x1000)
               Bprint(bioout, "#%lux", v);
       else {
               Bprint(bioout, "%s", s.name);
               if (delta)
                       Bprint(bioout, "+#%lux", delta);
       }
       Bprint(bioout, str);
}

#define STRINGSZ        128

/*
*      print the value of dot as file:line
*/
void
printsource(long dot)
{
       char str[STRINGSZ];

       if (fileline(str, STRINGSZ, dot))
               Bprint(bioout, "%s", str);
}

void
printlocals(Symbol *fn, ulong fp)
{
       int i;
       Symbol s;

       s = *fn;
       for (i = 0; localsym(&s, i); i++) {
               if (s.class != CAUTO)
                       continue;
               Bprint(bioout, "\t%s=#%lux\n", s.name, getmem_4(fp-s.value));
       }
}

void
printparams(Symbol *fn, ulong fp)
{
       int i;
       Symbol s;
       int first;

       fp += mach->szreg;                      /* skip saved pc */
       s = *fn;
       for (first = i = 0; localsym(&s, i); i++) {
               if (s.class != CPARAM)
                       continue;
               if (first++)
                       Bprint(bioout, ", ");
               Bprint(bioout, "%s=#%lux", s.name, getmem_4(fp+s.value));
       }
       Bprint(bioout, ") ");
}

#define STARTSYM        "_main"
#define FRAMENAME       ".frame"

ulong
findframe(ulong addr)
{
       ulong pc, fp;
       Symbol s, f;

       pc = reg.pc;
       fp = reg.r[29];
       while (findsym(pc, CTEXT, &s)) {
               if (strcmp(STARTSYM, s.name) == 0)
                       break;
               if (findlocal(&s, FRAMENAME, &f) == 0)
                       break;
               fp += f.value;
               if (s.value == addr)
                       return fp;
               if (s.type == 'L' || s.type == 'l')
                       pc = reg.r[31];
               else
                       pc = getmem_4(fp-f.value);
       }
       return 0;
}

void
stktrace(int modif)
{
       ulong pc, sp;
       Symbol s, f;
       int i;

       pc = reg.pc;
       sp = reg.r[29];
       i = 0;
       while (findsym(pc, CTEXT, &s)) {
               if(strcmp(STARTSYM, s.name) == 0) {
                       Bprint(bioout, "%s() at #%lux\n", s.name, s.value);
                       break;
               }
               if (pc == s.value)      /* at first instruction */
                       f.value = 0;
               else if (findlocal(&s, FRAMENAME, &f) == 0)
                       break;
               if (s.type == 'L' || s.type == 'l' || pc <= s.value+4)
                       pc = reg.r[31];
               else pc = getmem_4(sp);
               sp += f.value;
               Bprint(bioout, "%s(", s.name);
               printparams(&s, sp);
               printsource(s.value);
               Bprint(bioout, " called from ");
               psymoff(pc-8, SEGTEXT, " ");
               printsource(pc-8);
               Bprint(bioout, "\n");
               if(modif == 'C')
                       printlocals(&s, sp);
               if(++i > 40){
                       Bprint(bioout, "(trace truncated)\n");
                       break;
               }
       }
}