ATLAS Offline Software
ValgrindAuditor.cxx
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3 */
4 
5 // Package includes
6 #include "ValgrindAuditor.h"
7 
8 // Framework includes
9 #include "GaudiKernel/IIncidentSvc.h"
12 
13 // STL includes
14 #include <algorithm>
15 #include <iterator>
16 #include <limits>
17 
18 // Boost includes
19 #include <boost/algorithm/string.hpp>
20 
21 using std::string;
22 
23 // Constructor
25  ISvcLocator* pSvcLocator)
26  : Auditor(name, pSvcLocator),
27  m_valSvc("ValgrindSvc", this->name()),
28  m_eventCounter(0)
29 {
30  declareProperty("ProfiledAlgs", m_algs,
31  "List of algorithms to run in valgrind");
32 
33  declareProperty("ProfiledIntervals", m_intervals,
34  "Intervals to profile (e.g. 'MyAlg.initialize:MyAlg.finalize'" );
35 
36  declareProperty("IgnoreFirstNEvents", m_ignoreFirstNEvents = 0,
37  "Do not profile the first N events");
38 
39  declareProperty("DumpAfterEachInterval", m_dumpAfterEachInterval = true,
40  "Dump separate profile after each interval in ProfiledIntervals" );
41 }
42 
43 // Destructor
45 {
46 }
47 
48 
50 {
51  if ( !m_valSvc.retrieve().isSuccess()) {
52  msgStream() << MSG::ERROR << "Could not retrieve the ValgrindSvc" << endmsg;
53  return StatusCode::FAILURE;
54  }
55 
56  const IProperty* valSvcProp = dynamic_cast<const IProperty*>(&(*m_valSvc));
57  if ( !valSvcProp ) {
58  msgStream() << MSG::ERROR
59  << "Could not retrieve IProperty interface to ValgrindSvc."
60  << endmsg;
61  return StatusCode::FAILURE;
62  }
63 
64  // We inherit the OutputLevel from ValgrindSvc
65  ATH_CHECK(setProperty(valSvcProp->getProperty("OutputLevel")));
66 
67  msgStream() << MSG::VERBOSE
68  << "Initializing " << name() << "..."
69  << endmsg;
70 
71  // Copy some properties from ValgrindSvc
72  std::string properties[] = {"ProfiledAlgs",
73  "ProfiledIntervals",
74  "IgnoreFirstNEvents",
75  "DumpAfterEachInterval"};
76 
77  for( std::string prop : properties ) {
78  if ( !setProperty(valSvcProp->getProperty(prop)) ) {
79  msgStream() << MSG::ERROR << "Cannot set " << prop << " property." << endmsg;
80  return StatusCode::FAILURE;
81  }
82  }
83 
84  // Reset internal event counter
85  m_eventCounter = 0;
86 
87  // Create regular expressions from algorithm names
88  for( const std::string& re : m_algs ) {
89  try {
90  m_algsRegEx.push_back( boost::regex(re) );
91  }
92  catch ( const boost::regex_error& ) {
93  msgStream() << MSG::ERROR << "Ignoring invalid regular expression: " << re << endmsg;
94  }
95  }
96 
97  if ( msgLevel() <= MSG::INFO ) {
98  std::ostringstream out;
99  out << "[ ";
100  std::copy( m_algs.begin(), m_algs.end(),
101  std::ostream_iterator<std::string>( out, " " ) );
102  out << "]";
103  msgStream() << MSG::INFO << "Profiled algorithms: " << out.str() << endmsg;
104 
105  out.str("");
106  out << "[ ";
107  std::copy( m_intervals.begin(), m_intervals.end(),
108  std::ostream_iterator<std::string>( out, " " ) );
109  out << "]";
110  msgStream() << MSG::INFO << "Profiled intervals: " << out.str() << endmsg;
111  }
112 
113  if (!m_intervals.empty()) {
114  if ( decodeIntervals().isFailure() ) {
115  msgStream() << MSG::ERROR << "Syntax error in ProfiledIntervals" << endmsg;
116  return StatusCode::FAILURE;
117  }
118  }
119 
120  // Register incidents
121  ServiceHandle<IIncidentSvc> incSvc("IncidentSvc", this->name());
122  if ( !incSvc.retrieve().isSuccess() ) {
123  msgStream() << MSG::ERROR << "Unable to get the IncidentSvc" << endmsg;
124  return StatusCode::FAILURE;
125  }
126  // Try to be the first incident handler to be called
127  const long prio = std::numeric_limits<long>::max();
128 
129  incSvc->addListener( this, IncidentType::BeginEvent, prio );
130 
131  for( const std::pair<NameEvt,NameEvt>& h : m_hooks ) {
132  // No regular expressions allowed for incidents. Take the original string.
133  if ( h.first.second=="incident" ) incSvc->addListener( this, h.first.first.str(), prio );
134  if ( h.second.second=="incident" ) incSvc->addListener( this, h.second.first.str(), prio );
135  }
136 
137  return StatusCode::SUCCESS;
138 }
139 
140 
141 /********************************************************************************
142  * Incident handler
143  */
144 void ValgrindAuditor::handle( const Incident& inc )
145 {
146  // Internal event counter
147  if ( inc.type() == IncidentType::BeginEvent ) {
148  m_eventCounter++;
149  }
150 
151  // Check if the incident appears at beginning or end of interval
152  std::vector< std::pair<NameEvt,NameEvt> >::const_iterator h;
153  for (h=m_hooks.begin(); h!=m_hooks.end(); ++h) {
154  if ( h->first.second=="incident" ) do_before(inc.type(), "incident");
155  if ( h->second.second=="incident" ) do_after(inc.type(), "incident");
156  }
157 }
158 
159 
160 /********************************************************************************
161  * Gaudi auditor hooks
162  */
163 void ValgrindAuditor::before (const std::string& event, const std::string& name,
164  const EventContext&)
165 {
167  else do_before(name, boost::to_lower_copy(event));
168 }
169 
170 void ValgrindAuditor::after(const std::string& event, const std::string& name,
171  const EventContext&, const StatusCode&)
172 {
174  else do_after(name, boost::to_lower_copy(event));
175 }
176 
177 
178 /********************************************************************************
179  * Enable callgrind instrumentation before algorithm execution
180  */
181 void ValgrindAuditor::do_beforeExecute(const std::string& name)
182 {
183  if ( algMatch(name) ) {
185  if ( msgLevel() <= MSG::DEBUG )
186  msgStream() << MSG::DEBUG << "Starting callgrind before execute of " << name
187  << " [event #" << m_eventCounter << "]" << endmsg;
188 
189  m_valSvc->callgrindStartInstrumentation();
190  }
191  }
192 }
193 
194 
195 /********************************************************************************
196  * Disable callgrind instrumentation after algorithm execution
197  */
198 void ValgrindAuditor::do_afterExecute(const std::string& name)
199 {
200  if ( algMatch(name) ) {
202  m_valSvc->callgrindStopInstrumentation();
203  if ( msgLevel() <= MSG::DEBUG )
204  msgStream() << MSG::DEBUG << "Stopping callgrind after execute of " << name
205  << " [event #" << m_eventCounter << "]" << endmsg;
206  }
207  }
208 }
209 
210 
211 /********************************************************************************
212  * Enable callgrind instrumentation
213  */
214 void ValgrindAuditor::do_before(const std::string& name, const std::string& hook)
215 {
216  std::vector< std::pair<NameEvt,NameEvt> >::const_iterator iter;
217 
218  for (iter=m_hooks.begin(); iter!=m_hooks.end(); ++iter) {
219  if ( boost::regex_match(name, iter->first.first) &&
220  iter->first.second == hook ) {
221  m_valSvc->callgrindStartInstrumentation();
222  if ( msgLevel() <= MSG::DEBUG )
223  msgStream() << MSG::DEBUG << "Starting callgrind before " << hook
224  << " of " << name << endmsg;
225  }
226  }
227 }
228 
229 /********************************************************************************
230  * Disable callgrind instrumentation
231  */
232 void ValgrindAuditor::do_after(const std::string& name, const std::string& hook)
233 {
234  std::vector< std::pair<NameEvt,NameEvt> >::const_iterator iter;
235 
236  for (iter=m_hooks.begin(); iter!=m_hooks.end(); ++iter) {
237  if ( boost::regex_match(name, iter->second.first) &&
238  iter->second.second == hook ) {
239  m_valSvc->callgrindStopInstrumentation();
240  if ( msgLevel() <= MSG::DEBUG )
241  msgStream() << MSG::DEBUG << "Stopping callgrind after " << hook
242  << " of " << name << endmsg;
243 
244  if ( m_dumpAfterEachInterval ) {
245  m_valSvc->callgrindDumpStats(msgStream().stream());
246  msgStream() << MSG::INFO << "Creating callgrind profile #" << m_valSvc->profileCount()
247  << " after " << hook << " of " << name << endmsg;
248  }
249  }
250  }
251 }
252 
253 
254 /********************************************************************************
255  * Decodes the intervals
256  */
257 
258 namespace {
259  // Helper to decode name/event pair from string (e.g. MyAlg.initialize)
260  StatusCode decodeNameEvt(const std::string& s, ValgrindAuditor::NameEvt& nameEvt)
261  {
262  // Find last(!) "." delimiter (earlier ones might be part of regexp)
263  string::size_type loc = s.rfind('.');
264  if ( loc==string::npos ) return StatusCode::FAILURE;
265 
266  try {
267  nameEvt.first = boost::regex(s.substr(0,loc));
268  }
269  catch ( const boost::regex_error& ) {
270  return StatusCode::FAILURE;
271  }
272 
273  nameEvt.second = s.substr(loc+1);
274 
275  return StatusCode::SUCCESS;
276  }
277 }
278 
280 {
281  m_hooks.clear();
282  m_hooks.reserve(m_intervals.size());
283 
284  std::vector<string>::const_iterator iter = m_intervals.begin();
285  for (; iter!=m_intervals.end(); ++iter) {
286  const string& spec = *iter;
287  string::size_type loc = spec.find(':');
288 
289  // If there is no delimiter interpret as [a,a]
290  string s1, s2;
291  if (loc==string::npos) s1 = s2 = spec;
292  else { // Extract interval
293  s1 = spec.substr(0,loc);
294  s2 = spec.substr(loc+1);
295  }
296 
297  NameEvt ne1, ne2;
298  if ( s1=="" || s2=="" ||
299  decodeNameEvt(s1,ne1).isFailure() || decodeNameEvt(s2,ne2).isFailure() ) {
300  msgStream() << MSG::ERROR << "Invalid profiling interval [" << spec << "]" << endmsg;
301  return StatusCode::FAILURE;
302  }
303 
304  std::pair<NameEvt,NameEvt> p(ne1,ne2);
305  m_hooks.push_back(p);
306  }
307 
308  return StatusCode::SUCCESS;
309 }
310 
311 /********************************************************************************
312  * Match the algorithm name against all regular expressions
313  */
314 bool ValgrindAuditor::algMatch(const std::string& name)
315 {
316  std::vector<boost::regex>::const_iterator iter;
317  for ( iter=m_algsRegEx.begin(); iter!=m_algsRegEx.end(); ++iter ) {
318  if ( boost::regex_match(name,*iter) ) return true;
319  }
320  return false;
321 }
322 
ValgrindAuditor::decodeIntervals
StatusCode decodeIntervals()
Definition: ValgrindAuditor.cxx:279
ValgrindAuditor::before
virtual void before(const std::string &event, const std::string &name, const EventContext &ctx) override
Definition: ValgrindAuditor.cxx:163
ReadCellNoiseFromCoolCompare.s1
s1
Definition: ReadCellNoiseFromCoolCompare.py:378
createLinkingScheme.iter
iter
Definition: createLinkingScheme.py:62
AthCheckMacros.h
python.TestDriveDummies.properties
dictionary properties
Definition: TestDriveDummies.py:14
CaloCondBlobAlgs_fillNoiseFromASCII.spec
spec
Definition: CaloCondBlobAlgs_fillNoiseFromASCII.py:46
ValgrindAuditor::m_algs
std::vector< std::string > m_algs
List of algorithms to profile.
Definition: ValgrindAuditor.h:72
max
constexpr double max()
Definition: ap_fixedTest.cxx:33
python.AthDsoLogger.out
out
Definition: AthDsoLogger.py:70
ValgrindAuditor::m_algsRegEx
std::vector< boost::regex > m_algsRegEx
Regular expressions for algorithm name matching.
Definition: ValgrindAuditor.h:87
ValgrindAuditor::m_valSvc
ServiceHandle< IValgrindSvc > m_valSvc
Handle to ValgrindSvc.
Definition: ValgrindAuditor.h:69
ValgrindAuditor::m_ignoreFirstNEvents
unsigned int m_ignoreFirstNEvents
Don't profile on the first N events.
Definition: ValgrindAuditor.h:78
ValgrindAuditor::ValgrindAuditor
ValgrindAuditor(const std::string &name, ISvcLocator *pSvcLocator)
Definition: ValgrindAuditor.cxx:24
AthenaPoolTestWrite.stream
string stream
Definition: AthenaPoolTestWrite.py:12
ValgrindAuditor::m_hooks
std::vector< std::pair< NameEvt, NameEvt > > m_hooks
Internal storage of intervals.
Definition: ValgrindAuditor.h:90
ValgrindAuditor::m_dumpAfterEachInterval
bool m_dumpAfterEachInterval
Dump profile after each interval.
Definition: ValgrindAuditor.h:81
ValgrindAuditor::NameEvt
std::pair< boost::regex, std::string > NameEvt
Typedef for algorithm/event pair, e.g. ("MyAlg","initialize")
Definition: ValgrindAuditor.h:64
PrepareReferenceFile.regex
regex
Definition: PrepareReferenceFile.py:43
ValgrindAuditor::m_eventCounter
unsigned int m_eventCounter
Internal event counter for BeginEvent incident.
Definition: ValgrindAuditor.h:84
LArPulseShapeRunConfig.Execute
Execute
Definition: LArPulseShapeRunConfig.py:62
ValgrindAuditor.h
python.utils.AtlRunQueryDQUtils.p
p
Definition: AtlRunQueryDQUtils.py:209
event
POOL::TEvent event(POOL::TEvent::kClassAccess)
IValgrindSvc.h
endmsg
#define endmsg
Definition: AnalysisConfig_Ntuple.cxx:63
EL::StatusCode
::StatusCode StatusCode
StatusCode definition for legacy code.
Definition: PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/StatusCode.h:22
extractSporadic.h
list h
Definition: extractSporadic.py:96
ValgrindAuditor::handle
virtual void handle(const Incident &incident) override
Incident handler.
Definition: ValgrindAuditor.cxx:144
ValgrindAuditor::after
virtual void after(const std::string &event, const std::string &name, const EventContext &ctx, const StatusCode &sc) override
Definition: ValgrindAuditor.cxx:170
ATH_CHECK
#define ATH_CHECK
Definition: AthCheckMacros.h:40
ValgrindAuditor::algMatch
bool algMatch(const std::string &name)
Definition: ValgrindAuditor.cxx:314
ValgrindAuditor::do_after
void do_after(const std::string &name, const std::string &hook)
Definition: ValgrindAuditor.cxx:232
ValgrindAuditor::do_before
void do_before(const std::string &name, const std::string &hook)
Definition: ValgrindAuditor.cxx:214
name
std::string name
Definition: Control/AthContainers/Root/debug.cxx:240
ValgrindAuditor::m_intervals
std::vector< std::string > m_intervals
List of auditor intervals to profile.
Definition: ValgrindAuditor.h:75
ValgrindAuditor::initialize
virtual StatusCode initialize() override
Definition: ValgrindAuditor.cxx:49
h
python.Constants.INFO
int INFO
Definition: Control/AthenaCommon/python/Constants.py:15
re
const boost::regex re(r_e)
DEBUG
#define DEBUG
Definition: page_access.h:11
ReadCellNoiseFromCoolCompare.s2
s2
Definition: ReadCellNoiseFromCoolCompare.py:379
ValgrindAuditor::do_beforeExecute
virtual void do_beforeExecute(const std::string &name)
Start callgrind instrumentation.
Definition: ValgrindAuditor.cxx:181
python.SystemOfUnits.s
float s
Definition: SystemOfUnits.py:147
calibdata.copy
bool copy
Definition: calibdata.py:26
python.Constants.VERBOSE
int VERBOSE
Definition: Control/AthenaCommon/python/Constants.py:13
ValgrindAuditor::~ValgrindAuditor
virtual ~ValgrindAuditor()
Definition: ValgrindAuditor.cxx:44
ValgrindAuditor::do_afterExecute
virtual void do_afterExecute(const std::string &name)
Stop callgrind instrumentation.
Definition: ValgrindAuditor.cxx:198
ServiceHandle< IIncidentSvc >