/* -*- c-file-style: "GNU" -*- */
/*
 * Copyright (C) CNRS, INRIA, Universite Bordeaux 1, Telecom SudParis
 * See COPYING in top-level directory.
 */

#ifndef __EZTRACE_H__
#define __EZTRACE_H__

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "eztrace_config.h"

#include <stddef.h>
#include <stdint.h>
#include <dlfcn.h>
#if USE_GETTID
#include <unistd.h>
#include <sys/syscall.h>	/* For SYS_xxx definitions */
#else
#include <pthread.h>
#endif	/* USE_GETTID */

#include "eztrace_types.h"
#include "eztrace_sampling.h"

#include "litl_types.h"
#include "litl_write.h"

#include "eztrace_litl.h"
#include "eztrace_litl_packed.h"

#define EZTRACE_API_VERSION 0x00000900

/* start the recording of events */
void eztrace_start();
/* stop recording events and write the trace to the disk */
void eztrace_stop();
/* start eztrace but only record some event (thread creation, mpi_init, mpi communicators creation..)*/
void eztrace_silent_start();

/* pre-defined color for manual instrumentation*/
enum ezt_color {
  EZTRACE_RED,
  EZTRACE_BLUE,
  EZTRACE_GREEN,
  EZTRACE_YELLOW,
  EZTRACE_PINK
};

/* Stop the recording of events  */
void eztrace_pause();
/* Restart the recording of events*/
void eztrace_resume();
/* Manually add one event and choose one color for the display */
void eztrace_enter_event(char *, enum ezt_color);
/* Set the end of the event */
void eztrace_leave_event();

/* change the trace filename */
void eztrace_set_filename(char* name);

void eztrace_code0(uint32_t code);
void eztrace_code1(uint32_t code, uint64_t arg1);
void eztrace_code2(uint32_t code, uint64_t arg1, uint64_t arg2);
void eztrace_code3(uint32_t code, uint64_t arg1, uint64_t arg2, uint64_t arg3);
void eztrace_code4(uint32_t code, uint64_t arg1, uint64_t arg2, uint64_t arg3,
		   uint64_t arg4);
void eztrace_code5(uint32_t code, uint64_t arg1, uint64_t arg2, uint64_t arg3,
		   uint64_t arg4, uint64_t arg5);
void eztrace_code6(uint32_t code, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5, uint64_t arg6);
void eztrace_code7(uint32_t code, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5, uint64_t arg6, uint64_t arg7);
void eztrace_code8(uint32_t code, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5, uint64_t arg6, uint64_t arg7, uint64_t arg8);
void eztrace_code9(uint32_t code, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5, uint64_t arg6, uint64_t arg7, uint64_t arg8, uint64_t arg9);

typedef void (*eztrace_function_t)(void);

/* register a callback to be called when the application calls
 * eztrace_start().
 * This function is only usefull when AUTOSTART is disabled
 */
void eztrace_register_init_routine(eztrace_function_t init_func);


typedef void (*eztrace_atexit_function_t)(void*);

/*
 * register a function to be called before eztrace_stop.
 */
void eztrace_atexit(eztrace_atexit_function_t f, void* param);

#define RECORD_HW_COUNTERS()			\
  {						\
    ezt_sampling_check_callbacks();		\
  }

enum ezt_trace_status {
  ezt_trace_status_uninitialized,
  ezt_trace_status_running,
  ezt_trace_status_paused,
  ezt_trace_status_stopped,
  ezt_trace_status_being_finalized, /* atexit functions are being called */
  ezt_trace_status_finalized,	/* event recording has stopped */
};

#define EZT_NB_MODULES_MAX 32

struct __ezt_write_trace {
  litl_write_trace_t *litl_trace;
  enum ezt_trace_status status;
  int debug_level;
  size_t buffer_size;
  char *filename;
  /* number of init functions to be called when the eztrace_start
   * is called.
   */
  int nb_module;
  eztrace_function_t init_routines[EZT_NB_MODULES_MAX];
};
extern struct __ezt_write_trace __ezt_trace;

#define EZT_PRINTF(_debug_level_, args...) {		\
    if(__ezt_trace.debug_level >= _debug_level_)	\
      fprintf(stderr, ##args);				\
  }

#define FUNCTION_ENTRY					\
  {							\
    EZT_PRINTF(1, "Calling [%s]\n", __FUNCTION__);	\
    RECORD_HW_COUNTERS();				\
  }

/* current thread id */
#if USE_GETTID
#define CUR_TID syscall(SYS_gettid)
#else
#define CUR_TID pthread_self()
#endif

/*
 * Since litl_write_finalize_trace() from LiTL calls litl_write_probe_reg_0() and flush() that use fwrite as well as pthread_mutex_(un)lock,
 *   litl_write_finalize_trace() needs to be protected in order to avoid being intercepted.
 */
