Some parameters cannot be set as global variable

I need to merge the data from some [.root] files and [.txt] files into one [.root] file.
So I set the parameters of the output file as global variable.

It works well when there are only [.txt] files as follows:

// #include
TString rootfilename;
TFile* file_raw;
TTree* tree_raw;
Int_t ev_num;
TDatime* datime;
Double_t (*waves)[1024];

void read_a_txt(TString filefullpath)
{/*code*/}
void read_a_root(TString filefullpath)
{/* same structure as read_a_txt()*/}
void test()
{
	rootfilename = "test.root";
	datime = new TDatime;
	waves = new Double_t[8][1024]{};
	file_raw = new TFile(rootfilename, "recreate");
	tree_raw = new TTree("raw", "raw data of the experiment");
	tree_raw->SetDirectory(file_raw);
	tree_raw->Branch("ev", &ev_num, "ev/I");
	tree_raw->Branch("datime", &datime);
	tree_raw->Branch("waves", waves, "waves[8][1024]/D");

	read_a_txt("1.txt");
	read_a_txt("2.txt");

	file_raw->Write();
	delete datime;
	delete[] waves;
	delete tree_raw;
	delete file_raw;
}

But the program will break down when I call read_a_root()

I have found the problem and the following code works well but I don’t know why

  1. If I define Double_t (*waves)[1024] as global variable, the program will break down when running tree_old->GetEntry(i);
  2. If I define TString rootfilename as global variable, the program will produce the output file correctly but output a message: munmap_chunk(): invalid pointer
// #include

//global variable
TFile* file_raw;
TTree* tree_raw;
Int_t ev_num;
TDatime* datime;

void test()
{
TString rootfilename;
Double_t (*waves)[1024];

	rootfilename = "test.root";
	datime = new TDatime;
	waves = new Double_t[8][1024]{};
	file_raw = new TFile(rootfilename, "recreate");
	tree_raw = new TTree("raw", "raw data of the experiment");
	tree_raw->SetDirectory(file_raw);
	tree_raw->Branch("ev", &ev_num, "ev/I");
	tree_raw->Branch("datime", &datime);
	tree_raw->Branch("waves", waves, "waves[8][1024]/D");

//the code in read_a_root()
...
TTree* tree_old = file_old->Get<TTree>("raw");
...
for (Long64_t i = 0; i < n_event; i++){
	tree_old->GetEntry(i);
	...
}
//the code in read_a_root()


	file_raw->Write();
	delete datime;
	delete[] waves;
	delete tree_raw;
	delete file_raw;
}

Besides, the program works well in DEBUG mode of VSCODE. Just like I have encountered last month
So I guess there are memory errors. But as the second program I showed, there is just one function which is the main function. All that determine whether the code breaks is where the pointer is defined.
It also confuses me that even the position of a simple TString will affect the program.

_ROOT Version:6.26.10
_Platform:wsl2
Compiler: Not Provided


Hi,

Thanks for the post. I do not see anything which seems to point to a ROOT issue, but rather a potential issue to be debugged in your code.
My suggestion would be to trim down the code until the issue becomes easily reproducible and debuggable.

Cheers,
Danilo

Thanks for your suggestion. I will try to work it out myself.
But I still think the second question is something related ROOT

If I define TString rootfilename as global variable, the program will produce the output file correctly but output a message: munmap_chunk(): invalid pointer

I am not completely sure. The message hints to a memory corruption, which is independent from the implementation of the string class.

Best,
D

It also confuses me that even the position of a simple TString will affect the program.

This is a cristal clear indication that the problem is either an out-of-bound memory read or write. The presence of the additional local variable changes the location other variable and their content.

As an example, if there is a memory over-write that change the content of the TString then the destructor of the TString might end up trying to delete a ‘random’ memory address instead of what it allocated.

The ordering is a problem. You need to either detach the user memory from the TTree before deleting it:

tree_raw->ResetBranchAddresses();
delete datime;
delete[] waves;

or simply delete them after the TTree:

	delete tree_raw; // At this point the TTree still have references to the deleted memory
	delete file_raw;
	delete datime;
	delete[] waves;

If the read_a_root fails with memory errors the code in the first ellipsis is most likely to contain the issue.

Thank you for your guidance! I used to think that I should delete TTree before deleting the variables.

Well, this is weird.

tree_raw->ResetBranchAddresses();

This solved the problem for the test program. But didn’t work for the original program.
And after rebooting my computer, I commented this line as follows in the test program to reproduce it.

// tree_raw->ResetBranchAddresses();
delete datime;
delete[ ] waves;
delete tree_raw;
delete file_raw;

But the program still works! I’m quite sure the same code would break in the terminal yesterday. I think it is out of my capacity to figure it out.
Anyway, for nonprofessional users like me: I have found another way to solve the problem: put the whole array in STATIC rather than HEAP, i.e., don’t use new/delete. Just define the array as a global variable, it will be stored in STATIC, which has a capacity of 2GB, much larger than STACK(~MB).

The behavior is undefined … this includes both executing a segmentation fault or … seemingly working :wink:

But didn’t work for the original program.

This may indicates that there may or may not be “other” memory errors. I recommend running your program using valgrind:

valgrind --suppressions=$ROOTSYS/etc/valgrind-root.supp myexecutable myarguments

to see if any other memory errors are present.

valgrind showed that there are ~900,000 bytes of possibly lost in my program.
So I simplified the program again and again to reduce it.
Finally I got:

#include <iostream>
#include <fstream>
#include <stream>
using namespace std;
#include "TROOT.h"
#include "TSystem.h"
#include "TStyle.h"
#include "TString.h"
#include "TFile.h"
#include "TTree.h"
#include "TCanvas.h"
#include "TDatime.h"
void assemble_data()
{
	TString rootfilename = "20240410_20240424_raw2.root";
	TFile file_raw_data(rootfilename, "recreate");
	TTree tree_raw_data("raw", "raw data of the experiment");
}

int main()
{
	assemble_data();
	return 0;
}

And it still reports

==12199== HEAP SUMMARY:
==12199==     in use at exit: 33,288,806 bytes in 17,545 blocks
==12199==   total heap usage: 141,508 allocs, 123,963 frees, 135,447,555 bytes allocated
==12199==
==12199== LEAK SUMMARY:
==12199==    definitely lost: 0 bytes in 0 blocks
==12199==    indirectly lost: 0 bytes in 0 blocks
==12199==      possibly lost: 270,548 bytes in 259 blocks
==12199==    still reachable: 820,544 bytes in 7,045 blocks
==12199==                       of which reachable via heuristic:
==12199==                         multipleinheritance: 14,584 bytes in 2 blocks
==12199==         suppressed: 32,197,714 bytes in 10,241 blocks
==12199== Rerun with --leak-check=full to see details of leaked memory
==12199==
==12199== For lists of detected and suppressed errors, rerun with: -s
==12199== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 208 from 48)

Then I change it back to the original version.
And found the problem disappeared again!!! !!! Even if I put the array on the HEAP which would break yesterday.
:confounded: :confounded: :confounded:

Is this with the ROOT suppressions file?

Either way, the leak would not be the source of the problem. If valgrind was able to detect the issue it would have reported it before the heap summary.