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

#include <stdio.h>
#include <GTG.h>
#include <assert.h>

#include "eztrace_convert.h"
#include "eztrace_convert_core.h"
#include "pthread_ev_codes.h"
#include "eztrace_list.h"
#include "eztrace_stack.h"
#include "eztrace_convert_pthread.h"


/* add a hook in the thread structure in order to store information
 * about pending parallel sections
 */
struct pthread_thread_info_t *__register_thread_hook(int tid)
{
  DECLARE_CUR_PROCESS(p_process);
  struct pthread_thread_info_t *p_info = (struct pthread_thread_info_t*) malloc(sizeof(struct pthread_thread_info_t));
  p_info->p_thread = GET_THREAD_INFO(CUR_INDEX, tid);

  int i;
  for(i=0; i<NB_LOCK_TYPES; i++) {
    p_info->duration_acquire[i] = 0;
    p_info->start_acquire[i] = 0;
    p_info->duration_critical_section[i] = 0;
  }

  /* add the hook in the thread info structure */
  ezt_hook_list_add(&p_info->p_thread->hooks, p_info, (uint8_t)EZTRACE_PTHREAD_EVENTS_ID);
  return p_info;
}

/* declare a var variable that points to the thread_info structure */
#define INIT_PTHREAD_THREAD_INFO(p_thread_info, var)			\
  struct pthread_thread_info_t *var = (struct pthread_thread_info_t*)	\
    ezt_hook_list_retrieve_data(&p_thread_info->hooks, (uint8_t)EZTRACE_PTHREAD_EVENTS_ID); \
  if(!(var)) {								\
    var = __register_thread_hook(CUR_THREAD_ID);			\
  }

/* Semaphore processing */
void handle_sem_post(void) {
  FUNC_NAME;

  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  DECLARE_CUR_PROCESS(p_process);

  app_ptr lock_ptr;
  GET_PARAM_PACKED_1(CUR_EV, lock_ptr);

  struct pthread_lock_info_t *lock_info = NULL;
  GET_LOCK_INFO(lock_info, p_process, lock_ptr, SEMAPHORE);

  PTHREAD_CHANGE()
    addEvent(CURRENT, "E_LockStart", thread_id, lock_info->info);
}

void handle_start_sem_wait(void) {
  FUNC_NAME;

  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  DECLARE_CUR_PROCESS(p_process);
  DECLARE_CUR_THREAD(p_thread);
  INIT_PTHREAD_THREAD_INFO(p_thread, p_info);

  app_ptr lock_ptr;
  GET_PARAM_PACKED_1(CUR_EV, lock_ptr);

  struct pthread_lock_info_t *lock_info = NULL;
  GET_LOCK_INFO(lock_info, p_process, lock_ptr, SEMAPHORE);
  record_lock_start_acquire(lock_info, p_info);

  PTHREAD_CHANGE()
    pushState(CURRENT, "ST_Thread", thread_id, "STV_Blocked_sem");
}

void handle_stop_sem_wait(void) {
  FUNC_NAME;

  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  DECLARE_CUR_PROCESS(p_process);
  DECLARE_CUR_THREAD(p_thread);
  INIT_PTHREAD_THREAD_INFO(p_thread, p_info);

  app_ptr lock_ptr;
  int retval;
  GET_PARAM_PACKED_2(CUR_EV, lock_ptr, retval);

  struct pthread_lock_info_t *lock_info = NULL;
  GET_LOCK_INFO(lock_info, p_process, lock_ptr, SEMAPHORE);
  record_lock_acquired(lock_info, p_info);

  PTHREAD_CHANGE()
    popState(CURRENT, "ST_Thread", thread_id);
  PTHREAD_CHANGE()
    addEvent(CURRENT, "E_SemWait_Done", thread_id, lock_info->info);
}

/* Spinlock processing */
void handle_spin_lock_start() {
  FUNC_NAME;

  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  DECLARE_CUR_PROCESS(p_process);
  DECLARE_CUR_THREAD(p_thread);
  INIT_PTHREAD_THREAD_INFO(p_thread, p_info);

  app_ptr lock_ptr;
  GET_PARAM_PACKED_1(CUR_EV, lock_ptr);

  struct pthread_lock_info_t *lock_info = NULL;
  GET_LOCK_INFO(lock_info, p_process, lock_ptr, SPINLOCK);
  record_lock_start_acquire(lock_info, p_info);

  PTHREAD_CHANGE()
    pushState(CURRENT, "ST_Thread", thread_id, "STV_Blocked_spin");
}

