    // @(#)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;
    }