I created a memory leak?

Dear All,

I think I created a memory leak, in any case, I’m sure that I created a mess, so hopefully you can help me :slight_smile:

I’m trying to plot data from several .csv files that contain time-stamped data from different sensors. I would like to select a time window for which I want to plot the data, and since I want to show data from different sensors in one plot that are sometimes in different .csv files, I have a complicated “onePlot” function that tries to extract the right data from the right tree (that I made from the .csv files) from the right time slot.

I create the plots using the “Draw” function from the Tree, because I found that to be the easiest way to get the right data from the right time slot. However, each plot then shows up on the screen, which in principle is no problem, but I fear that through the way I make the “onePlot” I start leaking memory at the moment. And this shows up in the titles of the plots in the following example code:

#include <iostream>
#include <map>
#include <vector>

#include "TCanvas.h"
#include "TPad.h"
#include "TGraph.h"
#include "TMultiGraph.h"
#include "TTree.h"
#include "TList.h"
#include "TDatime.h"
#include "TAxis.h"
#include "TLegend.h"
#include "TStyle.h"
#include "TMath.h"
#include "TLine.h"
#include "TText.h"

// Colors
const int cRed = 632;
const int cOrange = 800;
const int cBlue = 860;
const vector<int> cColorsFive {cRed-7, cOrange+6, cOrange-4, cBlue+6, cBlue-5};
const vector<int> cColorsSix {cOrange-4, cBlue+6, cOrange+6, cBlue+5, cRed-7, cBlue-5};
// const vector<int> cColorsSix {cBlue+6, cRed-7, cBlue+5, cOrange+6, cBlue-5, cOrange-4};
const std::vector<int> cColorsEight {cRed-7, cRed+1, cOrange+6, 0, cOrange-4, cOrange, cBlue+6, 0, cBlue-5, cBlue-6};
//const vector<int> cColorsEightShield {cRed-7, cBlue+6, cRed+1, cBlue+5, cOrange+6, cBlue-5, cOrange-4, cBlue-6};
const vector<int> cColorsEightShield {cBlue+6, cOrange-4, cBlue+5, cOrange+6, cBlue-5, cRed+1, cBlue-6, cRed-7, 0, 0};
const vector<int> cColorsEightThree {cOrange-4, cBlue+6, 16, cOrange+6, cBlue-5, 12, cRed-7, cBlue-6, 0, 0};
const vector<int> cColorsEightSupport {cOrange-4, cBlue+6, cOrange+6, cBlue+5, cRed-7, cBlue-5, cRed+1, cBlue-6,0 ,0};
const std::vector<int> cColorsTwo {cOrange-4, cBlue+6};

const int cLineThickness = 2;

TTree* makeTree (const char* pFileName) 
{
  TTree* myTree = new TTree();
  myTree->ReadFile(Form("%s", pFileName), "", ',');

  TDatime time;
  Int_t dateTime;
  char Date[100];

  TBranch *intTime = myTree->Branch("dateTime", &dateTime);
  myTree->SetBranchAddress("Timestamp (LOCAL_TIME)", &Date[0]);

  for (Long64_t i=0; i<myTree->GetEntries(); i++)
  //for (Long64_t i=0; i<200; i++)
  {
    myTree->GetEntry(i);

    // Get the date and make it into a string, cut into pieces
    std::string sDate(Date);
    time.Set(std::stoi(sDate.substr(0,4)), 
              std::stoi(sDate.substr(5,2)), 
              std::stoi(sDate.substr(8,2)), 
              std::stoi(sDate.substr(11,2)),
              std::stoi(sDate.substr(14,2)),
              std::stoi(sDate.substr(17,2)));

    dateTime = time.Convert();
    intTime->Fill();
  }

  myTree->ResetBranchAddresses(); // Disconnect from local variables
  // myTree->Print();
  return myTree;
}

TGraph* RunningAverageNormalized(TGraph* inputGraph, const int pWindow, const int pNorm)
{
  TGraph* outputGraphtemp = new TGraph();
  TGraph* outputGraph = new TGraph();

  if (pWindow <= 0)
    return inputGraph;

  for (int i=0; i<inputGraph->GetN()-pWindow; i++)
  {
    double averageX = 0;
    double averageY = 0;
    for (int j=0; j<pWindow; j++)
    {
      averageX += inputGraph->GetPointX(i+j);
      averageY += inputGraph->GetPointY(i+j);
    }
    outputGraphtemp->SetPoint(i,averageX/pWindow, averageY/pWindow);
  }

  if (pNorm > 0)
  {
    // double cNorm = TMath::MinElement(outputGraphtemp->GetN(), outputGraphtemp->GetY());
    double cNorm = 0.0;
    for(int i=0; i<50; i++)
      cNorm += outputGraphtemp->GetPointY(i);
    cNorm = cNorm/50;

    for(int i=0; i<outputGraphtemp->GetN(); i++)
      outputGraph->SetPoint(i, outputGraphtemp->GetPointX(i), outputGraphtemp->GetPointY(i)-cNorm);
      // outputGraph->SetPoint(i, outputGraphtemp->GetPointX(i), outputGraphtemp->GetPointY(i)-cNorm+pNorm);
  }
  else
    outputGraph = outputGraphtemp;

  return outputGraph;
}