void handle_spin_lock_stop() {
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  DECLARE_CUR_PROCESS(p_process);
  DECLARE_CUR_THREAD(p_thread);
  INIT_PTHREAD_THREAD_INFO(p_thread, p_info);

  app_ptr lock_ptr;
  int retval;
  GET_PARAM_PACKED_2(CUR_EV, lock_ptr, retval);

  struct pthread_lock_info_t *lock_info = NULL;
  GET_LOCK_INFO(lock_info, p_process, lock_ptr, SPINLOCK);
  record_lock_acquired(lock_info, p_info);

  PTHREAD_CHANGE()
    popState(CURRENT, "ST_Thread", thread_id);
  PTHREAD_CHANGE()
    pushState(CURRENT, "ST_Thread", thread_id, "STV_Critical_spin");
}

void handle_spin_trylock() {
  FUNC_NAME;
  DECLARE_CUR_THREAD(p_thread);
  INIT_PTHREAD_THREAD_INFO(p_thread, p_info);

  app_ptr lock_ptr;
  int result;
  GET_PARAM_PACKED_2(CUR_EV, lock_ptr, result);

  if (result) {
    DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
    DECLARE_CUR_PROCESS(p_process);

    struct pthread_lock_info_t *lock_info = NULL;
    GET_LOCK_INFO(lock_info, p_process, lock_ptr, SPINLOCK);
    record_lock_start_acquire(lock_info, p_info);
    record_lock_acquired(lock_info, p_info);

    PTHREAD_CHANGE()
      pushState(CURRENT, "ST_Thread", thread_id, "STV_Critical_spin");
  }
}

void handle_spin_unlock() {
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  DECLARE_CUR_PROCESS(p_process);
  DECLARE_CUR_THREAD(p_thread);
  INIT_PTHREAD_THREAD_INFO(p_thread, p_info);

  app_ptr lock_ptr;
  GET_PARAM_PACKED_1(CUR_EV, lock_ptr);

  struct pthread_lock_info_t *lock_info = NULL;
  GET_LOCK_INFO(lock_info, p_process, lock_ptr, SPINLOCK);

  record_lock_release(lock_info, p_info);
  //  __check_lock_mismatch(lock_info, p_process, CUR_THREAD_ID);

  PTHREAD_CHANGE()
    popState(CURRENT, "ST_Thread", thread_id);
}

/* Mutex processing */
void handle_mutex_trylock(int result) {
  FUNC_NAME;
  if (!result)
    return;

  app_ptr lock_ptr;
  GET_PARAM_PACKED_1(CUR_EV, lock_ptr);

  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  DECLARE_CUR_PROCESS(p_process);
  DECLARE_CUR_THREAD(p_thread);
  INIT_PTHREAD_THREAD_INFO(p_thread, p_info);

  struct pthread_lock_info_t *lock_info = NULL;
  GET_LOCK_INFO(lock_info, p_process, lock_ptr, MUTEX);
  record_lock_acquired(lock_info, p_info);

  PTHREAD_CHANGE()
    pushState(CURRENT, "ST_Thread", thread_id, "STV_Critical_mutex");
}

void handle_mutex_lock_start() {
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  DECLARE_CUR_PROCESS(p_process);
  DECLARE_CUR_THREAD(p_thread);
  INIT_PTHREAD_THREAD_INFO(p_thread, p_info);

  app_ptr lock_ptr;
  GET_PARAM_PACKED_1(CUR_EV, lock_ptr);

  struct pthread_lock_info_t *lock_info = NULL;
  GET_LOCK_INFO(lock_info, p_process, lock_ptr, MUTEX);
  record_lock_start_acquire(lock_info, p_info);

  PTHREAD_CHANGE()
    pushState(CURRENT, "ST_Thread", thread_id, "STV_Blocked_mutex");
}

void handle_mutex_lock_stop() {
  FUNC_NAME;

  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  DECLARE_CUR_PROCESS(p_process);
  DECLARE_CUR_THREAD(p_thread);
  INIT_PTHREAD_THREAD_INFO(p_thread, p_info);

  app_ptr lock_ptr;
  int retval;
  GET_PARAM_PACKED_2(CUR_EV, lock_ptr, retval);

  struct pthread_lock_info_t *lock_info = NULL;
  GET_LOCK_INFO(lock_info, p_process, lock_ptr, MUTEX);
  record_lock_acquired(lock_info, p_info);

  PTHREAD_CHANGE()
    popState(CURRENT, "ST_Thread", thread_id);
  PTHREAD_CHANGE()
    pushState(CURRENT, "ST_Thread", thread_id, "STV_Critical_mutex");
}

