/* $VER: profiler.c V0.1 (28.08.98)
 *
 * Portable profiler for vbcc.
 * Copyright (c) 1998  Frank Wille
 *
 * The vbcc profiler is split into a system independant (profiler)
 * and in a system specific (prof_sysdep) part.
 *
 * v0.1  (28.08.98) phx
 *       File created.
 */

#include "profiler.h"
#include "prof_sysdep.h"

static struct prof **hashtab = NULL;


static void prof_output()
{
  if (hashtab) {
    static char algnbuf[4] = { 0,0,0,0 };
    FILE *fp;
    struct prof *p;
    int i,l;

    if (fp = fopen(PROFFILENAME,"w")) {
      for (i=0; i<HASHTABSIZE; i++) {
        p = hashtab[i];
        while (p) {
          l = strlen(p->name) + 1;
          fwrite(p->name,1,l,fp);
          if (l &= 3)  /* 32-bit align */
            fwrite(algnbuf,1,4-l,fp);
          fwrite(&p->called,1,sizeof(unsigned long),fp);
          fwrite(&p->totaltime,1,sizeof(unsigned long),fp);
          p = p->hashchain;
        }
      }
      fclose(fp);
    }
    _prof_timerexit();
    free(hashtab);
  }
}


void _startprof(char *fname)
{
  __save_volatiles();

  if (!hashtab) {
    /* _startprof is called first time - do initialization first */
    if (!(hashtab = calloc(HASHTABSIZE,sizeof(struct prof *))))
      goto xit;
    if (!_prof_inittimer()) {
      free(hashtab);
      hashtab = NULL;
      goto xit;
    }
    atexit(prof_output);
  }

  {
    /* search old or allocate new prof node */
    unsigned long idx = ((unsigned long)fname>>2) & (HASHTABSIZE-1);
    struct prof *last = NULL, *p = hashtab[idx];

    while (p) {
      if (!strcmp(fname,p->name))
        break;
      last = p;
      p = p->hashchain;
    }
    if (!p) {
      /* allocate new prof node */
      if (!(p = calloc(1,sizeof(struct prof))))
        goto xit;
      if (last)
        last->hashchain = p;
      else
        hashtab[idx] = p;
      memset(p,0,sizeof(struct prof));
      p->name = fname;
    }

    p->called++;
    if (p->recursion_cnt++ == 0)
      p->entrytime = _prof_time();
  }

xit:
  __restore_volatiles();
}


void _endprof(char *fname)
{
  __save_volatiles();

  if (hashtab) {
    struct prof *p = hashtab[((unsigned long)fname>>2) & (HASHTABSIZE-1)];

    while (p) {
      if (!strcmp(fname,p->name))
        break;
      p = p->hashchain;
    }
    if (!p)  /* this should not happen - maybe something trashed memory? */
      fprintf(stderr,"_endprof: profiling data for %s have disappeared!\n",
              fname);
    if (--p->recursion_cnt == 0)
      p->totaltime += _prof_time() - p->entrytime;
  }

  __restore_volatiles();
}
