ATLAS Offline Software
GenericMonitoringTool.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 <map>
6 #include <mutex>
7 #include <algorithm>
8 
9 #include <TH1.h>
10 #include <TH2.h>
11 #include <TProfile.h>
12 #include <TProfile2D.h>
13 
18 
20 
21 using namespace Monitored;
22 
24 
26  ATH_CHECK(m_histSvc.retrieve());
27  return StatusCode::SUCCESS;
28 }
29 
31  if ( not m_explicitBooking ) {
32  ATH_MSG_DEBUG("Proceeding to histogram booking");
33  return book();
34  }
35  return StatusCode::SUCCESS;
36 }
37 
39  m_alwaysCreateFillers.clear();
40  m_fillers.clear();
41  if (m_registerHandler) {
42  ATH_MSG_DEBUG("Deregistering incident handler");
43  SmartIF<IIncidentSvc> incSvc{service("IncidentSvc")};
44  ATH_CHECK(incSvc.isValid());
45  incSvc->removeListener(this, IncidentType::BeginEvent);
46  }
47  return StatusCode::SUCCESS;
48 }
49 
50 void GenericMonitoringTool::handle( const Incident& ) {
51  for (const auto& filler : m_alwaysCreateFillers) {
52  filler->touch();
53  }
54 }
55 
57 
58  // If no histogram path given use parent or our own name
59  if (m_histoPath.empty()) {
60  auto named = dynamic_cast<const INamedInterface*>(parent());
61  m_histoPath = named ? named->name() : name();
62  }
63 
64  // Replace dot (e.g. MyAlg.MyTool) with slash to create sub-directory
65  std::replace( m_histoPath.begin(), m_histoPath.end(), '.', '/' );
66 
67  ATH_MSG_DEBUG("Booking histograms in path: " << m_histoPath.value());
68 
69  HistogramFillerFactory factory(this, m_histoPath);
70 
71  for (const std::string& item : m_histograms) {
72  if (item.empty()) {
73  ATH_MSG_DEBUG( "Skipping empty histogram definition" );
74  continue;
75  }
76  ATH_MSG_DEBUG( "Configuring monitoring for: " << item );
78 
79  if (def.ok) {
80  std::shared_ptr<HistogramFiller> filler(factory.create(def));
81 
82  if (filler) {
83  if (def.kAlwaysCreate) {
84  if (m_registerHandler) {
85  m_alwaysCreateFillers.push_back(filler); // prepare list of fillers for handler
86  } else {
87  filler->touch(); // create now and be done with it
88  }
89  }
90  m_fillers.push_back(filler);
91  } else {
92  ATH_MSG_WARNING( "The histogram filler cannot be instantiated for: " << def.name );
93  }
94  } else {
95  ATH_MSG_ERROR( "Unparsable histogram definition: " << item );
96  return StatusCode::FAILURE;
97  }
98  ATH_MSG_DEBUG( "Monitoring for variable " << def.name << " prepared" );
99  }
100 
101  if ( m_fillers.empty() && m_failOnEmpty ) {
102  std::string hists;
103  for (const auto &h : m_histograms) hists += (h+",");
104  ATH_MSG_ERROR("No monitored variables created based on histogram definition: [" << hists <<
105  "] Remove this monitoring tool or check its configuration.");
106  return StatusCode::FAILURE;
107  }
108 
109  // are there some histograms that should always be made?
110  // then register to be notified on every event
111  if (! m_alwaysCreateFillers.empty() && m_registerHandler) {
112  ATH_MSG_DEBUG("Registering incident handler");
113  SmartIF<IIncidentSvc> incSvc{service("IncidentSvc")};
114  ATH_CHECK(incSvc.isValid());
115  incSvc->addListener(this, IncidentType::BeginEvent);
116  }
117 
118  return StatusCode::SUCCESS;
119 }
120 
121 namespace Monitored {
122  std::ostream& operator<< ( std::ostream& os, const std::reference_wrapper<Monitored::IMonitoredVariable>& rmv ) {
123  std::string s = rmv.get().name();
124  return os << s;
125  }
126 }
127 
128 namespace std {
129  // Next four functions are for speeding up lookups in the the caching of invokeFillers
130  // They allow us to directly compare keys of the cache std::map
131  // with vectors of IMonitoredVariables, avoiding memory allocations
132  // these compare strings and IMonitoredVariables
133  bool operator<(const std::string& a, const std::reference_wrapper<Monitored::IMonitoredVariable>& b) {
134  return a < b.get().name();
135  }
136  bool operator<(const std::reference_wrapper<Monitored::IMonitoredVariable>& a, const std::string& b) {
137  return a.get().name() < b;
138  }
139 
140  // lexicographical comparison of cache map items and vector of IMonitoredVariables
141  bool operator<(const std::vector<std::string>& lhs,
142  const std::vector<std::reference_wrapper<Monitored::IMonitoredVariable>>& rhs) {
143  return std::lexicographical_compare(lhs.begin(), lhs.end(),
144  rhs.begin(), rhs.end());
145  }
146  bool operator<(const std::vector<std::reference_wrapper<Monitored::IMonitoredVariable>>& lhs,
147  const std::vector<std::string>& rhs) {
148  return std::lexicographical_compare(lhs.begin(), lhs.end(),
149  rhs.begin(), rhs.end());
150  }
151 }
152 
153 namespace {
154  // this exists to avoid reallocating memory on every invokeFillers call
156 
157  // Ensure that TLS defined in this library actually gets used.
158  // Avoids a potential slowdown in accessing TLS seen in simualation.
159  // See ATLASSIM-4932.
160  [[maybe_unused]]
161  const Monitored::HistogramFiller::VariablesPack& varDum = tl_vars;
162 }
163 
164 void GenericMonitoringTool::invokeFillers(const std::vector<std::reference_wrapper<Monitored::IMonitoredVariable>>& monitoredVariables) const {
165  // This is the list of fillers to consider in the invocation.
166  // If we are using the cache then this may be a proper subset of m_fillers; otherwise will just be m_fillers
167  const std::vector<std::shared_ptr<Monitored::HistogramFiller>>* fillerList{nullptr};
168  // do we need to update the cache?
169  bool makeCache = false;
170  // pointer to list of matched fillers, if we need to update the cache (default doesn't create the vector)
171  std::unique_ptr<std::vector<std::shared_ptr<Monitored::HistogramFiller>>> matchedFillerList;
172  if (m_useCache) {
173  // lock the cache during lookup
174  std::scoped_lock cacheguard(m_cacheMutex);
175  const auto match = m_fillerCacheMap.find(monitoredVariables);
176  if (match != m_fillerCacheMap.end()) {
177  fillerList = match->second.get();
178  } else {
179  fillerList = &m_fillers;
180  matchedFillerList = std::make_unique<std::vector<std::shared_ptr<Monitored::HistogramFiller>>>();
181  makeCache = true;
182  }
183  } else {
184  fillerList = &m_fillers;
185  }
186 
187  for ( auto filler: *fillerList ) {
188  tl_vars.reset();
189  const int fillerCardinality = filler->histogramVariablesNames().size() + (filler->histogramWeightName().empty() ? 0: 1) + (filler->histogramCutMaskName().empty() ? 0 : 1);
190 
191  if ( fillerCardinality == 1 ) { // simplest case, optimising this to be super fast
192  for ( auto& var: monitoredVariables ) {
193  if ( var.get().name().compare( filler->histogramVariablesNames()[0] ) == 0 ) {
194  tl_vars.var[0] = &var.get();
195  {
196  auto guard{filler->getLock()};
197  filler->fill( tl_vars );
198  }
199  if (makeCache) {
200  matchedFillerList->push_back(filler);
201  }
202  break;
203  }
204  }
205  } else { // a more complicated case, and cuts or weights
206  int matchesCount = 0;
207  for ( const auto& var: monitoredVariables ) {
208  bool matched = false;
209  for ( unsigned fillerVarIndex = 0; fillerVarIndex < filler->histogramVariablesNames().size(); ++fillerVarIndex ) {
210  if ( var.get().name().compare( filler->histogramVariablesNames()[fillerVarIndex] ) == 0 ) {
211  tl_vars.set(fillerVarIndex, &var.get());
212  matched = true;
213  matchesCount++;
214  break;
215  }
216  }
217  if ( matchesCount == fillerCardinality ) break;
218  if ( not matched ) { // may be a weight or cut variable still
219  if ( var.get().name().compare( filler->histogramWeightName() ) == 0 ) {
220  tl_vars.weight = &var.get();
221  matchesCount ++;
222  } else if ( var.get().name().compare( filler->histogramCutMaskName() ) == 0 ) {
223  tl_vars.cut = &var.get();
224  matchesCount++;
225  }
226  }
227  if ( matchesCount == fillerCardinality ) break;
228  }
229  if ( matchesCount == fillerCardinality ) {
230  {
231  auto guard{filler->getLock()};
232  filler->fill( tl_vars );
233  }
234  if (makeCache) {
235  matchedFillerList->push_back(filler);
236  }
237  } else if ( ATH_UNLIKELY( matchesCount != 0 ) ) { // something has matched, but not all, worth informing user
238  invokeFillersDebug(filler, monitoredVariables);
239  }
240  }
241  }
242 
243  if (makeCache) {
244  // we may hit this multiple times. If another thread has updated the cache in the meanwhile, don't update
245  // (or we might delete the fillerList under another thread)
246  std::scoped_lock cacheguard(m_cacheMutex);
247  const auto match = m_fillerCacheMap.find(monitoredVariables);
248  if (match == m_fillerCacheMap.end()) {
249  std::vector<std::string> key;
250  key.reserve(monitoredVariables.size());
251  for (const auto& mv : monitoredVariables) {
252  key.push_back(mv.get().name());
253  }
254  m_fillerCacheMap[key].swap(matchedFillerList);
255  }
256  }
257 }
258 
259 void GenericMonitoringTool::invokeFillersDebug(const std::shared_ptr<Monitored::HistogramFiller>& filler,
260  const std::vector<std::reference_wrapper<Monitored::IMonitoredVariable>>& monitoredVariables) const {
261  bool reasonFound = false;
262  if (ATH_UNLIKELY(!filler->histogramWeightName().empty() && !tl_vars.weight)) {
263  reasonFound = true;
264  ATH_MSG_DEBUG("Filler weight not found in monitoredVariables:"
265  << "\n Filler weight : " << filler->histogramWeightName()
266  << "\n Asked to fill from mon. tl_vars: " << monitoredVariables);
267  }
268  if (ATH_UNLIKELY(!filler->histogramCutMaskName().empty() && !tl_vars.cut)) {
269  reasonFound = true;
270  ATH_MSG_DEBUG("Filler cut mask not found in monitoredVariables:"
271  << "\n Filler cut mask : " << filler->histogramCutMaskName()
272  << "\n Asked to fill from mon. tl_vars: " << monitoredVariables);
273  }
274  if ( not reasonFound ) {
275  ATH_MSG_DEBUG("Filler has different variables than monitoredVariables:"
276  << "\n Filler variables : " << filler->histogramVariablesNames()
277  << "\n Asked to fill from mon. tl_vars: " << monitoredVariables
278  << "\n Selected monitored variables: " << tl_vars.names() );
279  }
280 }
281 
283  return Gaudi::Hive::currentContext().eventID().run_number();
284 }
285 
287  return Gaudi::Hive::currentContext().eventID().lumi_block();
288 }
operator<
bool operator<(const DataVector< T > &a, const DataVector< T > &b)
Vector ordering relation.
replace
std::string replace(std::string s, const std::string &s2, const std::string &s3)
Definition: hcg.cxx:307
beamspotnt.var
var
Definition: bin/beamspotnt.py:1394
python.SystemOfUnits.s
int s
Definition: SystemOfUnits.py:131
Monitored::HistogramFiller::histogramVariablesNames
const std::vector< std::string > & histogramVariablesNames() const
Definition: HistogramFiller.h:132
GenericMonitoringTool::stop
virtual StatusCode stop() override
Definition: GenericMonitoringTool.cxx:38
IMonitoredVariable.h
Monitored::HistogramFiller::histogramWeightName
const std::string & histogramWeightName() const
Definition: HistogramFiller.h:136
xAOD::uint32_t
setEventNumber uint32_t
Definition: EventInfo_v1.cxx:127
HistogramDef.h
GenericMonitoringTool::lumiBlock
virtual uint32_t lumiBlock()
Definition: GenericMonitoringTool.cxx:286
Monitored::operator<<
std::ostream & operator<<(std::ostream &os, const std::reference_wrapper< Monitored::IMonitoredVariable > &rmv)
Definition: GenericMonitoringTool.cxx:122
ATH_UNLIKELY
#define ATH_UNLIKELY(x)
Definition: AthUnlikelyMacros.h:17
GenericMonitoringTool::book
StatusCode book()
Book histograms.
Definition: GenericMonitoringTool.cxx:56
Monitored::HistogramDef
the internal class used to keep parsed Filler properties
Definition: HistogramDef.h:15
GenericMonitoringTool::~GenericMonitoringTool
virtual ~GenericMonitoringTool() override
Definition: GenericMonitoringTool.cxx:23
Monitored::HistogramDef::ok
bool ok
good declaration: parsing or copying successful
Definition: HistogramDef.h:77
GenericMonitoringTool::initialize
virtual StatusCode initialize() override
Definition: GenericMonitoringTool.cxx:25
GenericMonitoringTool::invokeFillersDebug
void invokeFillersDebug(const std::shared_ptr< Monitored::HistogramFiller > &filler, const std::vector< std::reference_wrapper< Monitored::IMonitoredVariable >> &monitoredVariables) const
Definition: GenericMonitoringTool.cxx:259
Monitored::HistogramFiller::VariablesPack
helper class to pass variables to fillers
Definition: HistogramFiller.h:71
GenericMonitoringTool.h
ATH_MSG_ERROR
#define ATH_MSG_ERROR(x)
Definition: AthMsgStreamMacros.h:33
Monitored
Generic monitoring tool for athena components.
Definition: GenericMonitoringTool.h:30
h
Monitored::HistogramFiller::histogramCutMaskName
const std::string & histogramCutMaskName() const
Definition: HistogramFiller.h:140
EL::StatusCode
::StatusCode StatusCode
StatusCode definition for legacy code.
Definition: PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/StatusCode.h:22
ATH_MSG_DEBUG
#define ATH_MSG_DEBUG(x)
Definition: AthMsgStreamMacros.h:29
Monitored::HistogramDef::name
std::vector< std::string > name
names of monitored variables
Definition: HistogramDef.h:16
Monitored::HistogramDef::parse
static const HistogramDef parse(const std::string &histogramDefinition)
Parses histogram defintion from json data.
Definition: HistogramDef.cxx:11
GenericMonitoringTool::handle
void handle(const Incident &) override
Definition: GenericMonitoringTool.cxx:50
test_pyathena.parent
parent
Definition: test_pyathena.py:15
ATH_CHECK
#define ATH_CHECK
Definition: AthCheckMacros.h:40
MakeTH3DFromTH2Ds.hists
hists
Definition: MakeTH3DFromTH2Ds.py:72
Monitored::HistogramFillerFactory
Factory of the histogram fillers.
Definition: HistogramFillerFactory.h:22
ReadFromCoolCompare.os
os
Definition: ReadFromCoolCompare.py:231
GenericMonitoringTool::runNumber
virtual uint32_t runNumber()
Definition: GenericMonitoringTool.cxx:282
GenericMonitoringTool::invokeFillers
void invokeFillers(const std::vector< std::reference_wrapper< Monitored::IMonitoredVariable >> &monitoredVariables) const
feed the fillers
Definition: GenericMonitoringTool.cxx:164
GenericMonitoringTool::start
virtual StatusCode start() override
Definition: GenericMonitoringTool.cxx:30
HistogramFiller.h
name
std::string name
Definition: Control/AthContainers/Root/debug.cxx:225
plotBeamSpotMon.b
b
Definition: plotBeamSpotMon.py:77
python.ElectronD3PDObject.matched
matched
Definition: ElectronD3PDObject.py:138
item
Definition: ItemListSvc.h:43
a
TList * a
Definition: liststreamerinfos.cxx:10
ATH_MSG_WARNING
#define ATH_MSG_WARNING(x)
Definition: AthMsgStreamMacros.h:32
Monitored::HistogramFiller::touch
void touch() const
Ensure histogram exists.
Definition: HistogramFiller.h:127
Monitored::HistogramFillerFactory::create
HistogramFiller * create(const HistogramDef &def)
Creates HistogramFiller instance for given definition.
Definition: HistogramFillerFactory.cxx:25
Monitored::HistogramDef::kAlwaysCreate
bool kAlwaysCreate
always create this histogram, even if never filled
Definition: HistogramDef.h:52
ATLAS_THREAD_SAFE
#define ATLAS_THREAD_SAFE
Definition: checker_macros.h:211
HistogramFillerFactory.h
match
bool match(std::string s1, std::string s2)
match the individual directories of two strings
Definition: hcg.cxx:356
mapkey::key
key
Definition: TElectronEfficiencyCorrectionTool.cxx:37