void handle_mutex_unlock() {
  FUNC_NAME;

  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  DECLARE_CUR_PROCESS(p_process);
  DECLARE_CUR_THREAD(p_thread);
  INIT_PTHREAD_THREAD_INFO(p_thread, p_info);

  app_ptr lock_ptr;
  GET_PARAM_PACKED_1(CUR_EV, lock_ptr);

  struct pthread_lock_info_t *lock_info = NULL;
  GET_LOCK_INFO(lock_info, p_process, lock_ptr, MUTEX);
  record_lock_release(lock_info, p_info);
  //  __check_lock_mismatch(lock_info, p_process, CUR_THREAD_ID);

  PTHREAD_CHANGE()
    addEvent(CURRENT, "E_Mutex_Unlock", thread_id, lock_info->info);
  PTHREAD_CHANGE()
    popState(CURRENT, "ST_Thread", thread_id);
}

/* Condition processing */
void handle_cond_signal() {
  FUNC_NAME;
}

void handle_cond_broadcast() {
  FUNC_NAME;
}

void handle_cond_start_wait() {
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  DECLARE_CUR_PROCESS(p_process);
  DECLARE_CUR_THREAD(p_thread);
  INIT_PTHREAD_THREAD_INFO(p_thread, p_info);

  app_ptr lock_ptr;
  GET_PARAM_PACKED_1(CUR_EV, lock_ptr);

  struct pthread_lock_info_t *lock_info = NULL;
  GET_LOCK_INFO(lock_info, p_process, lock_ptr, CONDITION);
  record_lock_start_acquire(lock_info, p_info);

  PTHREAD_CHANGE() pushState(CURRENT, "ST_Thread", thread_id, "STV_Blocked_cond");
}

void handle_cond_stop_wait() {
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  DECLARE_CUR_PROCESS(p_process);
  DECLARE_CUR_THREAD(p_thread);
  INIT_PTHREAD_THREAD_INFO(p_thread, p_info);

  app_ptr lock_ptr;
  GET_PARAM_PACKED_1(CUR_EV, lock_ptr);

  struct pthread_lock_info_t *lock_info = NULL;
  GET_LOCK_INFO(lock_info, p_process, lock_ptr, CONDITION);
  record_lock_acquired(lock_info, p_info);

  PTHREAD_CHANGE()
    popState(CURRENT, "ST_Thread", thread_id);
}

/* RWLock processing */
void handle_rwlock_rdlock_start() {
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  DECLARE_CUR_PROCESS(p_process);
  DECLARE_CUR_THREAD(p_thread);
  INIT_PTHREAD_THREAD_INFO(p_thread, p_info);

  app_ptr lock_ptr;
  GET_PARAM_PACKED_1(CUR_EV, lock_ptr);

  struct pthread_lock_info_t *lock_info = NULL;
  GET_LOCK_INFO(lock_info, p_process, lock_ptr, RWLOCK);
  record_lock_start_acquire(lock_info, p_info);

  PTHREAD_CHANGE()
    pushState(CURRENT, "ST_Thread", thread_id, "STV_Blocked_rwlock");
}

void handle_rwlock_rdlock_stop() {
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  DECLARE_CUR_PROCESS(p_process);
  DECLARE_CUR_THREAD(p_thread);
  INIT_PTHREAD_THREAD_INFO(p_thread, p_info);

  app_ptr lock_ptr;
  GET_PARAM_PACKED_1(CUR_EV, lock_ptr);

  struct pthread_lock_info_t *lock_info = NULL;
  GET_LOCK_INFO(lock_info, p_process, lock_ptr, RWLOCK);
  record_lock_acquired(lock_info, p_info);

  PTHREAD_CHANGE()
    popState(CURRENT, "ST_Thread", thread_id);
  PTHREAD_CHANGE()
    pushState(CURRENT, "ST_Thread", thread_id, "STV_Critical_rwlock");
}

void handle_rwlock_wrlock_start() {
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  DECLARE_CUR_PROCESS(p_process);
  DECLARE_CUR_THREAD(p_thread);
  INIT_PTHREAD_THREAD_INFO(p_thread, p_info);

  app_ptr lock_ptr;
  GET_PARAM_PACKED_1(CUR_EV, lock_ptr);

  struct pthread_lock_info_t *lock_info = NULL;
  GET_LOCK_INFO(lock_info, p_process, lock_ptr, RWLOCK);
  record_lock_start_acquire(lock_info, p_info);

  PTHREAD_CHANGE()
    pushState(CURRENT, "ST_Thread", thread_id, "STV_Blocked_rwlock");
}

