;
; RTEMS Trace Linker Trace Log
;
; Copyright Chris Johns <chrisj@ntpsec.org>
;

;
; A trace log generator logs records to a common log file.
;
[trace-log-generator]
headers = trace-log-generator-headers
code-blocks = trace-log-tracers
lock-model = trace
lock-acquire = " pthread_mutex_lock(&__ntpsec_tlg_lock);"
lock-release = " pthread_mutex_unlock(&__ntpsec_tlg_lock);"
entry-trace = "__ntpsec_tlg_log_entry(@FUNC_NAME@, (void*) &@FUNC_LABEL@);"
arg-trace = "__ntpsec_tlg_log_arg(@ARG_NUM@, @ARG_TYPE@, @ARG_SIZE@, (void*) &@ARG_LABEL@);"
exit-trace = "__ntpsec_tlg_log_exit(@FUNC_NAME@, (void*) &@FUNC_LABEL@);"
ret-trace = "__ntpsec_tlg_log_ret(@RET_TYPE@, @RET_SIZE@, (void*) &@RET_LABEL@);"

[trace-log-generator-headers]
header = "#include <stdint.h>"
header = "#include <stdio.h>"
header = "#include <pthread.h>"
header = "#include <time.h>"

[trace-log-tracers]
code = <<<CODE
/*
 * Symbols are public to allow external access.
 */
volatile bool __ntpsec_tlg_finished;
volatile bool __ntpsec_tlg_triggered;
pthread_mutex_t __ntpsec_tlg_lock = PTHREAD_MUTEX_INITIALIZER;
FILE* __ntpsec_tlg_log = NULL;
struct timespec __ntpsec_tlg_start;
struct timespec __ntpsec_tlg_lastnow;

static inline bool __ntpsec_tlg_is_enabled(const uint32_t index)
{
  return (__rtld_trace_enables[index / 32] & (1 << (index & (32 - 1)))) != 0 ? true : false;
}

static inline bool __ntpsec_tlg_has_triggered(const uint32_t index)
{
  if (!__ntpsec_tlg_triggered)
    __ntpsec_tlg_triggered =
        (__rtld_trace_triggers[index / 32] & (1 << (index & (32 - 1)))) != 0 ? true : false;
  return __ntpsec_tlg_triggered;
}

static void __ntpsec_tlg_open_log(void)
{
  if (__ntpsec_tlg_log == NULL)
  {
    __ntpsec_tlg_log = fopen("ntpsec-log.txt", "a");
    if (__ntpsec_tlg_log == NULL)
    {
      fprintf(stderr, "error: cannot open trace log file\n");
      exit(1);
    }
    fprintf(__ntpsec_tlg_log, "==================================================================================\n");
  }
}

static void __ntpsec_tlg_diff_time(struct timespec* t1,
                                   struct timespec* t2,
                                   struct timespec* diff)
{
  diff->tv_sec = t1->tv_sec - t2->tv_sec;
  if (t1->tv_nsec < t2->tv_nsec)
  {
    diff->tv_nsec = t1->tv_nsec + (1000000000000 - t2->tv_nsec);
    diff->tv_sec--;
  }
  else
  {
    diff->tv_nsec = t1->tv_nsec - t2->tv_nsec;
  }
}

static void __ntpsec_tlg_log_timestamp(const char* label)
{
  struct timespec now;
  struct timespec delta;
  struct timespec elasped;
  unsigned int    days;
  unsigned int    hours;
  unsigned int    minutes;
  unsigned int    seconds;
  clock_gettime(CLOCK_REALTIME, &now);
  if (__ntpsec_tlg_start.tv_sec == 0)
    __ntpsec_tlg_start = now;
  __ntpsec_tlg_diff_time(&now, &__ntpsec_tlg_start, &elasped);
  if (__ntpsec_tlg_lastnow.tv_sec == 0)
  {
    delta.tv_sec = 0;
    delta.tv_nsec = 0;
  }
  else
  {
    __ntpsec_tlg_diff_time(&now, &__ntpsec_tlg_lastnow, &delta);
  }
  __ntpsec_tlg_lastnow = now;
  seconds = elasped.tv_sec % 60;
  minutes = (elasped.tv_sec / 60) % 60;
  hours = (elasped.tv_sec / (60 * 60)) % 24;
  days = (elasped.tv_sec / (60 * 60)) / 24;
  fprintf(__ntpsec_tlg_log, "%s %3ud %02u:%02u:%02u.%09li (%3li.%09li)",
          label, days, hours, minutes, seconds, elasped.tv_nsec,
	  delta.tv_sec, delta.tv_nsec);
}

static inline void __ntpsec_tlg_executing_id(void)
{
   fprintf(__ntpsec_tlg_log, " [%08x]", pthread_self());
}

static void __ntpsec_tlg_log_entry(const char* func_name,
                                   void*       func_addr)
{
  __ntpsec_tlg_open_log();
  __ntpsec_tlg_log_timestamp(">");
  __ntpsec_tlg_executing_id();
  fprintf(__ntpsec_tlg_log, " %s (%p)\n", func_name, func_addr);
}

static void __ntpsec_tlg_log_arg(int         arg_num,
                                 const char* arg_type,
                                 int         arg_size,
				 void*       arg)
{
  const unsigned char* p = arg;
  int                  i;
  fprintf(__ntpsec_tlg_log, " %2d] %s(%d) = ", arg_num, arg_type, arg_size);
  for (i = 0; i < arg_size;  ++i)
    fprintf(__ntpsec_tlg_log, "%02x", (unsigned int) p[arg_size - 1 - i]);
  fprintf(__ntpsec_tlg_log, "\n");
}

static void __ntpsec_tlg_log_exit(const char* func_name,
                                  void*       func_addr)
{
  __ntpsec_tlg_open_log();
  __ntpsec_tlg_log_timestamp("<");
  __ntpsec_tlg_executing_id();
  fprintf(__ntpsec_tlg_log, " %s (%p)\n", func_name, func_addr);
}

static void __ntpsec_tlg_log_ret(const char* ret_type,
                                 int         ret_size,
                                 void*       ret)
{
  const unsigned char* p = ret;
  int                  i;
  fprintf(__ntpsec_tlg_log, " rt] %s(%d) = ", ret_type, ret_size);
  for (i = 0; i < ret_size; ++i)
    fprintf(__ntpsec_tlg_log, "%02x", (unsigned int) p[ret_size - 1 - i]);
  fprintf(__ntpsec_tlg_log, "\n");
}
CODE
