Skip to content

Commit fd2f191

Browse files
committed
AOD: File Inspector
1 parent 8751877 commit fd2f191

File tree

2 files changed

+294
-3
lines changed

2 files changed

+294
-3
lines changed

Framework/AODMerger/CMakeLists.txt

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,23 @@
1212
o2_add_executable(merger
1313
COMPONENT_NAME aod
1414
SOURCES src/aodMerger.cxx
15-
PUBLIC_LINK_LIBRARIES ROOT::Core ROOT::Net)
15+
PUBLIC_LINK_LIBRARIES ROOT::Core ROOT::Net)
1616

1717
o2_add_executable(thinner
1818
COMPONENT_NAME aod
1919
SOURCES src/aodThinner.cxx
20-
PUBLIC_LINK_LIBRARIES ROOT::Core ROOT::Net)
20+
PUBLIC_LINK_LIBRARIES ROOT::Core ROOT::Net)
2121

2222
o2_add_executable(strainer
2323
COMPONENT_NAME aod
2424
SOURCES src/aodStrainer.cxx
25-
PUBLIC_LINK_LIBRARIES ROOT::Core ROOT::Net)
25+
PUBLIC_LINK_LIBRARIES ROOT::Core ROOT::Net)
26+
27+
o2_add_executable(inspector
28+
COMPONENT_NAME aod
29+
SOURCES src/aodInspector.cxx
30+
PUBLIC_LINK_LIBRARIES ROOT::Core ROOT::Net
31+
Boost::program_options
32+
O2::Version
33+
O2::CommonUtils
34+
O2::FrameworkLogger)
Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
#include <RtypesCore.h>
2+
#include <TKey.h>
3+
#include "O2Version.h"
4+
#include "Framework/Logger.h"
5+
#include "CommonUtils/StringUtils.h"
6+
7+
#include "TFile.h"
8+
#include "TBranch.h"
9+
#include "TTree.h"
10+
#include "TGrid.h"
11+
#include "TMap.h"
12+
#include "TObjString.h"
13+
#include "TStopwatch.h"
14+
15+
#include <boost/program_options.hpp>
16+
17+
#include <filesystem>
18+
#include <memory>
19+
#include <vector>
20+
#include <string>
21+
#include <fstream>
22+
#include <unordered_map>
23+
24+
namespace bpo = boost::program_options;
25+
namespace fs = std::filesystem;
26+
27+
using FilePtr = std::unique_ptr<TFile>;
28+
29+
// Description of possible exit codes
30+
enum ExitCodes : int {
31+
Success = 0, // there is only one success code
32+
Fail = 1, // non-descriptive general fail
33+
ArgParse = 2, // failed cli arg parse
34+
NonExistantFile = 3, // the input file does not exists
35+
InputList = 3, // input file list not opened
36+
};
37+
38+
// Collected info for a specific table
39+
struct TableInfo {
40+
Long64_t uncBytes{0};
41+
Long64_t compBytes{0};
42+
Long64_t seen{0};
43+
};
44+
using TableInfoMap = std::unordered_map<std::string, TableInfo>;
45+
void printTableInfoMap(const TableInfoMap& info);
46+
void printMetaData(const TMap* meta);
47+
bool checkFileExists(const std::string& fileName);
48+
FilePtr openFile(const std::string& fileName);
49+
void testFile(const std::string& file, TableInfoMap& info);
50+
void testDataFrame(TDirectory* dir, TableInfoMap& info);
51+
void testTree(TTree* tree, TableInfo& table);
52+
void testBranch(TBranch* bra, TableInfoMap& info);
53+
54+
bool initOptionsAndParse(bpo::options_description& options, int argc, char* argv[], bpo::variables_map& vm)
55+
{
56+
options.add_options()(
57+
"help,h", "Program description")(
58+
"input,i", bpo::value<std::string>()->default_value("AO2D.root"), "Single AO2D file or text file with path to files per line");
59+
try {
60+
bpo::store(bpo::parse_command_line(argc, argv, options), vm);
61+
bpo::notify(vm);
62+
} catch (const bpo::error& e) {
63+
LOGP(error, "Exception during command-line argument parsing: '{}'", e.what());
64+
LOG(info) << options;
65+
return false;
66+
}
67+
return true;
68+
}
69+
70+
int main(int argc, char* argv[])
71+
{
72+
LOGP(info, "AO2D Inspector (Version '{}'; git '{}'; Info '{}')", o2::fullVersion(), o2::gitRevision(), o2::getBuildInfo());
73+
bpo::options_description options("Allowed options");
74+
bpo::variables_map vm;
75+
if (!initOptionsAndParse(options, argc, argv, vm)) {
76+
return ExitCodes::ArgParse;
77+
}
78+
79+
if (vm.count("help") != 0u) { // first thing check if help requested
80+
LOG(info) << options;
81+
return ExitCodes::Success;
82+
}
83+
84+
auto inputFile = vm["input"].as<std::string>();
85+
if (!checkFileExists(inputFile)) {
86+
return ExitCodes::NonExistantFile;
87+
}
88+
89+
TStopwatch watch;
90+
watch.Start();
91+
92+
// built list of files to open eventually
93+
std::vector<std::string> fileNames;
94+
if (inputFile.ends_with(".root")) {
95+
fileNames.emplace_back(inputFile);
96+
} else { // treat as input list
97+
std::ifstream file(inputFile);
98+
if (!file.is_open()) {
99+
LOGP(error, "Cannot open input list '{}'", inputFile);
100+
return ExitCodes::InputList;
101+
}
102+
std::string line;
103+
while (std::getline(file, line)) {
104+
o2::utils::Str::trim(line);
105+
if (line.empty() || line.starts_with('\n')) {
106+
continue;
107+
}
108+
fileNames.emplace_back(line);
109+
}
110+
}
111+
LOGP(info, "Prepared {} files for parsing ({:.2f} s)", fileNames.size(), watch.RealTime());
112+
113+
watch.Start(true);
114+
TableInfoMap info;
115+
for (const auto& file : fileNames) {
116+
testFile(file, info);
117+
}
118+
LOGP(info, "Testing all files finished ({:.2f} s)", watch.RealTime());
119+
printTableInfoMap(info);
120+
121+
return ExitCodes::Success;
122+
}
123+
124+
bool checkFileExists(const std::string& fileName)
125+
{
126+
auto b = o2::utils::Str::pathExists(fileName);
127+
if (!b) {
128+
LOGP(error, "File '{}' does not exist!", fileName);
129+
}
130+
return b;
131+
}
132+
133+
FilePtr openFile(const std::string& fileName)
134+
{
135+
if (!checkFileExists(fileName)) {
136+
return nullptr;
137+
}
138+
139+
std::string realFile;
140+
if (fileName.compare(0, 8, "alien://") == 0) {
141+
if (!gGrid && !TGrid::Connect("alien://")) {
142+
LOGP(error, "Failed to initialize alien for {}", fileName);
143+
return nullptr;
144+
}
145+
realFile = fileName;
146+
} else {
147+
realFile = o2::utils::Str::getFullPath(fileName);
148+
}
149+
150+
FilePtr file = std::unique_ptr<TFile>(TFile::Open(realFile.c_str(), "READ"));
151+
if (file == nullptr || !file->IsOpen() || file->IsZombie()) {
152+
LOGP(error, "Failed to open file '{}' (from '{}')", realFile, fileName);
153+
return nullptr;
154+
}
155+
return file;
156+
}
157+
158+
void testFile(const std::string& fileName, TableInfoMap& info)
159+
{
160+
auto file = openFile(fileName);
161+
if (!file) {
162+
return;
163+
}
164+
LOGP(info, "Size of {} is {}", fileName, file->GetSize());
165+
auto keys = file->GetListOfKeys();
166+
keys->Sort();
167+
for (auto okey : *keys) {
168+
auto key = dynamic_cast<TKey*>(okey);
169+
if (key == nullptr) {
170+
continue;
171+
}
172+
TString name = key->GetName();
173+
TString keyClassName = key->GetClassName();
174+
175+
if (keyClassName == "TMap" && name == "metaData") {
176+
auto meta = file->Get<TMap>("metaData");
177+
if (meta != nullptr) {
178+
printMetaData(meta);
179+
}
180+
}
181+
182+
if (keyClassName != "TDirectoryFile") {
183+
continue;
184+
}
185+
186+
if (!name.BeginsWith("DF_")) {
187+
continue;
188+
}
189+
190+
auto df = file->Get<TDirectoryFile>(name);
191+
if (df != nullptr) {
192+
testDataFrame(df, info);
193+
}
194+
}
195+
}
196+
197+
void testDataFrame(TDirectory* dir, TableInfoMap& info)
198+
{
199+
auto keys = dir->GetListOfKeys();
200+
keys->Sort();
201+
for (auto okey : *keys) {
202+
auto key = dynamic_cast<TKey*>(okey);
203+
if (key == nullptr) {
204+
continue;
205+
}
206+
207+
if (std::string(key->GetClassName()) != "TTree") {
208+
continue;
209+
}
210+
211+
const auto name = key->GetName();
212+
if (name[0] != 'O' || name[1] != '2') {
213+
continue;
214+
}
215+
if (info.find(name) == info.end()) {
216+
info.emplace(name, TableInfo());
217+
}
218+
TableInfo& table = info[name];
219+
auto tree = dir->Get<TTree>(name);
220+
if (tree != nullptr) {
221+
testTree(tree, table);
222+
}
223+
}
224+
}
225+
226+
void testTree(TTree* tree, TableInfo& table)
227+
{
228+
table.seen++;
229+
table.compBytes += tree->GetZipBytes();
230+
table.uncBytes += tree->GetTotBytes();
231+
}
232+
233+
void testBranch(TBranch* bra, TableInfoMap& info) {}
234+
235+
void printTableInfoMap(const TableInfoMap& tableInfoMap)
236+
{
237+
constexpr int width = 20;
238+
constexpr double mb = 1. / (1024. * 1024.);
239+
240+
std::array<std::string, 4> header{"Name", "UncBytes", "CompBytes", "Seen"};
241+
std::string sep;
242+
for (int i{0}; i < header.size(); ++i) {
243+
sep += "+" + std::string(width + 2, '-');
244+
}
245+
sep += "+";
246+
247+
LOGP(info, "{}", sep);
248+
LOGP(info, "| {:<{}} | {:>{}} | {:>{}} | {:>{}} |", header[0], width, header[1], width, header[2], width, header[3], width);
249+
LOGP(info, "{}", sep);
250+
251+
Long64_t totUncBytes{0}, totCompBytes{0}, totSeen{0};
252+
253+
for (const auto& [name, table] : tableInfoMap) {
254+
LOGP(info, "| {:<{}} | {:>{}} | {:>{}} | {:>{}} |", name, width, table.uncBytes * mb, width, table.compBytes * mb, width, table.seen, width);
255+
256+
totUncBytes += table.uncBytes;
257+
totCompBytes += table.compBytes;
258+
totSeen += table.seen;
259+
}
260+
LOGP(info, "{}", sep);
261+
LOGP(info, "| {:<{}} | {:>{}} | {:>{}} | {:>{}} |", "Total", width, totUncBytes * mb, width, totCompBytes * mb, width, totSeen, width);
262+
LOGP(info, "{}", sep);
263+
}
264+
265+
void printMetaData(const TMap* meta)
266+
{
267+
LOGP(info, "Found MetaData with:");
268+
auto iter = meta->MakeIterator();
269+
TObject* key{nullptr};
270+
while ((key = iter->Next())) {
271+
auto value = meta->GetValue(key);
272+
auto keyStr = dynamic_cast<TObjString*>(key);
273+
auto valueStr = dynamic_cast<TObjString*>(value);
274+
if (keyStr && valueStr) {
275+
LOGP(info, " - {}: {}", keyStr->GetName(), valueStr->GetName());
276+
} else {
277+
LOGP(info, " - {}: {}", keyStr->GetName(), (value ? value->GetName() : "null"));
278+
}
279+
}
280+
281+
delete iter;
282+
}

0 commit comments

Comments
 (0)