Manual schema evolution when adding a data member leads to a crash when reading an old file

ROOT Version: 6.28/04
Platform: Ubuntu 20.04.6
Compiler: conda-forge gcc 12.3.0-0


I’ll try to attach a MWE (I’m not allowed here…)

I wrote a TTree with a class Foo that has only 1 data member Int_t fA to a file. When writting the file I had ClassDef(Foo, 1) in the class definition. Then, I updated the class, adding Int_t fB as a member and increasing the ClassDef to 2. If I now try to read the file (or browse it with TBrowser), I get some default value for fB and everything works. But, if I modify the linkdef and add:

#pragma read sourceClass = "Foo" source = "Int_t fB" version = "[1]" targetClass = "Foo" target = "fB" code = "{ fB = 0;}"

browsing or reading the file and accessingfB leads to a crash.

Am I using the rule properly?

In this example, having a specific rule for adding a member is an overkill. Still, for larger classes that might change again in the future, I think it would be useful to be able to specify exactly how an older file should be read.

I’ve tested and I get the same behavior on CC7, ROOT v6.22.0 and gcc 8.3.0.

Here’s the code needed to generate the “older” file:
Foo.h

#ifndef FOO_H
#define FOO_H

#include "TObject.h"

class Foo : public TObject{
public:
  Foo();
  Foo(int val);
  virtual ~Foo() {};
  const Int_t GetA() const {return fA;}
  void SetA(Int_t val) {fA = val;} 
private:
  Int_t fA;

  ClassDef(Foo, 1);
};

#endif

Foo.cc

#include "Foo.h"
ClassImp(Foo);
Foo::Foo():fA(0){}
Foo::Foo(int val) : fA(val) {}

main.cc

#include "Foo.h"
#include "TFile.h"
#include "TTree.h"
#include "TRandom3.h"
#include <iostream>

void write(){
  TFile outputFile("output.root", "RECREATE");
  TTree *outputTree = new TTree("tree", "tree");

  Foo *p_foo = new Foo();
  outputTree->Branch("FooBranch", "Foo", &p_foo,32000,99);

  TRandom3 rndm(42);
  for (Int_t i = 0; i < 10; ++i){
    p_foo->SetA(rndm.Integer(10));
    outputTree->Fill();
  }

  outputTree->Write();
  outputFile.Close();

}

void read(){
  TFile inputFile("output_v1.root", "READ");
  TTree *inputTree = static_cast<TTree*>(inputFile.FindObjectAny("tree"));

  Foo *p_foo = nullptr;
  inputTree->SetBranchAddress("FooBranch", &p_foo);

  Long64_t nEntries = inputTree->GetEntries();
  for (Long64_t iE = 0; iE < nEntries; ++iE){
    inputTree->GetEntry(iE);
    std::cout << p_foo->GetA() /*<< " " << p_foo->GetB()*/ << std::endl;
  }

}

int main(int argc, char **argv){

  if (argc == 1){
    write();
  }
  else{
    read();
  }
  return 0;
}

FooLinkDef.h

#ifdef __CINT__

#pragma link off all globals;
#pragma link off all classes;
#pragma link off all functions;

#pragma link C++ class Foo + ;

//#pragma read sourceClass = "Foo" source = "Int_t fB" version = "[1]" targetClass = "Foo" target = "fB" code = "{ fB = 0;}"

#endif

CMakeLists.txt

cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
project(SchemaEvol)
find_package(ROOT REQUIRED RIO)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
add_library(Foo SHARED Foo.cc)
ROOT_GENERATE_DICTIONARY(G__Foo Foo.h MODULE Foo LINKDEF FooLinkDef.h)
add_executable(main main.cc)
target_link_libraries(main PUBLIC ${ROOT_LIBRARIES} Foo)

The problem appears when the class and the linkdef are modified to this
Foo.h

#ifndef FOO_H
#define FOO_H

#include "TObject.h"

class Foo : public TObject{
public:
  Foo();
  Foo(int val);
  virtual ~Foo() {};

  const Int_t GetA() const {return fA;}
  void SetA(Int_t val) {fA = val;} 

  const Int_t GetB() const {return fB;}
  void SetB(Int_t val) {fB = val;} 

private:
  Int_t fA;
  Int_t fB;

  ClassDef(Foo, 2);
};

#endif

Foo.cc

#include "Foo.h"
ClassImp(Foo);
Foo::Foo():fA(0), fB(0){}
Foo::Foo(int val) : fA(val), fB(0) {}

FooLinkDef.h

#ifdef __CINT__

#pragma link off all globals;
#pragma link off all classes;
#pragma link off all functions;

#pragma link C++ class Foo + ;

#pragma read sourceClass = "Foo" source = "Int_t fB" version = "[1]" targetClass = "Foo" target = "fB" code = "{ fB = 0;}"

#endif

Hi @sghinesc,

First of all, welcome to the ROOT forum! I took a quick look at your code and, apparently, there is nothing wrong in it. @pcanal any ideas?

Also, could you share any information about the crash?

Cheers,
J.

Hi @jalopezg ! Thanks for the quick reply!

Here’s the stack trace when trying to read the old file with the new version of the class:

*** Break *** segmentation violation