// *********************** //
// *** One script plot *** //
// *********************** //

void onePlot (double unit, int normalize, int nColumns, bool temperature,
      std::vector<TTree*> pTree,
      std::vector<const char*> pSensorList,
      std::vector<const char*> pLegends,
      const char* pTitles,
      const char* pSaveFileName,
      const vector<int> cColors,
      TDatime pStart, TDatime pStop,
      const double yLow, const double yHigh, const double pWindow,
      std::vector<int> pLines, std::vector<const char*> pLineTitles, bool IVcurve = false)
{
  // Make a nice multigraph
  TMultiGraph *mg = new TMultiGraph();

  std::cout << "\n" << pTitles << std::endl;
  mg->SetTitle(pTitles);

  int count = 0;
  for ( auto it : pSensorList )
  {
    TCanvas* c1 = new TCanvas();

    // Check if we have an empty sensor
    if (it == std::string("NULL"))
    {
        Double_t x[1] = {double(pStart.Convert())};
        Double_t y[1] = {-99.00};
        TGraph *myNullHist = new TGraph(1, x, y);
        mg->Add(myNullHist, ""); myNullHist->SetTitle(" "); myNullHist->SetLineColor(0);
        ++count;
        continue;
    }
    TTree* myTree = NULL;
    for ( auto jt : pTree )
      if (jt->GetBranch(it) != 0) myTree = jt;

    // Draw the data for the selected sensor
    if (temperature)
      myTree->Draw(Form("%s*%f:dateTime", it, unit), Form("dateTime>=%d&&dateTime<=%d&&%s>0", pStart.Convert()-3600, pStop.Convert()+3600, it));
    else if (IVcurve)
      myTree->Draw(Form("Voltage:Current"), Form("dateTime>=%d&&dateTime<=%d", pStart.Convert(), pStop.Convert()));
    else 
      myTree->Draw(Form("%s*%f:dateTime", it, unit), Form("dateTime>=%d&&dateTime<=%d", pStart.Convert()-3600, pStop.Convert()+3600));

    // Access the graphs
    TGraph* myHistTemp = (TGraph*)gPad->GetPrimitive("Graph");
    TGraph* myHist = RunningAverageNormalized(myHistTemp, pWindow, normalize);

    if (IVcurve)
      mg->Add(myHist, "p");
    else
      mg->Add(myHist, "l");

    myHist->SetTitle(pLegends.operator[](count));  
    myHist->SetLineColor(cColors.operator[](count));
    myHist->SetLineWidth(cLineThickness);

    ++count;
    if (IVcurve) break;
  }

  TCanvas *cMult = new TCanvas();
  cMult->SetBottomMargin(0.15);
  gStyle->SetTitleFontSize(0.025);

  mg->GetXaxis()->SetLabelOffset(0.025);
  if (!IVcurve)
  {
    mg->GetXaxis()->SetTitleOffset(1.9);
    mg->GetXaxis()->SetLimits(pStart.Convert(), pStop.Convert());
    mg->GetXaxis()->SetTimeDisplay(1);
    mg->GetXaxis()->SetTimeFormat("#splitline{%d/%m/%y}{   %H:%M}");
    mg->GetXaxis()->SetTimeOffset(0,"local");
  }
  mg->GetXaxis()->SetNdivisions(505);
  mg->SetMinimum(yLow);
  mg->SetMaximum(yHigh);
  mg->GetYaxis()->SetNdivisions(505);
  mg->GetYaxis()->SetLabelOffset(0.01);
  mg->GetYaxis()->SetTitleOffset(1.2);

  mg->Draw("a");

  double cLegendSizeY = cColors.size();
  double cLegendSizeX = 1;
  if (cLegendSizeY > 5) 
  {
    cLegendSizeY = cLegendSizeY/nColumns;
    cLegendSizeX = nColumns;
  }
  double lX1 = 0.89, lX0 = lX1-0.08*cLegendSizeX, lY1 = 0.89, lY0 = lY1-0.03*cLegendSizeY;

  TLegend* l0 = NULL;
  if (!IVcurve)
  {
    l0 = cMult->BuildLegend(lX0, lY0, lX1, lY1, "", "l");
    Float_t symbolMargin = 0.02*cLegendSizeX / (lX1 - lX0);
    l0->SetMargin(symbolMargin);
    l0->SetNColumns(nColumns);
  }

  TLine* myLine = new TLine(0.4, 0.4, 1.4, 1.4);
  myLine->SetLineWidth(cLineThickness);
  myLine->SetLineStyle(2);
  myLine->SetLineColor(12);
  int countL = 0;
  for (auto it : pLines)
  {
    if (it > 0)
    {
      myLine->DrawLine(it, yLow, it, yHigh);
      // auto *myText = new TText(it, (TMath::Abs(yHigh)-TMath::Abs(yLow))/5.+yLow, pLineTitles.operator[](countL));
      auto *myText = new TText(it, yHigh, pLineTitles.operator[](countL));
      myText->SetTextAngle(45);
      myText->SetTextAlign(11);
      myText->SetTextSize(0.02);
      myText->SetTextColor(12);
      myText->Draw();
      ++countL;
    }
  }

  if (!IVcurve)
    l0->Draw();

  cMult->SaveAs(Form("%s_from_%d-%d-%d_%dh%dm%ds-to_%d-%d-%d_%dh%dm%ds.pdf", pSaveFileName,
        pStart.GetYear(), pStart.GetMonth(), pStart.GetDay(), pStart.GetHour(), pStart.GetMinute(), pStart.GetSecond(),
        pStop.GetYear(), pStop.GetMonth(), pStop.GetDay(), pStop.GetHour(), pStop.GetMinute(), pStop.GetSecond()));
}

