/**
 * VampirTrace
 * http://www.tu-dresden.de/zih/vampirtrace
 *
 * Copyright (c) 2005-2013, ZIH, TU Dresden, Federal Republic of Germany
 *
 * Copyright (c) 1998-2005, Forschungszentrum Juelich, Juelich Supercomputing
 *                          Centre, Federal Republic of Germany
 *
 * See the file COPYING in the package base directory for details
 **/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "vt_comp.h"
#include "vt_defs.h"
#include "vt_mallocwrap.h"
#include "vt_pform.h"
#include "vt_thrd.h"
#include "vt_trc.h"

extern void* vftr_getname(void);
extern int vftr_getname_len(void);

/*
 * Macro for getting id of calling thread
 */

#define GET_THREAD_ID(tid) \
  VT_CHECK_THREAD;         \
  (tid) = VT_MY_THREAD

/*
 *-----------------------------------------------------------------------------
 * Simple hash table to map function names to region identifier
 *-----------------------------------------------------------------------------
 */

typedef struct HN {
  long id;             /* hash code (address of function name) */
  uint32_t vtid;       /* associated region identifier  */
  struct HN* next;
} HashNode;

#define HASH_MAX 1021

static int necsx_init = 1;       /* is initialization needed? */

static HashNode* htab[HASH_MAX];

/*
 * Stores region identifier `e' under hash code `h'
 */

static void hash_put(long h, uint32_t e) {
  long id = h % HASH_MAX;
  HashNode *add = (HashNode*)malloc(sizeof(HashNode));
  add->id = h;
  add->vtid = e;
  add->next = htab[id];
  htab[id] = add;
}

/*
 * Lookup hash code `h'
 * Returns region identifier if already stored, otherwise VT_NO_ID
 */

static uint32_t hash_get(long h) {
  long id = h % HASH_MAX;
  HashNode *curr = htab[id];
  while ( curr ) {
    if ( curr->id == h ) {
      return curr->vtid;
    }
    curr = curr->next;
  }
  return VT_NO_ID;
}

/*
 * Register new region
 */

static uint32_t register_region(uint32_t tid, char *func, int len) {
  uint32_t rid;
  static char fname[1024];

  strncpy(fname, func, len);
  fname[len] = '\0';
  rid = vt_def_region(tid, fname, VT_NO_ID, VT_NO_LNO, VT_NO_LNO,
                      NULL, VT_FUNCTION);
  hash_put((long) func, rid);
  return rid;
}

void ftrace_finalize(void);
void _ftrace_enter2_(void);
void _ftrace_exit2_(void);
void _ftrace_stop2_(void);

/*
 * Finalize instrumentation interface
 */

void ftrace_finalize()
{
  int i;

  for ( i = 0; i < HASH_MAX; i++ )
  {
    if ( htab[i] ) {
      free(htab[i]);
      htab[i] = NULL;
    }
  }
  necsx_init = 1;  
}

/*
 * This function is called at the entry of each function
 * The call is generated by the NEC SX compilers
 */

void _ftrace_enter2_() {
  char *func = (char *)vftr_getname();
  int len = vftr_getname_len();
  uint32_t tid;
  uint32_t rid;
  uint64_t time;

  /* -- if not yet initialized, initialize VampirTrace -- */
  if ( necsx_init ) {
    necsx_init = 0;
    vt_open();
    vt_comp_finalize = &ftrace_finalize;
  }

  /* -- if VampirTrace already finalized, return -- */
  if ( !vt_is_alive ) return;

  /* -- ignore NEC OMP runtime functions -- */
  if ( strchr(func, '$') != NULL ) return;

  /* -- get calling thread id -- */
  GET_THREAD_ID(tid);

  VT_SUSPEND_MALLOC_TRACING(tid);

  time = vt_pform_wtime();

  /* -- get region identifier -- */
  if ( (rid = hash_get((long) func)) == VT_NO_ID ) {
    /* -- region entered the first time, register region -- */
#if (defined(VT_MT) || defined(VT_HYB))
    VTTHRD_LOCK_IDS();
    if ( (rid = hash_get((long) func)) == VT_NO_ID )
      rid = register_region(tid, func, len);
    VTTHRD_UNLOCK_IDS();
#else /* VT_MT || VT_HYB */
    rid = register_region(tid, func, len);
#endif /* VT_MT || VT_HYB */
  }

  /* -- write enter record -- */
  vt_enter(tid, &time, rid);

  VT_RESUME_MALLOC_TRACING(tid);
}

/*
 * This function is called at the exit of each function
 * The call is generated by the NEC SX compilers
 */

void _ftrace_exit2_() {
  char *func;
  uint32_t tid;
  uint64_t time;

  /* -- if VampirTrace already finalized, return -- */
  if ( !vt_is_alive ) return;

  /* -- get calling thread id -- */
  GET_THREAD_ID(tid);

  VT_SUSPEND_MALLOC_TRACING(tid);

  func = (char *)vftr_getname();

  /* -- ignore NEC OMP runtime functions -- */
  if ( strchr(func, '$') != NULL )
  {
    VT_RESUME_MALLOC_TRACING(tid);
    return;
  }

  /* -- write exit record -- */
  time = vt_pform_wtime();
  vt_exit(tid, &time);

  VT_RESUME_MALLOC_TRACING(tid);
}

/*
 * This function is called at the exit of the program
 * The call is generated by the NEC SX compilers
 */

void _ftrace_stop2_() {
  vt_close();
}

