ATLAS Offline Software
Loading...
Searching...
No Matches
TrigOpMonitor.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
3*/
4
5#include "TrigOpMonitor.h"
6
13#include "GaudiKernel/DirSearchPath.h"
14#include "GaudiKernel/IIncidentSvc.h"
15#include "Gaudi/Interfaces/IOptionsSvc.h"
16#include "eformat/DetectorMask.h"
17#include "eformat/SourceIdentifier.h"
18
19#include <algorithm>
20#include <fstream>
21#include <list>
22
23// Helpers
24namespace {
26 std::string folder2HistName(const std::string& folder)
27 {
28 std::string s(folder);
29 if (s.front() == '/') s.erase(0, 1);
30 std::replace(s.begin(), s.end(), '/', '_');
31 return s;
32 }
33
35 StatusCode readReleaseData(const std::string& file, std::map<std::string, std::string>& result)
36 {
37 std::ifstream f;
38 f.open(file.c_str(), std::ifstream::in);
39 if (!f.is_open()) return StatusCode::FAILURE;
40
41 std::string line;
42 while (getline(f, line)) {
43 // Format: name:value
44 std::string::size_type loc = line.find(':');
45 if (loc == std::string::npos || loc == 0) continue;
46 result[line.substr(0, loc)] = line.substr(loc + 1);
47 }
48 f.close();
49 return StatusCode::SUCCESS;
50 }
51
52} // namespace
53
54TrigOpMonitor::TrigOpMonitor(const std::string& name, ISvcLocator* pSvcLocator) :
55 base_class(name, pSvcLocator),
56 m_histPath("/EXPERT/HLTFramework/" + name + "/")
57{}
58
60{
61 ATH_CHECK(m_histSvc.retrieve());
64
65 ATH_CHECK( m_incidentSvc.retrieve() );
67
68 return StatusCode::SUCCESS;
69}
70
71void TrigOpMonitor::handle( const Incident& incident ) {
72 // One time fills after fork
73 if (incident.type() == AthenaInterprocess::UpdateAfterFork::type()) {
74 if (!m_IOVDbSvc) m_IOVDbSvc = service("IOVDbSvc", /*createIf=*/false);
77 const AthenaInterprocess::UpdateAfterFork& updinc = dynamic_cast<const AthenaInterprocess::UpdateAfterFork&>(incident);
79 }
80}
81
83{
84 m_previousLB = 0;
85 if (!m_IOVDbSvc) m_IOVDbSvc = service("IOVDbSvc", /*createIf=*/false);
87
89 return StatusCode::SUCCESS;
90}
91
92StatusCode TrigOpMonitor::execute(const EventContext& ctx)
93{
94 /* One-time fills */
95 [[maybe_unused]] const static bool once = [&]() {
97 return true;
98 }();
99
100 /* Per-LB fills */
101 if (m_previousLB != ctx.eventID().lumi_block()) { // New LB
102 m_previousLB = ctx.eventID().lumi_block();
103
104 // Fill IOV diff histogram
106
107 // Fill lumi histogram
108 fillLumiHist(ctx);
109 }
110
111 return StatusCode::SUCCESS;
112}
113
115{
116 m_iovChangeHist = nullptr;
117 m_currentIOVs.clear();
118 m_folderHist.clear();
119
120 m_iovChangeHist = new TH2I("CoolFolderUpdate_LB", "COOL folder updates;Lumiblock;", 1, 0, 1, 1, 0, 1);
121
122 // create histogram for magnetic field information
123 m_magFieldHist = new TH2I("MagneticFieldSettings", "Magnetic field settings", 2, 0, 2, 2, 0, 2);
124 m_magFieldHist->GetYaxis()->SetBinLabel(1, "off");
125 m_magFieldHist->GetYaxis()->SetBinLabel(2, "on");
126 m_magFieldHist->GetXaxis()->SetBinLabel(1, "Solenoid");
127 m_magFieldHist->GetXaxis()->SetBinLabel(2, "Toroid");
128
129 m_releaseHist = new TH1I("GeneralOpInfo", "General operational info;;Applications", 1, 0, 1);
130
131 m_mtConfigHist = new TH2I("MTConfig", "Multi-threading configuration", 2, 0, 2, 10, 0, 10);
132 m_mtConfigHist->SetCanExtend(TH1::kYaxis);
133 m_mtConfigHist->GetXaxis()->SetBinLabel(1, "N threads");
134 m_mtConfigHist->GetXaxis()->SetBinLabel(2, "N slots");
135
136 m_workersHist = new TH1I("MPWorkers", "Worker IDs;Worker ID;Number of workers", 10, 0, 10);
137 m_workersHist->SetCanExtend(TH1::kXaxis);
138
139 m_subdetHist = new TH2I("Subdetectors", "State of subdetectors", 1, 0, 1, 3, 0, 3);
140 m_subdetHist->SetCanExtend(TH1::kXaxis);
141 m_subdetHist->GetYaxis()->SetBinLabel(1, "# ROB");
142 m_subdetHist->GetYaxis()->SetBinLabel(2, "off");
143 m_subdetHist->GetYaxis()->SetBinLabel(3, "on");
144
145 m_lumiHist = new TProfile("Luminosity", "Luminosity;Lumiblock;Luminosity [10^{33} cm^{-2}s^{-1}]",
146 m_maxLB, 0, m_maxLB);
147
148 m_muHist = new TProfile("Pileup", "Pileup;Lumiblock;Interactions per BX", m_maxLB, 0, m_maxLB);
149
150 // Register histograms
151 TH1* hist[] = {
154 };
155 for (TH1* h : hist) {
156 if (h) ATH_CHECK(m_histSvc->regHist(m_histPath + h->GetName(), h));
157 }
158
159 return StatusCode::SUCCESS;
160}
161
163{
164 if (!m_fieldMapKey.empty()) {
166 const MagField::AtlasFieldMap* fieldMap = fieldMapHandle->fieldMap();
167 // if there is no field map, the field is off
168 m_magFieldHist->Fill("Solenoid", fieldMap && fieldMap->solenoidOn() ? "on" : "off", 1.0);
169 m_magFieldHist->Fill("Toroid", fieldMap && fieldMap->toroidOn() ? "on" : "off", 1.0);
170 }
171}
172
174{
175 // create and fill histogram for IOVDb entries
176 std::vector<std::string> keyList;
177 if (m_IOVDbSvc) keyList = m_IOVDbSvc->getKeyList();
178
179 // set up histograms
180 TH2I* IOVDbRunHist = new TH2I("IOVDbRunRange", "IOVDb Run Range", 1, 0, 1, 1, 0, 1);
181 TH2I* IOVDbTimeHist = new TH2I("IOVDbTimeRange", "IOVDb Time Range", 1, 0, 1, 1, 0, 1);
182 TProfile* IOVDbBytesReadHist = new TProfile("IOVDbBytesRead", "Folder data read", 1, 0, 1);
183 IOVDbBytesReadHist->SetYTitle("Data read [bytes]");
184 TProfile* IOVDbReadTimeHist = new TProfile("IOVDbReadTime", "Folder update time", 1, 0, 1);
185 IOVDbReadTimeHist->SetYTitle("Update time [ms]");
186
187 // fill histograms
189 for (const std::string& key : keyList) {
190 // cppcheck-suppress nullPointerRedundantCheck
191 if (m_IOVDbSvc->getKeyInfo(key, info) && info.retrieved) {
192
193 m_currentIOVs[key] = info.range;
194
195 IOVTime start(info.range.start());
196 IOVTime stop(info.range.stop());
197
198 // fill start condition (run number)
199 if (start.isRunEvent()) {
200 IOVDbRunHist->Fill(std::to_string(start.run()).c_str(), info.folderName.c_str(), 1.0);
201 }
202
203 // fill start condition (timestamp)
204 if (start.isTimestamp()) {
205 IOVDbTimeHist->Fill(std::to_string(start.timestamp()).c_str(), info.folderName.c_str(), 1.0);
206 }
207
208 // fill stop condition (run number)
209 if (stop.isRunEvent()) {
210 if (stop.run() == IOVTime::MAXRUN) {
211 IOVDbRunHist->Fill("infinity", info.folderName.c_str(), 1.0);
212 }
213 else {
214 IOVDbRunHist->Fill(std::to_string(stop.run()).c_str(), info.folderName.c_str(), 1.0);
215 }
216 }
217
218 // fill stop condition (timestamp)
219 if (stop.isTimestamp()) {
220 if (stop.timestamp() == IOVTime::MAXTIMESTAMP) {
221 IOVDbTimeHist->Fill("infinity", info.folderName.c_str(), 1.0);
222 }
223 else {
224 IOVDbTimeHist->Fill(std::to_string(stop.timestamp()).c_str(), info.folderName.c_str(), 1.0);
225 }
226 }
227
228 // fill read bytes and time read
229 IOVDbBytesReadHist->Fill(info.folderName.c_str(), info.bytesRead, 1.0);
230 IOVDbReadTimeHist->Fill(info.folderName.c_str(), info.readTime * 1000, 1.0);
231 }
232 }
233
234 // final configuration of histograms
235 IOVDbRunHist->LabelsDeflate("X");
236 IOVDbRunHist->LabelsDeflate("Y");
237 IOVDbRunHist->GetYaxis()->LabelsOption("a");
238
239 IOVDbTimeHist->LabelsDeflate("X");
240 IOVDbTimeHist->LabelsDeflate("Y");
241 IOVDbTimeHist->GetYaxis()->LabelsOption("a");
242
243 IOVDbBytesReadHist->LabelsDeflate("X");
244 IOVDbReadTimeHist->LabelsDeflate("X");
245
246 // Register histograms
247 TH1* hist[] = {IOVDbRunHist, IOVDbTimeHist, IOVDbBytesReadHist, IOVDbReadTimeHist};
248 for (TH1* h : hist) {
249 m_histSvc->regHist(m_histPath + h->GetName(), h).ignore();
250 }
251}
252
253void TrigOpMonitor::fillIOVDbChangeHist(const EventContext& ctx)
254{
255 std::vector<std::string> keys;
256 if (m_IOVDbSvc) keys = m_IOVDbSvc->getKeyList();
257
258 // Loop over all keys known to IOVDbSvc
260 for (const std::string& k : keys) {
261 // cppcheck-suppress nullPointerRedundantCheck
262 if (not m_IOVDbSvc->getKeyInfo(k, info)) continue;
263 if (not info.retrieved) continue;
264
265 const auto curIOV = m_currentIOVs.find(k);
266 if (curIOV == m_currentIOVs.end()) {
267 m_currentIOVs[k] = info.range;
268 continue;
269 }
270
271 // Print IOV changes and fill histogram
272 if (info.range != curIOV->second) {
273 ATH_MSG_INFO("IOV of " << k << " changed from " << curIOV->second << " to " << info.range
274 << " on event: " << ctx.eventID());
275
276 if (m_iovChangeHist) {
277 // Perform a locked fill and remove any empty bins to allow correct gathering
279 m_iovChangeHist->Fill(std::to_string(ctx.eventID().lumi_block()).c_str(), info.folderName.c_str(),
280 1);
281 m_iovChangeHist->LabelsDeflate("X");
282 m_iovChangeHist->LabelsDeflate("Y");
283 }
284
285 // Create detailed histograms per changed folder
286 if (m_detailedHists) {
287 auto fh = m_folderHist.find(info.folderName);
288 if (fh == m_folderHist.end()) {
289 std::string name(folder2HistName(info.folderName));
290
291 fh = m_folderHist.insert({info.folderName, FolderHist()}).first;
292 fh->second.h_time = new TH1F((name + "_ReadTime").c_str(),
293 ("Update time for " + info.folderName + ";Time [ms];Entries").c_str(),
294 100, 0, 200);
295
296 fh->second.h_bytes = new TH1F(
297 (name + "_BytesRead").c_str(),
298 ("Bytes read for " + info.folderName + ";Data [bytes];Entries").c_str(), 100, 0, 1000);
299
300 for (TH1* h : {fh->second.h_time, fh->second.h_bytes}) {
301 m_histSvc->regHist(m_histPath + h->GetName(), h).ignore();
302 }
303 }
304
305 // Need to plot the difference because IOVDbSvc reports total time and bytes for entire job
306 fh->second.h_time->Fill(info.readTime * 1000 - m_folderHist[info.folderName].total_time);
307 fh->second.total_time += info.readTime * 1000;
308
309 fh->second.h_bytes->Fill(info.bytesRead - m_folderHist[info.folderName].total_bytes);
310 fh->second.total_bytes += info.bytesRead;
311 }
312
313 m_currentIOVs[k] = info.range;
314 }
315 }
316}
317
318void TrigOpMonitor::fillLumiHist(const EventContext& ctx)
319{
320 if (!m_lumiDataKey.empty()) {
322 m_lumiHist->Fill(ctx.eventID().lumi_block(), lumiData->lbAverageLuminosity());
323 m_muHist->Fill(ctx.eventID().lumi_block(), lumiData->lbAverageInteractionsPerCrossing());
324 }
325}
326
328{
329 const char* ld_lib_path = getenv("LD_LIBRARY_PATH");
330 if (ld_lib_path == nullptr) {
331 ATH_MSG_WARNING("LD_LIBRARY_PATH is not defined. Will not fill release histogram.");
332 return;
333 }
334
335 // Find all release metadata files
336 std::list<DirSearchPath::path> file_list = DirSearchPath(ld_lib_path, ":").find_all(m_releaseData.value());
337
338 if (file_list.empty()) {
339 ATH_MSG_WARNING("Could not find release metadata file " << m_releaseData
340 << " in LD_LIBRARY_PATH");
341 m_releaseHist->Fill("Release ?", 1);
342 return;
343 }
344
345 for (const auto& f : file_list) {
346 // Read metadata file
347 std::map<std::string, std::string> result;
348 if (readReleaseData(f.string(), result).isFailure()) {
349 ATH_MSG_WARNING("Could not read release metadata from " << f);
350 m_releaseHist->Fill("Release ?", 1);
351 return;
352 }
353
354 // Check format
355 if (result.find("project name") == result.end() || result.find("release") == result.end()) {
356 ATH_MSG_WARNING("Invalid release metadata format in " << f);
357 m_releaseHist->Fill("Release ?", 1);
358 return;
359 }
360
361 m_releaseHist->Fill((result["project name"] + " " + result["release"]).c_str(), 1);
362 }
363}
364
365void TrigOpMonitor::fillProcThreadHist(const int workerID)
366{
367 m_workersHist->Fill(workerID, 1);
368
369 auto getIntProp = [this](std::string_view name, const std::string& prop) -> std::optional<int> {
370 IProperty* svc = serviceLocator()->service(name).as<IProperty>();
371 if (svc == nullptr) return std::nullopt;
372 try {
373 return std::stoi(svc->getProperty(prop).toString());
374 }
375 catch (...) {
376 return std::nullopt;
377 }
378 };
379
380 const std::optional<int> numThreads = getIntProp("AvalancheSchedulerSvc","ThreadPoolSize");
381 if (numThreads.has_value()) {
382 m_mtConfigHist->Fill("N threads", numThreads.value(), 1.0);
383 }
384 else {
385 ATH_MSG_WARNING("Could not retrieve the number of threads to fill the monitoring histogram");
386 }
387
388 const std::optional<int> numSlots = getIntProp("EventDataSvc","NSlots");
389 if (numSlots.has_value()) {
390 m_mtConfigHist->Fill("N slots", numSlots.value(), 1.0);
391 }
392 else {
393 ATH_MSG_WARNING("Could not retrieve the number of slots to fill the monitoring histogram");
394 }
395}
396
398{
399 // Retrieve the enabled ROBs/SubDets list from DataFlowConfig which is a special object
400 // used online to hold DF properties passed from TDAQ to HLT as run parameters
401 auto jobOptionsSvc = service<Gaudi::Interfaces::IOptionsSvc>("JobOptionsSvc", /*createIf=*/ false);
402 if (!jobOptionsSvc.isValid()) {
403 ATH_MSG_WARNING("Could not retrieve JobOptionsSvc, will not fill SubDetectors histogram");
404 return;
405 }
406
407 Gaudi::Property<std::vector<uint32_t>> enabledROBsProp("EnabledROBs",{});
408 std::set<uint32_t> enabledROBs;
409 if (enabledROBsProp.fromString(jobOptionsSvc->get("DataFlowConfig.DF_Enabled_ROB_IDs","[]")).isSuccess()) {
410 enabledROBs.insert(enabledROBsProp.value().begin(), enabledROBsProp.value().end());
411 ATH_MSG_DEBUG("Retrieved a list of " << enabledROBs.size()
412 << " ROBs from DataFlowConfig.DF_Enabled_ROB_IDs");
413 }
414 else {
415 ATH_MSG_ERROR("Could not parse DataFlowConfig.DF_Enabled_ROB_IDs from JobOptionsSvc");
416 }
417
418 // Retrieve detector mask from detector store
419 SmartIF<StoreGateSvc> inputMetaDataStore = service<StoreGateSvc>("InputMetaDataStore", /*createIf=*/ false);
420 if (!inputMetaDataStore.isValid()) {
421 ATH_MSG_WARNING("Could not retrieve InputMetaDataStore, will not fill SubDetectors histogram");
422 return;
423 }
424 const ByteStreamMetadataContainer* metadatacont{nullptr};
425 if (inputMetaDataStore->retrieve(metadatacont, "ByteStreamMetadata").isFailure()) {
426 ATH_MSG_WARNING("Could not retrieve ByteStreamMetadata, will not fill SubDetectors histogram");
427 return;
428 }
429
430 const ByteStreamMetadata* metadata = *(metadatacont->begin());
431 uint64_t detMaskLeast = metadata->getDetectorMask();
432 uint64_t detMaskMost = metadata->getDetectorMask2();
433
434 // Decode subdetector masks
435 std::vector<eformat::SubDetector> subDetOn;
436 std::vector<eformat::SubDetector> subDetOff;
437 std::vector<eformat::SubDetector> subDetAll;
438 eformat::helper::DetectorMask(detMaskLeast, detMaskMost).sub_detectors(subDetOn);
439 eformat::helper::DetectorMask(~detMaskLeast, ~detMaskMost).sub_detectors(subDetOff);
440 eformat::helper::DetectorMask(~std::bitset<128>()).sub_detectors(subDetAll);
441
442 // Add bins with labels for every subdetector name
443 for (const eformat::SubDetector sd : subDetAll) {
444 m_subdetHist->GetXaxis()->FindBin(eformat::helper::SubDetectorDictionary.string(sd).data());
445 }
446 m_subdetHist->LabelsDeflate("X");
447
448 // Fill histogram with enabled subdetectors
449 for (const eformat::SubDetector sd : subDetOn) {
450 m_subdetHist->Fill(eformat::helper::SubDetectorDictionary.string(sd).data(), "on", 1.0);
451 }
452
453 // Fill histogram with disabled subdetectors
454 for (const eformat::SubDetector sd : subDetOff) {
455 m_subdetHist->Fill(eformat::helper::SubDetectorDictionary.string(sd).data(), "off", 1.0);
456 }
457
458 // Fill histogram with ROB counts
459 for (const uint32_t robid : enabledROBs) {
460 const std::string sdname = eformat::helper::SourceIdentifier(robid).human_detector();
461 m_subdetHist->Fill(sdname.data(), "# ROB", 1.0);
462 }
463}
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
This file contains the class definition for the ByteStreamMetadataContainer class.
This file contains the class definition for the ByteStreamMetadata class.
virtual void lock()=0
Interface to allow an object to lock itself when made const in SG.
Validity Range object.
OH histogram lock header file.
Online operations monitoring.
Header file for AthHistogramAlgorithm.
static const std::string & type()
Incident type.
Definition Incidents.h:49
int workerID() const
assigned worker ID from processing unit
Definition Incidents.h:40
This class is the StoreGate data object for bytestream metadata.
This class is the StoreGate data object for bytestream metadata.
const_iterator begin() const noexcept
Return a const_iterator pointing at the beginning of the collection.
Basic time unit for IOVSvc.
Definition IOVTime.h:33
static constexpr uint64_t MAXTIMESTAMP
Definition IOVTime.h:58
static constexpr uint32_t MAXRUN
Definition IOVTime.h:48
Map for magnetic field.
bool solenoidOn() const
status of the magnets
void fillProcThreadHist(int workerID)
TH1I * m_releaseHist
std::string m_histPath
histogram booking path
std::unordered_map< std::string, IOVRange > m_currentIOVs
current IOVs managed by IOVDbSvc
ServiceHandle< ITHistSvc > m_histSvc
ServiceHandle< IIncidentSvc > m_incidentSvc
virtual StatusCode initialize() override
void fillReleaseDataHist()
TrigOpMonitor(const std::string &name, ISvcLocator *pSvcLocator)
Gaudi::Property< bool > m_detailedHists
TH2I * m_magFieldHist
TProfile * m_lumiHist
virtual StatusCode start() override
virtual StatusCode execute(const EventContext &ctx) override
TProfile * m_muHist
void fillIOVDbChangeHist(const EventContext &ctx)
Gaudi::Property< unsigned short int > m_maxLB
Gaudi::Property< std::string > m_releaseData
SG::ReadCondHandleKey< AtlasFieldMapCondObj > m_fieldMapKey
virtual void handle(const Incident &incident) override
std::unordered_map< std::string, FolderHist > m_folderHist
histograms for COOL folders
SG::ReadCondHandleKey< LuminosityCondData > m_lumiDataKey
void fillLumiHist(const EventContext &ctx)
EventIDBase::number_type m_previousLB
LB of previous event.
StatusCode bookHists()
SmartIF< IIOVDbSvc > m_IOVDbSvc
TH2I * m_subdetHist
TH2I * m_iovChangeHist
TH2I * m_mtConfigHist
TH1I * m_workersHist
Scoped lock to be used for threaded histogram operations.
::StatusCode StatusCode
StatusCode definition for legacy code.
Filled by IIOVDbSvc::getKeyInfo.
Definition IIOVDbSvc.h:44
Histograms for COOL folders.
TFile * file