#define EZTRACE_FIN_TRACE()					\
  {								\
    EZTRACE_PROTECT {						\
      EZTRACE_PROTECT_ON();					\
      litl_write_finalize_trace(__ezt_trace.litl_trace);        \
      EZTRACE_PROTECT_OFF();					\
    }								\
  }

/* check whether dlsym returned successfully */
#define  TREAT_ERROR()				\
  do {						\
    char * error;				\
    if ((error = dlerror()) != NULL)  {		\
      fputs(error, stderr);			\
      abort();					\
    }						\
  }while(0)

/* intercept function func and store its previous value into var */
#define INTERCEPT(func, var)					\
  do {								\
    if(var) break;						\
    void *__handle = RTLD_NEXT;					\
    var = (typeof(var)) (uintptr_t) dlsym(__handle, func);	\
  } while(0)

/* return the offset of the field MEMBER in a structure TYPE */
#define ezt_offset_of(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

/* Find the global structure's address
 * It needs :
 * - ptr: address of intern field
 * - type: type of the global structure
 * - member: name of the intern field of the global structure
 */
#define ezt_container_of(ptr, type, member)			\
  ((type *)((char *)(__typeof__ (&((type *)0)->member))(ptr)-	\
	    ezt_offset_of(type,member)))

/* record an event (code=EZTRACE_CALLING_FUNCTION) with the calling function name */
void record_backtrace();

/* return 1 if an event is being recorded. */
int recursion_shield_on();

void set_recursion_shield_on();
void set_recursion_shield_off();

/* avoid infinite recursion */
#define EZTRACE_PROTECT if(! recursion_shield_on())

/* Set the recursion shield */
#define EZTRACE_PROTECT_ON() set_recursion_shield_on()
#define EZTRACE_PROTECT_OFF() set_recursion_shield_off()

/* pptrace stuff */

/* The PPTRACE_HIJACK_FUNCTION is an array that contain the list of functions that a module instrument
 * It is used by pptrace when insering probes in the binary as well as by eztrace.preload
 */
#define PPTRACE_INTERCEPT_FULL(function, repl, orig) #function " " #orig " " #repl,
#define PPTRACE_INTERCEPT(function) #function " orig_" #function " " #function,

#define PPTRACE_START_INTERCEPT(module_name) char* PPTRACE_SYMBOL_LIST(module_name) [] = {
#define PPTRACE_END_INTERCEPT(module_name)  NULL }; extern char** PPTRACE_SYMBOL_EXTERNAL(module_name) __attribute__((alias(PPTRACE_SYMBOL_ALIAS(module_name))));

#define ezt_is_in_launcher() ((getenv("TESTLAUNCHER") != NULL) && (strcmp(getenv("TESTLAUNCHER"), "1") == 0))

/* check wether dlsym returned successfully */
#define DYNAMIC_INTERCEPTION(func, varName, var)	\
  do {							\
    var = (void**) dlsym(NULL, varName);		\
    if(NULL == var) {					\
      TREAT_ERROR();					\
    }							\
    if(NULL != *var) break;				\
    *var = (void*) dlsym((void*)-1l, func);		\
  } while(0)

#define DYNAMIC_INTERCEPT_ALL_MODULE(module_name)		\
  do {								\
    if (ezt_is_in_launcher())					\
      return;							\
    char __buff[1024];						\
    int __i; char *__j; char *__k; void **__sym;		\
    for(__i = 0; PPTRACE_SYMBOL_LIST(module_name)[__i] != NULL; __i++) { \
      strncpy(__buff, PPTRACE_SYMBOL_LIST(module_name)[__i], 1024);	\
      __buff[1023] = 0;						\
      __j = strchr(__buff, ' ');				\
      __k = strchr(__j+1, ' ');					\
      *__k = 0; *__j = 0;					\
      DYNAMIC_INTERCEPTION(__buff, (__j+1), __sym);		\
      *__k = *__j = ' ';					\
    }								\
  } while(0)

#define START_INTERCEPT_MODULE(module_name) PPTRACE_START_INTERCEPT(module_name)
#define END_INTERCEPT_MODULE(module_name) PPTRACE_END_INTERCEPT(module_name)

#define DYNAMIC_INTERCEPT_ALL()	DYNAMIC_INTERCEPT_ALL_MODULE()
#define START_INTERCEPT START_INTERCEPT_MODULE()
#define END_INTERCEPT END_INTERCEPT_MODULE()

#define INTERCEPT2(func, var) func " " #var " " func,



/* record events (code=EZTRACE_CALLING_FUNCTION) containing the backtrace */
void eztrace_record_backtrace(int backtrace_depth);
void record_backtrace();

/* get information on frame frameid
 * the collected information is copied into output_str
 * buffer_len is the maximum number of bytes written to output_str
 */
void eztrace_get_stack_frame(int frameid, char *output_str, int buffer_len);

#endif	/* __EZTRACE_H__ */
