/*
 * Copyright (c) 2011 The Native Client Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */


/*
 * Simple Perf Counter Layer to be used by the rest of the service run time
 */

#include "native_client/src/shared/platform/nacl_log.h"
#include "native_client/src/shared/platform/nacl_time.h"
#include "native_client/src/include/portability.h"
#include "native_client/src/include/portability_string.h"
#include "native_client/src/include/nacl_assert.h"
#include "native_client/src/include/nacl_macros.h"
#include "native_client/src/trusted/perf_counter/nacl_perf_counter.h"

#define LAST_IDX(X) (NACL_ARRAY_SIZE(X)-1)

void NaClPerfCounterCtor(struct NaClPerfCounter *sv,
                         const char *app_name) {
  if (NULL == sv) {
    NaClLog(LOG_ERROR, "NaClPerfCounterStart received null pointer\n");
    return;
  };

  memset(sv, 0, sizeof(struct NaClPerfCounter));

  if (NULL == app_name) {
    app_name = "__unknown_app__";
  }

  NACL_ASSERT_IS_ARRAY(sv->app_name);
  strncpy(sv->app_name, app_name, LAST_IDX(sv->app_name));

  /* Being explicit about string termination */
  sv->app_name[LAST_IDX(sv->app_name)] = '\0';

  strncpy(sv->sample_names[0], "__start__", LAST_IDX(sv->sample_names[0]));


  while (0 != NaClGetTimeOfDay(&sv->sample_list[sv->samples])) {
    /* repeat until we get a sample */
  }

  sv->samples++;
}


/*
 * Records the time in sv and  returns its index in sv
 * Note that the first time this routine is called on a sv that was just
 * constructed via NaClPerfCounterCtor(), it will return 1, but that
 * is actually the SECOND sample.
 */
int NaClPerfCounterMark(struct NaClPerfCounter *sv, const char *ev_name) {
  if ((NULL == sv) || (NULL == ev_name)) {
    NaClLog(LOG_ERROR, "NaClPerfCounterMark received null args\n");
    return -1;
  }
  if (sv->samples >= NACL_MAX_PERF_COUNTER_SAMPLES) {
    NaClLog(LOG_ERROR, "NaClPerfCounterMark going beyond buffer size\n");
    return -1;
  }
  /* busy loop until we succeed, damn it */
  while (0 != NaClGetTimeOfDay(&(sv->sample_list[sv->samples])));

  /*
   * This relies upon memset() inside NaClPerfCounterCtor() for
   * correctness
   */

  NACL_ASSERT_IS_ARRAY(sv->sample_names[sv->samples]);

  strncpy(sv->sample_names[sv->samples], ev_name,
          LAST_IDX(sv->sample_names[sv->samples]));
  /* Being explicit about string termination */
  sv->sample_names[sv->samples][LAST_IDX(sv->sample_names[sv->samples])] =
    '\0';

  return (sv->samples)++;
}


int64_t NaClPerfCounterInterval(struct NaClPerfCounter *sv,
                                uint32_t a, uint32_t b) {
  if ((NULL != sv) && (a < ((unsigned)sv->samples)) &&
      (b < ((unsigned)sv->samples)) &&
      (sv->samples <= NACL_MAX_PERF_COUNTER_SAMPLES)) {
    uint32_t lo = (a < b)? a : b;
    uint32_t hi = (b < a)? a : b;
    int64_t seconds = (sv->sample_list[hi].nacl_abi_tv_sec -
                       sv->sample_list[lo].nacl_abi_tv_sec);
    int64_t usec = (sv->sample_list[hi].nacl_abi_tv_usec -
                    sv->sample_list[lo].nacl_abi_tv_usec);
    int64_t rtn = seconds * NACL_MICROS_PER_UNIT + usec;

    NaClLog(1, "NaClPerfCounterInterval(%s %s:%s): %"NACL_PRId64" microsecs\n",
            sv->app_name, sv->sample_names[lo], sv->sample_names[hi], rtn);

    return rtn;
  }
  return -1;
}

int64_t NaClPerfCounterIntervalLast(struct NaClPerfCounter *sv) {
  if (NULL != sv) {
    return NaClPerfCounterInterval(sv, sv->samples - 2, sv->samples - 1);
  }
  return -1;
}

int64_t NaClPerfCounterIntervalTotal(struct NaClPerfCounter *sv) {
  if (NULL != sv) {
    return NaClPerfCounterInterval(sv, 0, sv->samples - 1);
  }
  return -1;
}