void coilWindPlots () 
{

  // Cool down
  TDatime cStartDatePlot0(2022, 1, 14, 5, 0, 0);
  TDatime cStopDatePlot0 (2022, 1, 17, 23, 0, 0);  
  // Commissining day 1
  TDatime cStartDatePlot1(2022, 2, 1, 8, 0, 0);
  TDatime cStopDatePlot1 (2022, 2, 1, 18, 0, 0);
  // Commissining day 2
  TDatime cStartDatePlot2(2022, 2, 2, 0, 0, 0);
  TDatime cStopDatePlot2 (2022, 2, 3, 0, 0, 0);
  // Commissining day 3
  TDatime cStartDatePlot3(2022, 2, 3, 0, 0, 0);
  TDatime cStopDatePlot3 (2022, 2, 4, 0, 0, 0);
  // Commissining day 4
  TDatime cStartDatePlot4(2022, 2, 4, 0, 0, 0);
  TDatime cStopDatePlot4 (2022, 2, 4, 13, 0, 0);

  std::vector<TTree*> temperatureTreeVec;
  std::vector<TTree*> voltageTreeVec;

   double unit = 1.0;
  int normalize = -99;
  int nColumns = 3;
  bool temperatureYN = true;
  double cYLow = 30;
  double cYHigh = 70;
  double cWindow =-99;
  std::vector<int> pLines = {-99};
  std::vector<const char*> pLineTitles = {"-99"};

 ////////////// Coil Windings BQDs //////////////

  TTree* coilWindingBQD1Tree = makeTree("./bqd1.csv");  
  TTree* coilWindingBQD2Tree = makeTree("./bqd2.csv");
  TTree* coilWindingBQD3Tree = makeTree("./bqd3.csv");

  voltageTreeVec.push_back(coilWindingBQD1Tree);
  voltageTreeVec.push_back(coilWindingBQD2Tree);
  voltageTreeVec.push_back(coilWindingBQD3Tree);

  std::vector<const char*> cBQD                     {"MSS2aBQD1", "MSS2bBQD1", 
                                                     "MSS2aBQD3", "MSS2bBQD3", 
                                                     "MSS2aBQD2", "MSS2bBQD2"};

  std::vector<const char*> cBQDLegends              {"BQD 1/4 (A)", "BQD 1/4 (B)", 
                                                     "BQD 1/2 (A)", "BQD 1/2 (B)", 
                                                     "BQD 3/4 (A)", "BQD 3/4 (B)"};

  temperatureYN = false;
  cYLow = -30, cYHigh = 30;
  const char* cBQDBalancedTitles = Form("Balanced BQDs;Time [s];Voltage [mV]");
  const char* cWindingsBQDBalancedFile = "./windingsBQDBal";
  onePlot(unit, normalize, nColumns, temperatureYN, voltageTreeVec, cBQD, cBQDLegends, cBQDBalancedTitles, cWindingsBQDBalancedFile, cColorsSix, cStartDatePlot1, cStopDatePlot1, cYLow, cYHigh, cWindow, pLines, pLineTitles, false);
  onePlot(unit, normalize, nColumns, temperatureYN, voltageTreeVec, cBQD, cBQDLegends, cBQDBalancedTitles, cWindingsBQDBalancedFile, cColorsSix, cStartDatePlot2, cStopDatePlot2, cYLow, cYHigh, cWindow, pLines, pLineTitles, false);
  onePlot(unit, normalize, nColumns, temperatureYN, voltageTreeVec, cBQD, cBQDLegends, cBQDBalancedTitles, cWindingsBQDBalancedFile, cColorsSix, cStartDatePlot3, cStopDatePlot3, cYLow, cYHigh, cWindow, pLines, pLineTitles, false);
  onePlot(unit, normalize, nColumns, temperatureYN, voltageTreeVec, cBQD, cBQDLegends, cBQDBalancedTitles, cWindingsBQDBalancedFile, cColorsSix, cStartDatePlot4, cStopDatePlot4, cYLow, cYHigh, cWindow, pLines, pLineTitles, false);

  const char* cBQDBalancedRunnTitles = Form("Balanced BQDs (Runn. av. w = 500);Time [s];Voltage [mV]");
  const char* cWindingsBQDBalancedRunnFile = "./windingsBQDBalanced_runningAverage";
  onePlot(unit, 1, nColumns, temperatureYN, voltageTreeVec, cBQD, cBQDLegends, cBQDBalancedRunnTitles, cWindingsBQDBalancedRunnFile, cColorsSix, cStartDatePlot1, cStopDatePlot1, cYLow, cYHigh, 500, pLines, pLineTitles, false);
  onePlot(unit, 1, nColumns, temperatureYN, voltageTreeVec, cBQD, cBQDLegends, cBQDBalancedRunnTitles, cWindingsBQDBalancedRunnFile, cColorsSix, cStartDatePlot2, cStopDatePlot2, cYLow, cYHigh, 500, pLines, pLineTitles, false);
  onePlot(unit, 1, nColumns, temperatureYN, voltageTreeVec, cBQD, cBQDLegends, cBQDBalancedRunnTitles, cWindingsBQDBalancedRunnFile, cColorsSix, cStartDatePlot3, cStopDatePlot3, cYLow, cYHigh, 500, pLines, pLineTitles, false);
  onePlot(unit, 1, nColumns, temperatureYN, voltageTreeVec, cBQD, cBQDLegends, cBQDBalancedRunnTitles, cWindingsBQDBalancedRunnFile, cColorsSix, cStartDatePlot4, cStopDatePlot4, cYLow, cYHigh, 500, pLines, pLineTitles, false);

}

