The Gaudi Framework  master (37c0b60a)
merge.C
Go to the documentation of this file.
1 /***********************************************************************************\
2 * (c) Copyright 1998-2024 CERN for the benefit of the LHCb and ATLAS collaborations *
3 * *
4 * This software is distributed under the terms of the Apache version 2 licence, *
5 * copied verbatim in the file "LICENSE". *
6 * *
7 * In applying this licence, CERN does not waive the privileges and immunities *
8 * granted to it by virtue of its status as an Intergovernmental Organization *
9 * or submit itself to any jurisdiction. *
10 \***********************************************************************************/
11 #ifndef GAUDIROOTCNV_ROOTDATABASEMERGER_H
12 # define GAUDIROOTCNV_ROOTDATABASEMERGER_H
13 
14 // C++ includes
15 # include <map>
16 # include <memory>
17 # include <string>
18 # include <vector>
19 
20 // Forward declarations
21 class TTree;
22 class TFile;
23 class TBranch;
24 
25 /*
26  * Gaudi namespace declaration
27  */
28 namespace Gaudi {
29 
30  typedef int MergeStatus;
32 
34  int start;
35  int length;
36  };
37 
44 
47 
50  bool m_treeSections = false;
51 
52  union uuid_data {
53  unsigned char buf[16];
54  unsigned short sbuf[8];
55  unsigned int ibuf[4];
56  };
57 
58  public:
62  virtual ~RootDatabaseMerger();
64  bool exists( const std::string& fid ) const;
66  MergeStatus create( const std::string& fid );
68  MergeStatus attach( const std::string& fid );
72  MergeStatus merge( const std::string& fid );
74  void dumpSections();
79 
81  MergeStatus copyAllTrees( TFile* source );
83  MergeStatus copyTree( TFile* source, const std::string& name );
85  MergeStatus copyBranch( TTree* src_tree, TTree* out_tree, const std::string& name );
87  MergeStatus copyRefs( TFile* source, const std::string& name );
89  MergeStatus addSections( TTree* in, TTree* out );
90  };
91 } // End namespace Gaudi
92 
93 #endif // GAUDIROOTCNV_ROOTDATABASEMERGER_H
94 
95 #ifndef GAUDIROOTCNV_ROOTDATABASEMERGER_H
96 # include <RootDatabaseMerger.h>
97 #endif // GAUDIROOTCNV_ROOTDATABASEMERGER_H
98 #include <TBranch.h>
99 #include <TFile.h>
100 #include <TInterpreter.h>
101 #include <TKey.h>
102 #include <TLeaf.h>
103 #include <TROOT.h>
104 #include <TSystem.h>
105 #include <TTree.h>
106 #include <TTreeCloner.h>
107 #include <TUUID.h>
108 #ifdef _WIN32
109 #else
110 # include <libgen.h>
111 #endif
112 
113 using namespace Gaudi;
114 using namespace std;
115 
116 namespace {
117  static bool s_dbg = true;
118 }
119 
122 
125 
127 bool RootDatabaseMerger::exists( const std::string& fid ) const {
128  Bool_t result = gSystem->AccessPathName( fid.c_str(), kFileExists );
129  // if ( s_dbg ) ::printf("File %s %s!\n",fid.c_str(),result == kFALSE ? "EXISTS" : "DOES NOT EXIST");
130  return result == kFALSE;
131 }
132 
134 MergeStatus RootDatabaseMerger::attach( const string& file_id ) {
135  const char* fid = file_id.c_str();
136  if ( m_output ) {
137  ::printf( "+++ Another output file %s is already open. Request denied.\n", m_output->GetName() );
138  return MERGE_ERROR;
139  } else if ( !exists( file_id ) ) {
140  ::printf( "+++ Cannot attach output file %s --- file does not exist.\n", fid );
141  return MERGE_ERROR;
142  }
143  m_output.reset( TFile::Open( fid, "UPDATE" ) );
144  if ( m_output && !m_output->IsZombie() ) {
145  // if ( s_dbg ) ::printf("+++ Update output file %s.\n",fid);
146  // if ( s_dbg ) ::printf("+++ Update output file.\n");
147  return MERGE_SUCCESS;
148  }
149  ::printf( "+++ Failed to open new output file %s.\n", fid );
150  return MERGE_ERROR;
151 }
152 
155  if ( m_output ) {
156  ::printf( "+++ Another output file %s is already open. Request denied.\n", m_output->GetName() );
157  return MERGE_ERROR;
158  } else if ( exists( fid ) ) {
159  ::printf( "+++ Cannot create output file %s --- file already exists.\n", fid.c_str() );
160  return MERGE_ERROR;
161  }
162  m_output.reset( TFile::Open( fid.c_str(), "RECREATE" ) );
163  if ( m_output && !m_output->IsZombie() ) {
164  TTree* t1 = new TTree( "Sections", "Root Section data" );
165  TTree* t2 = new TTree( "Refs", "Root Section data" );
166  if ( t1 && t2 ) {
167  t1->Branch( "Sections", 0, "Sections/C" );
168  t2->Branch( "Links", 0, "Links/C" );
169  t2->Branch( "Params", 0, "Params/C" );
170  t2->Branch( "Databases", 0, "Databases/C" );
171  t2->Branch( "Containers", 0, "Containers/C" );
172  if ( s_dbg ) ::printf( "+++ Opened new output file %s.\n", fid.c_str() );
173  return MERGE_SUCCESS;
174  }
175  }
176  ::printf( "+++ Failed to open new output file %s.\n", fid.c_str() );
177  return MERGE_ERROR;
178 }
179 
182  static const char* fmt = "FID=%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX";
183  if ( m_output ) {
184  TTree* t = (TTree*)m_output->Get( "Refs" );
185  if ( t ) {
186  uuid_data d;
187  char text[256];
188  TUUID uuid;
189  TBranch* b = t->GetBranch( "Params" );
190  if ( b ) {
191  uuid.GetUUID( d.buf );
192  sprintf( text, fmt, d.ibuf[0], d.sbuf[2], d.sbuf[3], d.buf[8], d.buf[9], d.buf[10], d.buf[11], d.buf[12],
193  d.buf[13], d.buf[14], d.buf[15] );
194  b->SetAddress( text );
195  b->Fill();
196  t->Write();
197  if ( s_dbg ) ::printf( "+++ Added new GUID %s to merge file.\n", text );
198  return MERGE_SUCCESS;
199  }
200  }
201  }
202  ::printf( "+++ Failed to add new GUID to merge file.\n" );
203  return MERGE_ERROR;
204 }
205 
208  if ( m_output ) {
209  // if ( s_dbg ) ::printf("+++ Closing merge file: %s.\n",m_output->GetName());
210  if ( s_dbg ) ::printf( "+++ Closing merge file.\n" );
211  m_output->Write();
212  m_output->Purge();
213  if ( s_dbg ) m_output->ls();
214  m_output->Close();
215  m_output.reset();
216  }
217  return MERGE_SUCCESS;
218 }
219 
222  if ( m_output ) {
223  int nb, total = 0, nbytes = 0;
224  char text[1024];
225  TTree* t = (TTree*)m_output->Get( "Sections" );
226  if ( t ) {
227  TBranch* b = t->GetBranch( "Sections" );
228  if ( b ) {
229  b->SetAddress( text );
230  for ( const auto& i : m_sections ) {
231  string cont = i.first;
232  for ( const auto& j : i.second ) {
233  ::sprintf( text, "[CNT=%s][START=%d][LEN=%d]", cont.c_str(), j.start, j.length );
234  nb = t->Fill();
235  ++total;
236  if ( nb > 0 )
237  nbytes += nb;
238  else
239  ::printf( "+++ Failed to update Sections tree with new entries. [WRITE_ERROR]\n" );
240  }
241  }
242  ::sprintf( text, "[END-OF-SECTION]" );
243  nb = t->Fill();
244  ++total;
245  if ( nb > 0 )
246  nbytes += nb;
247  else
248  ::printf( "+++ Failed to update Sections branch with new entries. [WRITE_ERROR]\n" );
249  t->Write();
250  if ( s_dbg ) ::printf( "+++ Added %d Sections entries with %d bytes in total.\n", total, nbytes );
251  return MERGE_SUCCESS;
252  }
253  ::printf( "+++ Failed to update Sections tree with new entries. [NO_OUTPUT_BRANCH]\n" );
254  return MERGE_ERROR;
255  }
256  ::printf( "+++ Failed to update Sections tree with new entries. [NO_OUTPUT_TREE]\n" );
257  return MERGE_ERROR;
258  }
259  ::printf( "+++ Failed to update Sections tree with new entries. [NO_OUTPUT_FILE]\n" );
260  return MERGE_ERROR;
261 }
262 
265  for ( const auto& i : m_sections ) {
266  int cnt = 0;
267  string prefix = i.first;
268  for ( const auto& j : i.second ) {
269  char text[1024];
270  ::sprintf( text, "['%s'][%d]", prefix.c_str(), cnt++ );
271  if ( s_dbg ) {
272  ::printf( "+++ section %-55s Start:%8d ... %8d [%d entries]\n", text, j.start, j.start + j.length, j.length );
273  }
274  }
275  }
276 }
277 
280  if ( m_output ) {
281  std::unique_ptr<TFile> source{ TFile::Open( fid.c_str() ) };
282  if ( source && !source->IsZombie() ) {
283  size_t idx = fid.rfind( '/' );
284  ::printf( "+++ Start merging input file:%s\n",
285  idx != string::npos ? fid.substr( idx + 1 ).c_str() : fid.c_str() );
286  if ( copyAllTrees( source.get() ) == MERGE_SUCCESS ) {
287  if ( copyRefs( source.get(), "Refs" ) == MERGE_SUCCESS ) {
288  source->Close();
289  return MERGE_SUCCESS;
290  }
291  }
292  }
293  ::printf( "+++ Cannot open input file:%s\n", fid.c_str() );
294  m_output->cd();
295  return MERGE_ERROR;
296  }
297  ::printf( "+++ No valid output file present. Merge request refused for fid:%s.\n", fid.c_str() );
298  return MERGE_ERROR;
299 }
300 
303  if ( m_treeSections ) {
305  s.start = (int)( out ? out->GetEntries() : 0 );
306  s.length = (int)in->GetEntries();
307  m_sections[in->GetName()].push_back( s );
308  return MERGE_SUCCESS;
309  }
310 
311  TObjArray* a_in = in->GetListOfBranches();
312  for ( int i = 0, n = a_in->GetLast(); i < n; ++i ) {
313  TBranch* b_in = (TBranch*)a_in->At( i );
314  TBranch* b_out = out ? out->GetBranch( b_in->GetName() ) : 0;
315  if ( !out || b_out ) {
317  s.start = (int)( b_out ? b_out->GetEntries() : 0 );
318  s.length = (int)b_in->GetEntries();
319  m_sections[b_in->GetName()].push_back( s );
320  continue;
321  }
322  ::printf( "+++ Cannot merge incompatible branches:%s.\n", b_in->GetName() );
323  return MERGE_ERROR;
324  }
325  return MERGE_SUCCESS;
326 }
327 
329 MergeStatus RootDatabaseMerger::copyBranch( TTree* src_tree, TTree* out_tree, const string& name ) {
330  char text[4096];
331  TBranch* s = src_tree->GetBranch( name.c_str() );
332  TBranch* o = out_tree->GetBranch( name.c_str() );
333  if ( s && o ) {
334  s->SetAddress( text );
335  o->SetAddress( text );
336  for ( Long64_t i = 0, n = s->GetEntries(); i < n; ++i ) {
337  s->GetEntry( i );
338  o->Fill();
339  }
340  return MERGE_SUCCESS;
341  }
342  return MERGE_ERROR;
343 }
344 
347  TIter nextkey( source->GetListOfKeys() );
348  // m_treeSections = true;
349  for ( TKey* key = (TKey*)nextkey(); key; key = (TKey*)nextkey() ) {
350  const char* classname = key->GetClassName();
351  TClass* cl = gROOT->GetClass( classname );
352  if ( !cl ) continue;
353  if ( cl->InheritsFrom( "TTree" ) ) {
354  string name = key->GetName();
355  if ( name == "Refs" ) continue;
356  m_treeSections = 0 == ::strncmp( key->GetName(), "<local>_", 7 );
357  printf( "+++ Copy Tree:%s %d\n", name.c_str(), int( m_treeSections ) );
358  if ( copyTree( source, name ) != MERGE_SUCCESS ) {
359  m_treeSections = false;
360  return MERGE_ERROR;
361  }
362  }
363  }
364  m_treeSections = false;
365  return MERGE_SUCCESS;
366 }
367 
369 MergeStatus RootDatabaseMerger::copyTree( TFile* source, const string& name ) {
370  TTree* src_tree = (TTree*)source->Get( name.c_str() );
371  if ( src_tree ) {
372  Long64_t src_entries = src_tree->GetEntries();
373  TTree* out_tree = (TTree*)m_output->Get( name.c_str() );
374  m_output->cd();
375  if ( 0 == src_tree->GetEntries() ) { src_tree->SetEntries( 1 ); }
376  addSections( src_tree, out_tree );
377  if ( !out_tree ) {
378  out_tree = src_tree->CloneTree( -1, "fast" );
379  if ( s_dbg ) ::printf( "+++ Created new Tree %s.\n", out_tree->GetName() );
380  out_tree->Write();
381  delete out_tree;
382  return MERGE_SUCCESS;
383  }
384  m_output->GetObject( name.c_str(), out_tree );
385  TTreeCloner cloner( src_tree, out_tree, "fast" );
386  if ( cloner.IsValid() ) {
387  Long64_t out_entries = out_tree->GetEntries();
388  out_tree->SetEntries( out_entries + src_entries );
389  Bool_t res = cloner.Exec();
390  if ( s_dbg ) ::printf( "+++ Merged tree: %s res=%d\n", out_tree->GetName(), res );
391  out_tree->Write();
392  delete out_tree;
393  return MERGE_SUCCESS;
394  } else {
395  // Fast cloning is not possible for this input TTree.
396  // ... see TTree::CloneTree for example of recovery code ...
397  ::printf( "+++ Got a tree where fast cloning is not possible -- operation failed.\n" );
398  return MERGE_ERROR;
399  }
400 #if 0
401  Long64_t nb = out_tree->CopyEntries(src_tree,-1,"fast");
402  Long64_t out_entries = out_tree->GetEntries();
403  out_tree->SetEntries(out_entries+src_entries);
404  out_tree->Write();
405  if ( s_dbg ) ::printf("+++ Merged tree: %s res=%lld\n",out_tree->GetName(),nb);
406  return MERGE_SUCCESS;
407 #endif
408  }
409  return MERGE_ERROR;
410 }
411 
413 MergeStatus RootDatabaseMerger::copyRefs( TFile* source, const string& name ) {
414  TTree* src_tree = (TTree*)source->Get( name.c_str() );
415  if ( src_tree ) {
416  TTree* out_tree = (TTree*)m_output->Get( name.c_str() );
417  if ( out_tree ) {
418  addSections( src_tree, out_tree );
419  copyBranch( src_tree, out_tree, "Links" );
420  copyBranch( src_tree, out_tree, "Params" );
421  copyBranch( src_tree, out_tree, "Containers" );
422  copyBranch( src_tree, out_tree, "Databases" );
423  out_tree->Write();
424  return MERGE_SUCCESS;
425  }
426  }
427  return MERGE_ERROR;
428 }
429 
430 int merge( const char* target, const char* source, bool fixup = false, bool dbg = true ) {
431  s_dbg = dbg;
432  // s_dbg = true;
433 #if 0
434  static bool first = true;
435  if ( first ) {
436  first = false;
437  gSystem->Load("libCintex");
438  gInterpreter->ProcessLine("Cintex::Enable()");
439  //gSystem->Load("libGaudiKernelDict");
440  //gSystem->Load("libGaudiTestSuiteDict");
441  }
442 #endif
443  // printf("+++ Target:%s\n+++ Source file:%s Fixup:%s Dbg:%s\n",
444  // target,source, fixup ? "YES" : "NO",s_dbg ? "YES" : "NO");
446  MergeStatus ret = m.exists( target ) ? m.attach( target ) : m.create( target );
447  if ( ret == MERGE_SUCCESS ) {
448  ret = m.merge( source );
449  if ( ret == MERGE_SUCCESS ) {
450  m.dumpSections();
451  if ( fixup ) m.createFID();
452  m.saveSections();
453  return m.close();
454  }
455  }
456  ::printf( "+++ Cannot open output file:%s\n", target );
457  return 0;
458 }
Gaudi::RootDatabaseMerger::attach
MergeStatus attach(const std::string &fid)
Attach to existing output file for further merging.
Definition: merge.C:134
std::string
STL class.
Gaudi::RootDatabaseMerger::uuid_data::ibuf
unsigned int ibuf[4]
Definition: merge.C:55
Gaudi::ContainerSection
Definition: merge.C:33
Gaudi::MERGE_ERROR
@ MERGE_ERROR
Definition: merge.C:31
Gaudi::RootDatabaseMerger::uuid_data
Definition: merge.C:52
Gaudi::RootDatabaseMerger::copyRefs
MergeStatus copyRefs(TFile *source, const std::string &name)
Copy one single tree from the input file to the output file.
Definition: merge.C:413
gaudirun.s
string s
Definition: gaudirun.py:346
Gaudi::RootDatabaseMerger::m_treeSections
bool m_treeSections
Definition: merge.C:50
std::vector
STL class.
std::string::length
T length(T... args)
gaudirun.prefix
string prefix
Definition: gaudirun.py:361
Gaudi::RootDatabaseMerger::uuid_data::buf
unsigned char buf[16]
Definition: merge.C:53
Gaudi::RootDatabaseMerger::ContainerSections
std::vector< ContainerSection > ContainerSections
Definition: merge.C:45
Gaudi::RootDatabaseMerger::uuid_data::sbuf
unsigned short sbuf[8]
Definition: merge.C:54
Gaudi::RootDatabaseMerger::~RootDatabaseMerger
virtual ~RootDatabaseMerger()
Default destructor.
Definition: merge.C:124
Gaudi::MERGE_SUCCESS
@ MERGE_SUCCESS
Definition: merge.C:31
Gaudi::RootDatabaseMerger::copyBranch
MergeStatus copyBranch(TTree *src_tree, TTree *out_tree, const std::string &name)
Copy single reference branch.
Definition: merge.C:329
bug_34121.t
t
Definition: bug_34121.py:31
Gaudi::MergeStatus
int MergeStatus
Definition: merge.C:30
compareOutputFiles.target
target
Definition: compareOutputFiles.py:489
std::sprintf
T sprintf(T... args)
Gaudi::Units::m
constexpr double m
Definition: SystemOfUnits.h:108
ProduceConsume.j
j
Definition: ProduceConsume.py:104
Gaudi::RootDatabaseMerger::addSections
MergeStatus addSections(TTree *in, TTree *out)
Add section information for the next merge step.
Definition: merge.C:302
TemplatedAlg
Definition: TemplatedAlg.cpp:22
std::string::c_str
T c_str(T... args)
Gaudi::MergeStatusEnum
MergeStatusEnum
Definition: merge.C:31
std::map< std::string, ContainerSections >
Gaudi
This file provides a Grammar for the type Gaudi::Accumulators::Axis It allows to use that type from p...
Definition: __init__.py:1
cpluginsvc.n
n
Definition: cpluginsvc.py:234
Gaudi::ContainerSection::start
int start
Definition: merge.C:34
Gaudi::RootDatabaseMerger::exists
bool exists(const std::string &fid) const
Check if a database exists.
Definition: merge.C:127
Gaudi::RootDatabaseMerger::create
MergeStatus create(const std::string &fid)
Create new output file.
Definition: merge.C:154
Gaudi::ContainerSection::length
int length
Definition: merge.C:35
std::string::substr
T substr(T... args)
Gaudi::RootDatabaseMerger::dumpSections
void dumpSections()
Dump collected database sections.
Definition: merge.C:264
Gaudi::RootDatabaseMerger::DatabaseSections
std::map< std::string, ContainerSections > DatabaseSections
Definition: merge.C:46
merge
int merge(const char *target, const char *source, bool fixup=false, bool dbg=true)
Definition: merge.C:430
ConditionsStallTest.name
name
Definition: ConditionsStallTest.py:77
Gaudi::RootDatabaseMerger::saveSections
MergeStatus saveSections()
Save new sections to the output file.
Definition: merge.C:221
std
STL namespace.
Gaudi::RootDatabaseMerger::m_sections
DatabaseSections m_sections
Definition: merge.C:48
fmt
Gaudi::RootDatabaseMerger::copyTree
MergeStatus copyTree(TFile *source, const std::string &name)
Copy one single tree from the input file to the output file.
Definition: merge.C:369
Gaudi::RootDatabaseMerger::m_output
std::unique_ptr< TFile > m_output
Definition: merge.C:49
Gaudi::RootDatabaseMerger::copyAllTrees
MergeStatus copyAllTrees(TFile *source)
Copy all data trees from the input file to the output file.
Definition: merge.C:346
Gaudi::RootDatabaseMerger::merge
MergeStatus merge(const std::string &fid)
Merge new input to existing output.
Definition: merge.C:279
Gaudi::RootDatabaseMerger::createFID
MergeStatus createFID()
Create and add new FID to the newly merged file.
Definition: merge.C:181
Gaudi::RootDatabaseMerger::close
MergeStatus close()
Close output file.
Definition: merge.C:207
std::unique_ptr< TFile >
Gaudi::RootDatabaseMerger::RootDatabaseMerger
RootDatabaseMerger()
Standard constructor.
ProduceConsume.key
key
Definition: ProduceConsume.py:84
std::string::rfind
T rfind(T... args)
Gaudi::RootDatabaseMerger
Definition: merge.C:43
PrepareBase.out
out
Definition: PrepareBase.py:20