void handle_rwlock_wrlock_stop() {
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  DECLARE_CUR_PROCESS(p_process);
  DECLARE_CUR_THREAD(p_thread);
  INIT_PTHREAD_THREAD_INFO(p_thread, p_info);

  app_ptr lock_ptr;
  GET_PARAM_PACKED_1(CUR_EV, lock_ptr);

  struct pthread_lock_info_t *lock_info = NULL;
  GET_LOCK_INFO(lock_info, p_process, lock_ptr, RWLOCK);
  record_lock_acquired(lock_info, p_info);

  PTHREAD_CHANGE()
    popState(CURRENT, "ST_Thread", thread_id);
  PTHREAD_CHANGE()
    pushState(CURRENT, "ST_Thread", thread_id, "STV_Critical_rwlock");
}

void handle_rwlock_unlock() {
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  DECLARE_CUR_PROCESS(p_process);
  DECLARE_CUR_THREAD(p_thread);
  INIT_PTHREAD_THREAD_INFO(p_thread, p_info);

  app_ptr lock_ptr;
  GET_PARAM_PACKED_1(CUR_EV, lock_ptr);

  struct pthread_lock_info_t *lock_info = NULL;
  GET_LOCK_INFO(lock_info, p_process, lock_ptr, RWLOCK);

  record_lock_release(lock_info, p_info);
  //  __check_lock_mismatch(lock_info, p_process, CUR_THREAD_ID);

  PTHREAD_CHANGE()
    popState(CURRENT, "ST_Thread", thread_id);
}

void handle_barrier_start() {
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  DECLARE_CUR_PROCESS(p_process);
  DECLARE_CUR_THREAD(p_thread);
  INIT_PTHREAD_THREAD_INFO(p_thread, p_info);

  app_ptr lock_ptr;
  GET_PARAM_PACKED_1(CUR_EV, lock_ptr);
  struct pthread_lock_info_t *lock_info = NULL;
  GET_LOCK_INFO(lock_info, p_process, lock_ptr, BARRIER);
  record_lock_start_acquire(lock_info, p_info);

  PTHREAD_CHANGE() pushState(CURRENT, "ST_Thread", thread_id, "STV_Blocked_barrier");
}

void handle_barrier_stop() {
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  DECLARE_CUR_PROCESS(p_process);
  DECLARE_CUR_THREAD(p_thread);
  INIT_PTHREAD_THREAD_INFO(p_thread, p_info);

  app_ptr lock_ptr;
  GET_PARAM_PACKED_1(CUR_EV, lock_ptr);
  struct pthread_lock_info_t *lock_info = NULL;
  GET_LOCK_INFO(lock_info, p_process, lock_ptr, BARRIER);
  record_lock_acquired(lock_info, p_info);
  record_lock_release(lock_info, p_info);

  PTHREAD_CHANGE()
    popState(CURRENT, "ST_Thread", thread_id);
}

int eztrace_convert_pthread_init() {
  if (get_mode() == EZTRACE_CONVERT) {
    addEventType("E_SemPost", "CT_Thread", "SemPost");
    addEventType("E_SemWait_Done", "CT_Thread", "SemWait done");

    addEventType("E_Mutex_Lock", "CT_Thread", "Mutex Lock");
    addEventType("E_Mutex_Unlock", "CT_Thread", "Mutex Unlock");

    addEventType("E_CondSignal", "CT_Thread", "Cond Signal");

    addEventType("E_LockStart", "CT_Thread", "Waiting for a lock");
    addEventType("E_LockStop", "CT_Thread", "Lock acquired");

    addEntityValue("STV_Blocked_sem", "ST_Thread", "Blocked on a semaphore",
                   GTG_RED);
    addEntityValue("STV_Blocked_spin", "ST_Thread", "Blocked on a spinlock",
                   GTG_RED);
    addEntityValue("STV_Blocked_mutex", "ST_Thread", "Blocked on a mutex",
                   GTG_RED);
    addEntityValue("STV_Blocked_condition", "ST_Thread",
                   "Blocked on a condition", GTG_RED);
    addEntityValue("STV_Blocked_rwlock", "ST_Thread", "Blocked on a rwlock",
                   GTG_RED);
    addEntityValue("STV_Blocked_barrier", "ST_Thread", "Blocked on a barrier",
                   GTG_RED);

    addEntityValue("STV_Critical_sem", "ST_Thread",
                   "Critical Section (semaphore)", GTG_GREEN);
    addEntityValue("STV_Critical_spin", "ST_Thread",
                   "Critical Section (spinlock)", GTG_GREEN);
    addEntityValue("STV_Critical_mutex", "ST_Thread",
                   "Critical Section (mutex)", GTG_GREEN);
    addEntityValue("STV_Critical_condition", "ST_Thread",
                   "Critical Section (condition)", GTG_GREEN);
    addEntityValue("STV_Critical_rwlock", "ST_Thread",
                   "Critical Section (rwlock)", GTG_GREEN);
  }
  return 0;
}