===========================================================
There was a crash.
This is the entire stack trace of all threads:
===========================================================
#0  0x00007f85ea61fc3a in __GI___wait4 (pid=10914, stat_loc=stat_loc
entry=0x7fff01fc1fe8, options=options
entry=0, usage=usage
entry=0x0) at ../sysdeps/unix/sysv/linux/wait4.c:27
#1  0x00007f85ea61fbfb in __GI___waitpid (pid=<optimized out>, stat_loc=stat_loc
entry=0x7fff01fc1fe8, options=options
entry=0) at waitpid.c:38
#2  0x00007f85ea58ef67 in do_system (line=<optimized out>) at ../sysdeps/posix/system.c:172
#3  0x00007f85eb37f9fb in TUnixSystem::StackTrace() () from /home/stefan/miniconda3/envs/science/lib/libCore.so.6.28
#4  0x00007f85eb37f366 in TUnixSystem::DispatchSignals(ESignals) () from /home/stefan/miniconda3/envs/science/lib/libCore.so.6.28
#5  <signal handler called>
#6  0x00007f85eb323847 in TClass::GetRealData(char const*) const () from /home/stefan/miniconda3/envs/science/lib/libCore.so.6.28
#7  0x00007f85eb324a32 in TClass::GetDataMemberOffset(char const*) const () from /home/stefan/miniconda3/envs/science/lib/libCore.so.6.28
#8  0x00007f85ea95851e in ROOT::read_Foo_0(char*, TVirtualObject*) () from /home/stefan/Personal/ROOT_SchemaEvolution_bug/libFoo.so
#9  0x00007f85eaf50dc2 in int TStreamerInfo::ReadBufferArtificial<char**>(TBuffer&, char** const&, TStreamerElement*, int, int) () from /home/stefan/miniconda3/envs/science/lib/libRIO.so.6.28
#10 0x00007f85eb014910 in int TStreamerInfo::ReadBuffer<char**>(TBuffer&, char** const&, TStreamerInfo::TCompInfo* const*, int, int, int, int, int) () from /home/stefan/miniconda3/envs/science/lib/libRIO.so.6.28
#11 0x00007f85eae8be01 in TStreamerInfoActions::GenericReadAction(TBuffer&, void*, TStreamerInfoActions::TConfiguration const*) () from /home/stefan/miniconda3/envs/science/lib/libRIO.so.6.28
#12 0x00007f85eadaa135 in TBufferFile::ApplySequence(TStreamerInfoActions::TActionSequence const&, void*) () from /home/stefan/miniconda3/envs/science/lib/libRIO.so.6.28
#13 0x00007f85eac5ab87 in TBranchElement::GetEntry(long long, int) () from /home/stefan/miniconda3/envs/science/lib/libTree.so.6.28
#14 0x00007f85eacc1478 in TTree::GetEntry(long long, int) () from /home/stefan/miniconda3/envs/science/lib/libTree.so.6.28
#15 0x000055a6ea1ff4b4 in read() ()
#16 0x000055a6ea1ff0ef in main ()
===========================================================


The lines below might hint at the cause of the crash. If you see question
marks as part of the stack trace, try to recompile with debugging information
enabled and export CLING_DEBUG=1 environment variable before running.
You may get help by asking at the ROOT forum https://root.cern/forum
Only if you are really convinced it is a bug in ROOT then please submit a
report at https://root.cern/bugs Please post the ENTIRE stack trace
from above as an attachment in addition to anything else
that might help us fixing this issue.
===========================================================
#6  0x00007f85eb323847 in TClass::GetRealData(char const*) const () from /home/stefan/miniconda3/envs/science/lib/libCore.so.6.28
#7  0x00007f85eb324a32 in TClass::GetDataMemberOffset(char const*) const () from /home/stefan/miniconda3/envs/science/lib/libCore.so.6.28
#8  0x00007f85ea95851e in ROOT::read_Foo_0(char*, TVirtualObject*) () from /home/stefan/Personal/ROOT_SchemaEvolution_bug/libFoo.so
#9  0x00007f85eaf50dc2 in int TStreamerInfo::ReadBufferArtificial<char**>(TBuffer&, char** const&, TStreamerElement*, int, int) () from /home/stefan/miniconda3/envs/science/lib/libRIO.so.6.28
#10 0x00007f85eb014910 in int TStreamerInfo::ReadBuffer<char**>(TBuffer&, char** const&, TStreamerInfo::TCompInfo* const*, int, int, int, int, int) () from /home/stefan/miniconda3/envs/science/lib/libRIO.so.6.28
#11 0x00007f85eae8be01 in TStreamerInfoActions::GenericReadAction(TBuffer&, void*, TStreamerInfoActions::TConfiguration const*) () from /home/stefan/miniconda3/envs/science/lib/libRIO.so.6.28
#12 0x00007f85eadaa135 in TBufferFile::ApplySequence(TStreamerInfoActions::TActionSequence const&, void*) () from /home/stefan/miniconda3/envs/science/lib/libRIO.so.6.28
#13 0x00007f85eac5ab87 in TBranchElement::GetEntry(long long, int) () from /home/stefan/miniconda3/envs/science/lib/libTree.so.6.28
#14 0x00007f85eacc1478 in TTree::GetEntry(long long, int) () from /home/stefan/miniconda3/envs/science/lib/libTree.so.6.28
#15 0x000055a6ea1ff4b4 in read() ()
#16 0x000055a6ea1ff0ef in main ()
===========================================================

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.