// @(#)root/proof:$Name:  $:$Id: TPerfStats.cxx,v 1.1 2004/06/13 16:26:35 rdm Exp $
// Author: Kristjan Gulbrandsen   11/05/04

/*************************************************************************
 * Copyright (C) 1995-2004, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TPerfStats                                                           //
//                                                                      //
// Provides the interface for the PROOF internal performance measurment //
// and event tracing.                                                   //
//                                                                      //
//////////////////////////////////////////////////////////////////////////


#include "TPerfStats.h"

#include "Riostream.h"
#include "TCollection.h"
#include "TEnv.h"
#include "TError.h"
#include "TFile.h"
#include "TH1.h"
#include "TH2.h"
#include "TProofDebug.h"
#include "TProof.h"
#include "TProofServ.h"
#include "TSlave.h"
#include "TTree.h"


ClassImp(TPerfEvent)
ClassImp(TPerfStats)


//------------------------------------------------------------------------------

//______________________________________________________________________________
TPerfEvent::TPerfEvent(TTimeStamp *offset)
   : fEvtNode(-3), fType(TVirtualPerfStats::kUnDefined), fSlave(0),
     fEventsProcessed(0), fBytesRead(0), fLen(0), fLatency(0.0), fProcTime(0.0), fCpuTime(0.0),
     fIsStart(kFALSE), fIsOk(kFALSE)
{
   if (gProofServ != 0) {
      fEvtNode = gProofServ->IsMaster() ? -1: gProofServ->GetOrdinal();
   } else {
      fEvtNode = -2; // not on a PROOF server
   }

   if (offset != 0) {
      fTimeStamp = TTimeStamp(fTimeStamp.GetSec() - offset->GetSec(),
                     fTimeStamp.GetNanoSec() - offset->GetNanoSec());
   }
}


//______________________________________________________________________________
Int_t TPerfEvent::Compare(const TObject *obj) const
{
   // Compare method. Must return -1 if this is smaller than obj,
   // 0 if objects are equal and 1 if this is larger than obj.

   const TPerfEvent *pe = dynamic_cast<const TPerfEvent*>(obj);

   Assert(pe != 0);

   if (fTimeStamp < pe->fTimeStamp) {
      return -1;
   } else if (fTimeStamp == pe->fTimeStamp) {
      return 0;
   } else {
      return 1;
   }
}


//______________________________________________________________________________
void TPerfEvent::Print(Option_t *) const
{
   cout << "TPerfEvent: ";

   if ( fEvtNode == -2 ) {
      cout << "StandAlone ";
   } else if ( fEvtNode == -1 ) {
      cout << "Master ";
   } else {
      cout << "Slave " << fEvtNode << " ";
   }
   cout << TVirtualPerfStats::EventType(fType) << " "
        << double(fTimeStamp)
        << endl;
}


//------------------------------------------------------------------------------

//______________________________________________________________________________
TPerfStats::TPerfStats(Int_t nslaves, TList *input, TList *output)
   : fTrace(0), fPerfEvent(0), fPacketsHist(0), fEventsHist(0), fLatencyHist(0),
      fProcTimeHist(0), fCpuTimeHist(0)
{
   // Normal Constructor.

   PDB(kGlobal,1) Info("TPerfStats", "Statistics for %d slave(s)", nslaves);

   Bool_t doHist = (input->FindObject("PROOF_StatsHist") != 0);
   Bool_t doTrace = (input->FindObject("PROOF_StatsTrace") != 0);

   if (doTrace) {
      // Construct tree
      fTrace = new TTree("PerfStats","PROOF Statistics");
      fTrace->SetDirectory(0);
      fTrace->Bronch("PerfEvents","TPerfEvent",&fPerfEvent,64000,0);
      output->Add(fTrace);
   }

   if (gProofServ->IsMaster() && doHist) {
      // Make Histograms
      Double_t time_per_bin = 1e-3; // 10ms
      Double_t min_time = 0;
      Int_t ntime_bins = 2000;

      fPacketsHist = new TH1D("PacketsHist", "Packets processed per Slave",
                              nslaves, 0, nslaves);
      fPacketsHist->SetDirectory(0);
      output->Add(fPacketsHist);

      fEventsHist = new TH1D("EventsHist","Events processed per Slave",
                             nslaves, 0, nslaves);
      fEventsHist->SetDirectory(0);
      output->Add(fEventsHist);

      fNodeHist = new TH1D("NodeHist","Slaves per Fileserving Node",
                             nslaves, 0, nslaves);
      fNodeHist->SetDirectory(0);
      fNodeHist->SetBit(TH1::kCanRebin);
      output->Add(fNodeHist);

      fLatencyHist = new TH2D("LatencyHist","GetPacket Latency per Slave",
                              nslaves, 0, nslaves,
                              ntime_bins, min_time, time_per_bin*ntime_bins);
      fLatencyHist->SetDirectory(0);
      output->Add(fLatencyHist);

      fProcTimeHist = new TH2D("ProcTimeHist","Packet Processing Time per Slave",
                               nslaves, 0, nslaves,
                               ntime_bins, min_time, time_per_bin*ntime_bins);
      fProcTimeHist->SetDirectory(0);
      output->Add(fProcTimeHist);

      fCpuTimeHist = new TH2D("CpuTimeHist","Packet CPU Time per Slave",
                              nslaves, 0, nslaves,
                              ntime_bins, min_time, time_per_bin*ntime_bins);
      fCpuTimeHist->SetDirectory(0);
      output->Add(fCpuTimeHist);
   }
}


//______________________________________________________________________________
void TPerfStats::SimpleEvent(EEventType type)
{
   if (type == kStop && fPacketsHist != 0) {
      fNodeHist->LabelsDeflate("X");
      fNodeHist->LabelsOption("auv","X");
   }

   if (fTrace == 0) return;

   TPerfEvent pe(&fTzero);
   pe.fType = type;

   fPerfEvent = &pe;
   fTrace->SetBranchAddress("PerfEvents",&fPerfEvent);
   fTrace->Fill();
   fPerfEvent = 0;
}


//______________________________________________________________________________
void TPerfStats::PacketEvent(Int_t slave, const char* slavename, const char* filename,
                              Long64_t eventsprocessed, Double_t latency, Double_t proctime,
                              Double_t cputime, Long64_t bytesRead)
{
   if (fTrace != 0) {
      TPerfEvent pe(&fTzero);

      pe.fType = kPacket;
      pe.fSlaveName = slavename;
      pe.fFileName = filename;
      pe.fSlave = slave;
      pe.fEventsProcessed = eventsprocessed;
      pe.fBytesRead = bytesRead;
      pe.fLatency = latency;
      pe.fProcTime = proctime;
      pe.fCpuTime = cputime;

      fPerfEvent = &pe;
      fTrace->SetBranchAddress("PerfEvents",&fPerfEvent);
      fTrace->Fill();
      fPerfEvent = 0;
   }

   if (fPacketsHist != 0) {
      fPacketsHist->Fill(slave);
      fEventsHist->Fill(slave, eventsprocessed);
      fLatencyHist->Fill(slave, latency);
      fProcTimeHist->Fill(slave, proctime);
      fCpuTimeHist->Fill(slave, cputime);
   }
}


//______________________________________________________________________________
void TPerfStats::FileEvent(Int_t slave, const char *slavename, const char *nodename,
                            const char *filename, Bool_t isStart)
{
   if (fTrace != 0) {
      TPerfEvent pe(&fTzero);

      pe.fType = kFile;
      pe.fSlaveName = slavename;
      pe.fNodeName = nodename;
      pe.fFileName = filename;
      pe.fSlave = slave;
      pe.fIsStart = isStart;

      fPerfEvent = &pe;
      fTrace->SetBranchAddress("PerfEvents",&fPerfEvent);
      fTrace->Fill();
      fPerfEvent = 0;
   }

   if (fPacketsHist != 0) {
      fNodeHist->Fill(nodename, isStart ? 1 : -1);
   }
}


//______________________________________________________________________________
void TPerfStats::FileOpenEvent(TFile *file, const char *filename, Double_t proctime)
{
   if (fTrace != 0) {
      TPerfEvent pe(&fTzero);

      pe.fType = kFileOpen;
      pe.fFileName = filename;
      pe.fFileClass = file != 0 ? file->ClassName() : "none";
      pe.fProcTime = proctime;
      pe.fIsOk = (file != 0);

      fPerfEvent = &pe;
      fTrace->SetBranchAddress("PerfEvents",&fPerfEvent);
      fTrace->Fill();
      fPerfEvent = 0;
   }
}


//______________________________________________________________________________
void TPerfStats::FileReadEvent(TFile *file, Int_t len, Double_t proctime)
{
   if (fTrace != 0) {
      TPerfEvent pe(&fTzero);

      pe.fType = kFileRead;
      pe.fFileName = file->GetName();
      pe.fFileClass = file->ClassName();
      pe.fLen = len;
      pe.fProcTime = proctime;

      fPerfEvent = &pe;
      fTrace->SetBranchAddress("PerfEvents",&fPerfEvent);
      fTrace->Fill();
      fPerfEvent = 0;
   }
}


//______________________________________________________________________________
void TPerfStats::SetBytesRead(Long64_t num)
{
   fBytesRead = num;
}


//______________________________________________________________________________
Long64_t TPerfStats::GetBytesRead() const
{
   return fBytesRead;
}


//______________________________________________________________________________
void TPerfStats::Setup(TList *input)
{
   // Setup the PROOF input list with requested statistics and tracing options.

   if (gEnv->GetValue("Proof.StatsHist", 0)) {
      input->Add(new TNamed("PROOF_StatsHist",""));
   }
   if (gEnv->GetValue("Proof.StatsTrace", 0)) {
      input->Add(new TNamed("PROOF_StatsTrace",""));
   }
}


//______________________________________________________________________________
void TPerfStats::Start(TList *input, TList *output)
{
   // Initialize PROOF statistics run.

   if (gPerfStats != 0) {
      delete gPerfStats;
   }

   Int_t nslaves = 0;
   if (gProofServ->IsMaster()) {
      nslaves = gProofServ->GetProof()->GetParallel();
   }

   gPerfStats = new TPerfStats(nslaves, input, output);

   gPerfStats->SimpleEvent(TVirtualPerfStats::kStart);
}


//______________________________________________________________________________
void TPerfStats::Stop()
{
   // Terminate the PROOF statistics run.

   if (gPerfStats == 0) return;

   gPerfStats->SimpleEvent(TVirtualPerfStats::kStop);

   delete gPerfStats;
   gPerfStats = 0;
}