/* return 1 if the event was handled */
int handle_pthread_events(eztrace_event_t *ev) {
  if (!STARTED)
    return 0;

  switch (LITL_READ_GET_CODE(ev)) {
  /* PThread creation/destruction */

  /* Semaphore */
  case EZTRACE_SEM_POST:
    handle_sem_post();
    break;
  case EZTRACE_SEM_START_WAIT:
    handle_start_sem_wait();
    break;
  case EZTRACE_SEM_STOP_WAIT:
    handle_stop_sem_wait();
    break;

    /* Spinlock */
  case EZTRACE_SPIN_LOCK_START:
    handle_spin_lock_start();
    break;
  case EZTRACE_SPIN_LOCK_STOP:
    handle_spin_lock_stop();
    break;
  case EZTRACE_SPIN_TRYLOCK:
    handle_spin_trylock();
    break;
  case EZTRACE_SPIN_UNLOCK:
    handle_spin_unlock();
    break;

    /* Mutex */
  case EZTRACE_MUTEX_TRYLOCK_SUCCESS:
    handle_mutex_trylock(1);
    break;
  case EZTRACE_MUTEX_TRYLOCK_FAIL:
    handle_mutex_trylock(0);
    break;
  case EZTRACE_MUTEX_LOCK_START:
    handle_mutex_lock_start();
    break;
  case EZTRACE_MUTEX_LOCK_STOP:
    handle_mutex_lock_stop();
    break;
  case EZTRACE_MUTEX_UNLOCK:
    handle_mutex_unlock();
    break;

    /* Condition */
  case EZTRACE_COND_SIGNAL:
    handle_cond_signal();
    break;
  case EZTRACE_COND_BROADCAST:
    handle_cond_broadcast();
    break;
  case EZTRACE_COND_START_WAIT:
    handle_cond_start_wait();
    break;
  case EZTRACE_COND_STOP_WAIT:
    handle_cond_stop_wait();
    break;

    /* RWLock */
  case EZTRACE_RWLOCK_RDLOCK_START:
    handle_rwlock_rdlock_start();
    break;
  case EZTRACE_RWLOCK_RDLOCK_STOP:
    handle_rwlock_rdlock_stop();
    break;
  case EZTRACE_RWLOCK_WRLOCK_START:
    handle_rwlock_wrlock_start();
    break;
  case EZTRACE_RWLOCK_WRLOCK_STOP:
    handle_rwlock_wrlock_stop();
    break;
  case EZTRACE_RWLOCK_UNLOCK:
    handle_rwlock_unlock();
    break;

  case EZTRACE_BARRIER_START:
    handle_barrier_start();
    break;
  case EZTRACE_BARRIER_STOP:
    handle_barrier_stop();
    break;
  default:
    return 0;
  }
  return 1;
}

int handle_pthread_stats(eztrace_event_t *ev) {
  recording_stats = 1;
  return handle_pthread_events(ev);
}