The problem is that the title of the plots starts to get messed up after the second time I call “onePlot” in the coilWindPlots() function. I print the output of the titles for debugging (and to keep track where the code is) and it looks like this:

root [0]
Processing coilWindPlots.cc+…
Info in TMacOSXSystem::ACLiC: creating shared library /Users/nikkiedeelen/Documents/cernbox/atlas/magnetAnalysisAtlas/ncc012022/./coilWindPlots_cc.so

Balanced BQDs;Time [s];Voltage [mV]
Info in TCanvas::Print: pdf file ./nccPlots/windings/windingsBQDBal_from_2022-2-1_8h0m0s-to_2022-2-1_18h0m0s.pdf has been created

Balanced BQDs;Time [s];Voltage [mV]
Info in TCanvas::Print: pdf file ./nccPlots/windings/windingsBQDBal_from_2022-2-2_0h0m0s-to_2022-2-3_0h0m0s.pdf has been created

_n11
Info in TCanvas::Print: pdf file ./nccPlots/windings/windingsBQDBal_from_2022-2-3_0h0m0s-to_2022-2-4_0h0m0s.pdf has been created

_n20.&gif
Info in TCanvas::Print: pdf file ./nccPlots/windings/windingsBQDBal_from_2022-2-4_0h0m0s-to_2022-2-4_13h0m0s.pdf has been created

Balanced BQDs (Runn. av. w = 500);Time [s];Voltage [mV]
Info in TCanvas::Print: pdf file ./nccPlots/windings/windingsBQDBalanced_runningAverage_from_2022-2-1_8h0m0s-to_2022-2-1_18h0m0s.pdf has been created

