file_info_dumper.cc
Go to the documentation of this file.
5 #include "boost/program_options.hpp"
10 
11 #include "TError.h"
12 #include "TFile.h"
13 
14 extern "C" {
15 #include "sqlite3.h"
16 }
17 
18 #include <algorithm>
19 #include <bitset>
20 #include <iostream>
21 #include <ostream>
22 #include <sstream>
23 #include <string>
24 
25 namespace bpo = boost::program_options;
26 
27 using namespace std::string_literals;
29 using std::ostream;
30 using std::string;
31 using std::vector;
32 using stringvec = vector<string>;
33 
34 int print_process_history(InfoDumperInputFile const& file, ostream& output);
36  ostream& output,
37  bool compact);
38 int print_event_list(InfoDumperInputFile const& file, ostream& output);
39 int print_file_index(InfoDumperInputFile const& file, ostream& output);
40 int print_branchIDLists(InfoDumperInputFile const& file, ostream& output);
41 int db_to_file(InfoDumperInputFile const& file,
42  ostream& output,
43  ostream& errors);
44 
45 namespace {
46 
47  void
48  RootErrorHandler(int const level,
49  bool const die,
50  char const* location,
51  char const* message)
52  {
53  // Ignore dictionary errors.
54  if (level == kWarning && (!die) &&
55  strcmp(location, "TClass::TClass") == 0 &&
56  std::string(message).find("no dictionary") != std::string::npos) {
57  return;
58  } else {
59  // Default behavior
60  DefaultErrorHandler(level, die, location, message);
61  }
62  }
63 
64  // Code taken from the SQLite webpage:
65  // https://www.sqlite.org/backup.html
66  // With modifications.
67  //
68  // SQLite comments:
69  //
70  // This function is used to load the contents of a database file on disk
71  // into the "main" database of open database connection pInMemory, or
72  // to save the current contents of the database opened by pInMemory into
73  // a database file on disk. pInMemory is probably an in-memory database,
74  // but this function will also work fine if it is not.
75  //
76  // Parameter zFilename points to a nul-terminated string containing the
77  // name of the database file on disk to load from or save to. If parameter
78  // isSave is non-zero, then the contents of the file zFilename are
79  // overwritten with the contents of the database opened by pInMemory. If
80  // parameter isSave is zero, then the contents of the database opened by
81  // pInMemory are replaced by data loaded from the file zFilename.
82  //
83  // If the operation is successful, SQLITE_OK is returned. Otherwise, if
84  // an error occurs, an SQLite error code is returned.
85 
86  int
87  dbToFile(sqlite3* pInMemory, const char* zFilename)
88  {
89  int rc{0}; // Function return code
90  sqlite3* pFile{nullptr}; // Database connection opened on zFilename
91  sqlite3_backup* pBackup{nullptr}; // Backup object used to copy data
92 
93  // Open the database file identified by zFilename. Exit early if this fails
94  // for any reason.
95  rc = sqlite3_open(zFilename, &pFile);
96  if (rc == SQLITE_OK) {
97 
98  // Set up the backup procedure to copy from the "main" database of
99  // connection pFile to the main database of connection pInMemory.
100  // If something goes wrong, pBackup will be set to NULL and an error
101  // code and message left in connection pTo.
102 
103  // If the backup object is successfully created, call backup_step()
104  // to copy data from pFile to pInMemory. Then call backup_finish()
105  // to release resources associated with the pBackup object. If an
106  // error occurred, then an error code and message will be left in
107  // connection pTo. If no error occurred, then the error code belonging
108  // to pTo is set to SQLITE_OK.
109 
110  pBackup = sqlite3_backup_init(pFile, "main", pInMemory, "main");
111  if (pBackup != nullptr) {
112  (void)sqlite3_backup_step(pBackup, -1);
113  (void)sqlite3_backup_finish(pBackup);
114  }
115  rc = sqlite3_errcode(pFile);
116  }
117 
118  // Close the database connection opened on database file zFilename
119  // and return the result of this function.
120  (void)sqlite3_close(pFile);
121  return rc;
122  }
123 
124 } // namespace
125 
126 int
127 main(int argc, char* argv[]) try {
128  // ------------------
129  // use the boost command line option processing library to help out
130  // with command line options
131  std::ostringstream descstr;
132  descstr << argv[0] << " <options> [<source-file>]+";
133 
134  bpo::options_description desc{descstr.str()};
135  desc.add_options()("help,h", "produce help message")(
136  "full-path", "print full path of file name")(
137  "event-list", "print event-list for each input file")(
138  "file-index", "prints FileIndex object for each input file")(
139  "process-history",
140  "prints list of processes that produced this file (output given in "
141  "chronological order)")(
142  "range-of-validity",
143  bpo::value<string>()->implicit_value("full"),
144  "prints range of validity for each input file. Allowed values are\n"
145  " \"full\" (default)\n"
146  " \"compact\"")("branch-ids,B",
147  "prints BranchID lists stored in the file")(
148  "db-to-file",
149  ("Writes RootFileDB to external SQLite database with the same base name as the input file and the suffix '.db'.\n"s +
150  "(Writes to directory in which '"s + argv[0] + "' is executed)."s)
151  .c_str())(
152  "source,s", bpo::value<stringvec>(), "source data file (multiple OK)");
153 
154  bpo::options_description all_opts{"All Options"};
155  all_opts.add(desc);
156 
157  // Each non-option argument is interpreted as the name of a files to
158  // be processed. Any number of filenames is allowed.
159  bpo::positional_options_description pd;
160  pd.add("source", -1);
161  // The variables_map contains the actual program options.
162  bpo::variables_map vm;
163  try {
164  bpo::store(bpo::command_line_parser(argc, argv)
165  .options(all_opts)
166  .positional(pd)
167  .run(),
168  vm);
169  bpo::notify(vm);
170  }
171  catch (bpo::error const& e) {
172  std::cerr << "Exception from command line processing in " << argv[0] << ": "
173  << e.what() << "\n";
174  return 2;
175  }
176 
177  if (vm.count("help")) {
178  std::cout << desc << std::endl;
179  return 1;
180  }
181 
182  // Get the names of the files we will process.
183  stringvec file_names;
184  size_t const file_count{vm.count("source")};
185  if (file_count < 1) {
186  std::cerr << "One or more input files must be specified;"
187  << " supply filenames as program arguments\n"
188  << "For usage and options list, please do '" << argv[0]
189  << " --help'.\n";
190  return 3;
191  }
192  file_names.reserve(file_count);
193  cet::copy_all(vm["source"].as<stringvec>(), std::back_inserter(file_names));
194 
195  enum options_t {
196  PrintProcessHistory = 0,
197  PrintRangeSetsFull,
198  PrintRangeSetsCompact,
199  PrintEventList,
200  PrintFileIndex,
201  SaveDBtoFile,
202  FullPath,
203  PrintBranchIDLists,
204  NumOptions
205  };
206 
207  std::bitset<NumOptions> options;
208  options[PrintProcessHistory] = vm.count("process-history") > 0;
209  if (vm.count("range-of-validity") > 0) {
210  auto const& rov_value = vm["range-of-validity"].as<std::string>();
211  if (rov_value == "full") {
212  options.set(PrintRangeSetsFull);
213  } else if (rov_value == "compact") {
214  options.set(PrintRangeSetsCompact);
215  } else {
216  std::cerr
217  << "Incorrect argument value supplied for the 'range-of-validity'\n"
218  << "program option. Allowed values are:\n"
219  << " \"full\" (default)\n"
220  << " \"compact\"\n";
221  return 4;
222  }
223  }
224  options[PrintEventList] = vm.count("event-list") > 0;
225  options[PrintFileIndex] = vm.count("file-index") > 0;
226  options[SaveDBtoFile] = vm.count("db-to-file") > 0;
227  options[FullPath] = vm.count("full-path") > 0;
228  options[PrintBranchIDLists] = vm.count("branch-ids") > 0;
229 
230  if (options.none()) {
231  std::cerr << "No options were specified for processing input files.\n"
232  << "For usage and options list, please do '" << argv[0]
233  << " --help'.\n";
234  return 5;
235  }
236 
237  if (options.test(PrintEventList) && options.test(PrintFileIndex)) {
238  std::cerr
239  << "The --event-list and --file-index options are mutually exclusive.\n";
240  return 6;
241  }
242 
243  SetErrorHandler(RootErrorHandler);
244  tkeyvfs_init();
245 
246  ostream& output = std::cout;
247  ostream& errors = std::cerr;
248 
249  int rc{0};
250  for (auto const& fn : file_names) {
251  auto const& printed_name =
252  options.test(FullPath) ? fn : fn.substr(fn.find_last_of('/') + 1ul);
253  output << cet::HorizontalRule{30}('=') << '\n'
254  << "File: " << printed_name << '\n';
255  InfoDumperInputFile const file{fn};
256  if (options.test(PrintProcessHistory))
257  rc += print_process_history(file, output);
258  if (options.test(PrintRangeSetsFull))
259  rc += print_range_sets(file, output, false);
260  if (options.test(PrintRangeSetsCompact))
261  rc += print_range_sets(file, output, true);
262  if (options.test(PrintFileIndex))
263  rc += print_file_index(file, output);
264  if (options.test(PrintEventList))
265  rc += print_event_list(file, output);
266  if (options.test(SaveDBtoFile))
267  rc += db_to_file(file, output, errors);
268  if (options.test(PrintBranchIDLists))
269  rc += print_branchIDLists(file, output);
270  output << '\n';
271  }
272  return rc;
273 }
274 catch (cet::exception const& e) {
275  std::cerr << e.what() << '\n';
276  return 7;
277 }
278 catch (...) {
279  std::cerr << "Exception thrown for the last processed file. Please remove "
280  "it from the file list.\n";
281  return 8;
282 }
283 
284 //============================================================================
285 
286 int
287 print_process_history(InfoDumperInputFile const& file, ostream& output)
288 {
289  file.print_process_history(output);
290  return 0;
291 }
292 
293 int
295  ostream& output,
296  bool const compactRanges)
297 {
298  file.print_range_sets(output, compactRanges);
299  return 0;
300 }
301 
302 int
303 print_event_list(InfoDumperInputFile const& file, ostream& output)
304 {
305  file.print_event_list(output);
306  return 0;
307 }
308 
309 int
310 print_file_index(InfoDumperInputFile const& file, ostream& output)
311 {
312  file.print_file_index(output);
313  return 0;
314 }
315 
316 int
317 print_branchIDLists(InfoDumperInputFile const& file, ostream& output)
318 {
319  file.print_branchIDLists(output);
320  return 0;
321 }
322 
323 int
324 db_to_file(InfoDumperInputFile const& file, ostream& output, ostream& errors)
325 {
326  TFile* current_file = file.tfile();
327  std::string const& rootFileName = current_file->GetName();
328 
329  // db file name has the same base as the input art/ROOT file
330  std::string::size_type const dist{rootFileName.find(".root") -
331  rootFileName.find_last_of('/')};
332  std::string const base{
333  rootFileName.substr(rootFileName.find_last_of('/') + 1, dist)};
334  std::string const extFileName{base + "db"};
335 
336  art::SQLite3Wrapper db{current_file, "RootFileDB"};
337  int const rc{dbToFile(db, extFileName.c_str())};
338  if (rc == 0) {
339  output << "\nRootFileDB from file \"" << current_file->GetName() << "\"\n"
340  << "saved to external database file \"" << extFileName << "\".\n";
341  } else {
342  errors << "\nCould not save RootFileDB from file \""
343  << current_file->GetName() << "\"\n"
344  << "to external database file.\n";
345  }
346  return rc;
347 }
int main(int argc, char *argv[])
void print_event_list(std::ostream &) const
std::string string
Definition: nybbler.cc:12
int print_range_sets(InfoDumperInputFile const &file, ostream &output, bool compact)
int print_file_index(InfoDumperInputFile const &file, ostream &output)
void RootErrorHandler(int level, bool die, char const *location, char const *message)
std::vector< std::string > stringvec
auto vector(Vector const &v)
Returns a manipulator which will print the specified array.
Definition: DumpUtils.h:265
int print_branchIDLists(InfoDumperInputFile const &file, ostream &output)
error
Definition: includer.cc:31
const double e
int print_event_list(InfoDumperInputFile const &file, ostream &output)
int db_to_file(InfoDumperInputFile const &file, ostream &output, ostream &errors)
int tkeyvfs_init(void)
Definition: tkeyvfs.cc:1768
int print_process_history(InfoDumperInputFile const &file, ostream &output)
void print_process_history(std::ostream &) const
auto copy_all(FwdCont &, FwdIter)
void print_range_sets(std::ostream &, bool compactRanges) const
static const double s
Definition: Units.h:99
void print_file_index(std::ostream &) const
void print_branchIDLists(std::ostream &os) const
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33
unsigned int run