// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) Intel Corporation.
 * All rights reserved.
 *
 * Author: Anjaneya Chagam <anjaneya.chagam@intel.com>
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation.  See file COPYING.
 *
 */

#include "common/EventTrace.h"
#include "common/TracepointProvider.h"
#include "messages/MOSDOpReply.h"

#ifdef WITH_LTTNG
#define TRACEPOINT_DEFINE
#define TRACEPOINT_PROBE_DYNAMIC_LINKAGE
#include "tracing/eventtrace.h"
#undef TRACEPOINT_PROBE_DYNAMIC_LINKAGE
#undef TRACEPOINT_DEFINE
#else
#define tracepoint(...)
#endif

TracepointProvider::Traits event_tracepoint_traits("libeventtrace_tp.so", "event_tracing");
bool EventTrace::tpinit = false;

void EventTrace::init_tp(CephContext *_ctx)
{
  if (unlikely(!_ctx))
    return;

  if (unlikely(!tpinit)) {
    TracepointProvider::initialize<event_tracepoint_traits>(_ctx);
    tpinit = true;
  }
}

void EventTrace::set_message_attrs(const Message *m, string& oid, string& context, bool incl_oid)
{
  // arg1 = oid, arg2 = message type, arg3 = source!source_addr!tid!sequence
  if (m && (m->get_type() == CEPH_MSG_OSD_OP || m->get_type() == CEPH_MSG_OSD_OPREPLY)) {
    if (incl_oid) {
      if (m->get_type() == CEPH_MSG_OSD_OP)
        oid = ((MOSDOp *)m)->get_oid().name;
      else
        oid = ((MOSDOpReply *)m)->get_oid().name;
    }

    ostringstream buf;
    buf << m->get_source() << "!" << m->get_source_addr() << "!"
        << m->get_tid() << "!" << m->get_seq() << "!" << m->get_type();
    context = buf.str();
  }
}

EventTrace::EventTrace(CephContext *_ctx, const char *_file, const char *_func, int _line) :
  ctx(_ctx),
  file(_file),
  func(_func),
  line(_line)
{
  if (unlikely(!ctx)) 
    return;
  last_ts = ceph_clock_now();
  init_tp(ctx);

  lsubdout(ctx, eventtrace, LOG_LEVEL) << "ENTRY (" <<  func << ") " << file << ":" << line << dendl;
  tracepoint(eventtrace, func_enter, file.c_str(), func.c_str(), line);
}

EventTrace::~EventTrace()
{
  if (unlikely(!ctx)) 
    return;
  lsubdout(ctx, eventtrace, LOG_LEVEL) << "EXIT (" << func << ") " << file << dendl;
  tracepoint(eventtrace, func_exit, file.c_str(), func.c_str());
}

void EventTrace::log_event_latency(const char *event)
{
  utime_t now = ceph_clock_now();
  double usecs = (now.to_nsec()-last_ts.to_nsec())/1000;
  OID_ELAPSED("", usecs, event);
  last_ts = now;
}

void EventTrace::trace_oid_event(const char *oid, const char *event, const char *context,
  const char *file, const char *func, int line)
{
  if (unlikely(!g_ceph_context))
    return;
  init_tp(g_ceph_context);
  tracepoint(eventtrace, oid_event, oid, event, context, file, func, line);
}

void EventTrace::trace_oid_event(const Message *m, const char *event, const char *file,
  const char *func, int line, bool incl_oid)
{
  string oid, context;
  set_message_attrs(m, oid, context, incl_oid);
  trace_oid_event(oid.c_str(), event, context.c_str(), file, func, line);
}

void EventTrace::trace_oid_elapsed(const char *oid, const char *event, const char *context,
  double elapsed, const char *file, const char *func, int line)
{
  if (unlikely(!g_ceph_context))
    return;
  init_tp(g_ceph_context);
  tracepoint(eventtrace, oid_elapsed, oid, event, context, elapsed, file, func, line);
}

void EventTrace::trace_oid_elapsed(const Message *m, const char *event, double elapsed,
  const char *file, const char *func, int line, bool incl_oid)
{
  string oid, context;
  set_message_attrs(m, oid, context, incl_oid);
  trace_oid_elapsed(oid.c_str(), event, context.c_str(), elapsed, file, func, line);
}