Balanced BQDs (Runn. av. w = 500);Time [s];Voltage [mV]
Info in TCanvas::Print: pdf file ./nccPlots/windings/windingsBQDBalanced_runningAverage_from_2022-2-2_0h0m0s-to_2022-2-3_0h0m0s.pdf has been created

1_n38
Info in TCanvas::Print: pdf file ./nccPlots/windings/windingsBQDBalanced_runningAverage_from_2022-2-3_0h0m0s-to_2022-2-4_0h0m0s.pdf has been created

tex
Info in TCanvas::Print: pdf file ./nccPlots/windings/windingsBQDBalanced_runningAverage_from_2022-2-4_0h0m0s-to_2022-2-4_13h0m0s.pdf has been created

The lines where it says “Balanced BQDs (Runn. av. w = 500);Time [s];Voltage [mV]” have the right titles, but below those there are lines with “1_n38” that appears as my plot titles for those plots :slight_smile: or “tex” a little lower that then is my title plot.

This “1_n38” for instance is a canvas, and I have no clue why at some point this becomes my title, after calling the “onePlot()” function 2 times basically. Then I change to a new title, and the same thing happens after 2 times of onePlot().

I know it might be hard to debug this, but is there anyone with a clue where I go wrong, and maybe with some tips to make this process less messy? (less open canvasses, or something like that…)

The data files that I use can be downloaded here (you will need three files):
https://cernbox.cern.ch/index.php/s/eqxXdDNW0FiPQdx
https://cernbox.cern.ch/index.php/s/jG897VbT5CVADTy
https://cernbox.cern.ch/index.php/s/7eEvsozVARsnPZs

Have a nice day,
Nikkie


_ROOT Version: ROOT 6.24/06
_Platform: Built for macosxarm64 on Dec 01 2021, 18:19:00
_Compiler: With Apple clang version 13.0.0 (clang-1300.0.29.3)


Because that’s the title of the multigraph you Draw:

This printout comes form your onPlot function. and that’s the title of your multigraph.

Dear @couet

Thanks, that is indeed true. But the title of the plot is given by a parameter to the function called “pTitles”, and as you can see the pTitles I give to onePlot is:

const char* cBQDBalancedTitles = Form(“Balanced BQDs;Time [s];Voltage [mV]”);

And then I use this cBQDBalancedTitles as input for onePlot after 4 times, because I have 4 different times that I want to plot. For the first two times I indeed get this set of titles, but from the 3rd plot I get this strange title, however, the parameter I use in onePlot stays the same:

Try with:
const char *cBQDBalancedTitles = "Balanced BQDs;Time [s];Voltage [mV]";
const char *cBQDBalancedRunnTitles = "Balanced BQDs (Runn. av. w = 500);Time [s];Voltage [mV]";

1 Like

I can reproduce the issue, however in my case title outputs slightly different:

Balanced BQDs;Time [s];Voltage [mV]
Balanced BQDs;Time [s];Voltage [mV]
ateTime<=1643846400
.&gif

Balanced BQDs (Runn. av. w = 500);Time [s];Voltage [mV]
Balanced BQDs (Runn. av. w = 500);Time [s];Voltage [mV]
1_n38.&ps
n47.&jpg

But the last two titles go completely wrong for some reason.

I noticed that commenting everything inside for ( auto it : pSensorList ) loop at line 130 makes proper titles. So I assume it has something to do with TTree->Draw() ?

Removing Form() fixed it for me

This function is dangerous (the string contents in the static “circular buffer” can be overwritten):
extern char *Form(const char *fmt, ...) // format in circular buffer

Thanks a lot @FoxWise and @Wile_E_Coyote that was indeed the solution.

One more question on this script that I wrote.

Because I use the Draw() function of TTree I get a new TCanvas for each individual plot, which I make myself at the beginning of the pSensorList for loop, but if I don’t make it the program crashes, probably because each time I call Draw in the loop it want to draw the histogram on a canvas, and if it is not there it has troubles overwriting the previous one?

But eventually I want to call the onePlot() function maybe 200 times, and then I get 200*8 canvases.

If I try to delete the canvas at the end of the for loop in which I create it, I get an error. Is there anyway to not have so many canvasses as output, and still use the Draw() function from TTree?

Cheers,
Nikkie

Try:

TGraph *myHistTemp = (TGraph*)gPad->GetPrimitive("Graph")->Clone();
delete c1; // no longer needed

or:

TCanvas *c1 = new TCanvas();
for ( auto it : pSensorList )
{
  c1->Clear();
  // ...
  TGraph *myHistTemp = (TGraph*)gPad->GetPrimitive("Graph")->Clone();
  // ...
}
delete c1; // no longer needed

Thanks! That works very nicely :slight_smile: