// @(#)root/memstat:$Id: TMemStat.cxx 37063 2010-11-29 13:00:55Z brun $
// Author: Anar Manafov (A.Manafov@gsi.de) and Rene Brun  23/09/2010

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

//___________________________________________________________________________
// TMemStat records all the calls to malloc and free and write a TTree
// with the position where the memory is allocated/freed , as well as
// the number of bytes.
//
// To use the class TMemStat, add the following statement at the beginning
// of your script or program
//     TMemStat mm("gnubuiltin");
// or in an interactive session do something like:
//    root > TMemStat mm("gnubuiltin");
//    root > .x somescript.C
//    root > .q
//
// another (may be more practical way) is to modify $ROOTSYS/etc/system.rootrc
// and activate the variable
//    Root.TMemStat:           1
//
// The file collected by TMemStat is named memstat_ProcessID and can be analyzed and results shown
// by executing the static function Show.
// When TMemStat is active it recors every call to malloc/free in a ROOT Tree.
// You must be careful when running jobs with many millions (or more) of calls
// to malloc/free because the generated Tree may become very large.
// The TMemStat constructor TMemStat(const char* system, Int_t buffersize, Int_t maxcalls)
// has its 3 arguments optional:
//   -system refers to the internal algorithm to compute the back traces.
//    the recommended value is "gnubuiltin"
//   -buffersize is the number of calls to malloc or free that can be stored in one memory buffer.
//    when the buffer is full, the calls to malloc/free pointing to the same location
//    are eliminated and not written to the final Tree. The default value 100000
//    is such that between 50 and 90% of the calls are eliminated depending on the application.
//    You can set buffersize <=1 to keep every single call to malloc/free.
//   -maxcalls can set a limit for the maximum number of calls to be registered in the Tree.
//    The default value is 5000000.
// The 3 arguments can be set  in $ROOTSYS/etc/system.rootrc
//    Root.TMemStat.system      gnubuiltin
//    Root.TMemStat.buffersize  100000
//    Root.TMemStat.maxcalls    5000000
//
// TMemStat::Show creates 3 canvases.
// -In canvas1 it displays a dynamic histogram showing for pages (10 kbytes by default)
//  the percentage of the page used.
//  A summary pave shows the total memory still in use when the TMemStat object
//  goes out of scope and the average occupancy of the pages.
//  The average occupancy gives a good indication of the memory fragmentation.
//
// -In canvas2 it displays the histogram of memory leaks in decreasing order.
//  when moving the mouse on this canvas, a tooltip shows the backtrace for the leak
//  in the bin below the mouse.
//
// -In canvas3 it displays the histogram of the nbigleaks largest leaks (default is 20)
//    for each leak, the number of allocs and average alloc size is shown.
//
//
// Simply do:
//   root > TMemStat::Show()
// or specifying arguments
//   root > TMemStat::Show(0.1,20,"mydir/mymemstat.root");
//
// The first argument to Show is the percentage of the time of the original job
// that produced the file after which the display is updated. By default update=0.1,
// ie 10 time intervals will be shown.
// The second argument is nbigleaks.
// The third argument is the imput file name (result of TMemStat).
// If this argument is omitted, Show will take the most recent file
// generated by TMemStat.
//
// You can restrict the address range to be analyzed via TMemStatShow::SetAddressRange
// You can restrict the entry range to be analyzed via TMemStatShow::SetEntryRange
//
//___________________________________________________________________________

#include "TROOT.h"
#include "TDirectory.h"
#include "TMemStat.h"
#include "TMemStatBacktrace.h"
#include "TMemStatMng.h"
#include "TMemStatHelpers.h"

ClassImp(TMemStat)

using namespace std;
using namespace memstat;

_INIT_TOP_STACK;

//______________________________________________________________________________
TMemStat::TMemStat(Option_t* option, Int_t buffersize, Int_t maxcalls): fIsActive(kFALSE)
{
   // Supported options:
   //    "gnubuiltin" - if declared, then MemStat will use gcc build-in function,
   //                      otherwise glibc backtrace will be used
   //
   // Note: Currently MemStat uses a hard-coded output file name (for writing) = "memstat.root";

   // It marks the highest used stack address.
   _GET_CALLER_FRAME_ADDR;

   //preserve context. When exiting will restore the current directory
   TDirectory::TContext context(gDirectory);

   Bool_t useBuiltin = kTRUE;
   // Define string in a scope, so that the deletion of it will be not recorded by YAMS
   {
      string opt(option);
      transform(opt.begin(), opt.end(), opt.begin(),
                memstat::ToLower_t());

      useBuiltin = (opt.find("gnubuiltin") != string::npos) ? kTRUE : kFALSE;
   }

   TMemStatMng::GetInstance()->SetUseGNUBuiltinBacktrace(useBuiltin);
   TMemStatMng::GetInstance()->SetBufferSize(buffersize);
   TMemStatMng::GetInstance()->SetMaxCalls(maxcalls);
   TMemStatMng::GetInstance()->Enable();
   // set this variable only if "NEW" mode is active
   fIsActive = kTRUE;

}

//______________________________________________________________________________
TMemStat::~TMemStat()
{
   //destructor
   if (fIsActive) {
      TMemStatMng::GetInstance()->Disable();
      TMemStatMng::GetInstance()->Close();
   }
}

//______________________________________________________________________________
void TMemStat::Close()
{
   //close the TMemStat manager
   TMemStatMng::Close();
}

//______________________________________________________________________________
void TMemStat::Disable()
{
   //Disable memory statistics
   TMemStatMng::GetInstance()->Disable();
}

//______________________________________________________________________________
void TMemStat::Enable()
{
   //Enable memory statistics
   TMemStatMng::GetInstance()->Enable();
}

//______________________________________________________________________________
void TMemStat::Show(Double_t update, Int_t nbigleaks, const char* fname)
{
   //Show results
   TString action = TString::Format("TMemStatShow::Show(%g,%d,\"%s\");",update,nbigleaks,fname);
   gROOT->ProcessLine(action);
}