void print_pthread_stats() {
  printf("\nPThread:\n");
  printf("-------\n");
  /* todo:
   * add:
   *  - min/max/average duration of critical sections
   *  - min/max/average delay to get a lock
   */

  int i;
  for (i = 0; i < NB_TRACES; i++) {
    struct process_info_t *p_process = GET_PROCESS_INFO(i);
    struct pthread_process_info_t *p_info =
      (struct pthread_process_info_t*) ezt_hook_list_retrieve_data(
          &p_process->hooks, (uint8_t) EZTRACE_PTHREAD_EVENTS_ID);

    if (!p_info)
      continue;

    uint32_t nb_acquire = 0;
    uint32_t nb_locks = 0;

    printf("%s:\n", p_process->container->name);
    ezt_stack_token_t *token;
    ezt_list_foreach(&p_info->lock_list, token)
    {
      struct pthread_lock_info_t * lock =
        (struct pthread_lock_info_t *) token->data;
      nb_acquire += lock->nb_acquire;
      nb_locks++;
      char lock_type[80];
      __lock_type_to_str(lock_type, lock->lock);
      printf("\t%s 0x%p was acquired %d times.", lock_type, (void*)lock->ptr, lock->nb_acquire);

      if (lock->duration_critical_section)
        printf(" total duration of critical_sections: %lf ms.",
               lock->duration_critical_section);

      if (lock->duration_acquire)
        printf(" total time spent waiting: %lf ms.",
               lock->duration_acquire);
      printf("\n");
    }
    printf("Total: %d locks acquired %d times\n", nb_locks, nb_acquire);

    __free_lock_info_list(p_info);
  }


  double total_duration_acquire[NB_LOCK_TYPES]; /* time spent in waiting for a lock */
  double total_duration_critical_section[NB_LOCK_TYPES]; /* time spent in a critical section */
  int l;
  for(l=0; l<NB_LOCK_TYPES; l++) {
    total_duration_acquire[l] = 0;
    total_duration_critical_section[l] = 0;
  }
  for(i=0; i<NB_TRACES; i++) {
    unsigned j;
    struct eztrace_container_t* p_cont = GET_PROCESS_CONTAINER(i);
    for(j=0; j<p_cont->nb_children; j++) {
      struct eztrace_container_t* p_thread = p_cont->children[j];
      struct thread_info_t *ptr = (struct thread_info_t*)(p_thread->container_info);
      struct pthread_thread_info_t *t_info = (struct pthread_thread_info_t*) ezt_hook_list_retrieve_data(&ptr->hooks, (uint8_t)EZTRACE_PTHREAD_EVENTS_ID);

      if(!t_info)
	continue;

      int l;
      int should_skip=1;
      for(l=0; l<NB_LOCK_TYPES; l++) {
	if(t_info->duration_acquire[l] || t_info->duration_critical_section[l]) {
	  should_skip=0;
	}
      }

      if(should_skip) {
	continue;
      }

      printf("\nThread %s\n", p_thread->name);
      for(l=0; l<NB_LOCK_TYPES; l++) {
	if(t_info->duration_acquire[l]) {
	  char lock_type[80];
	  __lock_type_to_str(lock_type, l);
	  printf("\ttime spent waiting on a %s: %lf ms\n", lock_type, t_info->duration_acquire[l]);
	  total_duration_acquire[l] += t_info->duration_acquire[l];
	}

	if(t_info->duration_critical_section[l]) {
	  char lock_type[80];
	  __lock_type_to_str(lock_type, l);
	  printf("\ttime spent in a %s critical: %lf ms\n", lock_type,
		 t_info->duration_critical_section[l]);
	  total_duration_critical_section[l] += t_info->duration_critical_section[l];
	}
      }
    }

    printf("\nTotal for %s\n", p_cont->name);
    int l;
    for(l=0; l<NB_LOCK_TYPES; l++) {
      if(total_duration_acquire[l]) {
	char lock_type[80];
	__lock_type_to_str(lock_type, l);
	printf("\ttime spent waiting on a %s: %lf ms\n", lock_type, total_duration_acquire[l]);
      }
      if(total_duration_critical_section[l]) {
	char lock_type[80];
	__lock_type_to_str(lock_type, l);
	printf("\ttime spent in a %s critical: %lf ms\n", lock_type,
	       total_duration_critical_section[l]);
      }
    }
  }
}

struct eztrace_convert_module pthread_module;

void libinit(void) __attribute__ ((constructor));
void libinit(void) {
  pthread_module.api_version = EZTRACE_API_VERSION;
  pthread_module.init = eztrace_convert_pthread_init;
  pthread_module.handle = handle_pthread_events;
  pthread_module.handle_stats = handle_pthread_stats;
  pthread_module.print_stats = print_pthread_stats;

  pthread_module.module_prefix = EZTRACE_PTHREAD_EVENTS_ID;
  int res __attribute__ ((__unused__));
  res = asprintf(&pthread_module.name, "pthread");
  res =
    asprintf(
        &pthread_module.description,
        "Module for PThread synchronization functions (mutex, semaphore, spinlock, etc.)");

  pthread_module.token.data = &pthread_module;
  eztrace_convert_register_module(&pthread_module);
}

void libfinalize(void) __attribute__ ((destructor));
void libfinalize(void) {
}
