ATLAS Offline Software
Loading...
Searching...
No Matches
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
21using std::string;
22
23// Constructor
24ValgrindAuditor::ValgrindAuditor(const std::string& name,
25 ISvcLocator* pSvcLocator)
26 : Auditor(name, pSvcLocator),
27 m_valSvc("ValgrindSvc", this->name()),
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
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
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 */
144void ValgrindAuditor::handle( const Incident& inc )
145{
146 // Internal event counter
147 if ( inc.type() == IncidentType::BeginEvent ) {
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 */
163void ValgrindAuditor::before (const std::string& event, const std::string& name,
164 const EventContext&)
165{
166 if ( event == IAuditor::Execute ) do_beforeExecute(name);
167 else do_before(name, boost::to_lower_copy(event));
168}
169
170void ValgrindAuditor::after(const std::string& event, const std::string& name,
171 const EventContext&, const StatusCode&)
172{
173 if ( event == IAuditor::Execute ) do_afterExecute(name);
174 else do_after(name, boost::to_lower_copy(event));
175}
176
177
178/********************************************************************************
179 * Enable callgrind instrumentation before algorithm execution
180 */
181void 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 */
198void 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 */
214void 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 */
232void 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
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
258namespace {
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.emplace_back(std::move(p));
306 }
307
308 return StatusCode::SUCCESS;
309}
310
311/********************************************************************************
312 * Match the algorithm name against all regular expressions
313 */
314bool 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
const boost::regex re(r_e)
#define endmsg
#define ATH_CHECK
Evaluate an expression and check for errors.
void setProperty(columnar::PythonToolHandle &self, const std::string &key, nb::object value)
Header file for AthHistogramAlgorithm.
virtual StatusCode initialize() override
ValgrindAuditor(const std::string &name, ISvcLocator *pSvcLocator)
std::vector< std::string > m_algs
List of algorithms to profile.
StatusCode decodeIntervals()
unsigned int m_eventCounter
Internal event counter for BeginEvent incident.
void do_after(const std::string &name, const std::string &hook)
virtual void after(const std::string &event, const std::string &name, const EventContext &ctx, const StatusCode &sc) override
void do_before(const std::string &name, const std::string &hook)
std::vector< std::string > m_intervals
List of auditor intervals to profile.
std::pair< boost::regex, std::string > NameEvt
Typedef for algorithm/event pair, e.g. ("MyAlg","initialize")
virtual void handle(const Incident &incident) override
Incident handler.
virtual ~ValgrindAuditor()
bool algMatch(const std::string &name)
std::vector< std::pair< NameEvt, NameEvt > > m_hooks
Internal storage of intervals.
unsigned int m_ignoreFirstNEvents
Don't profile on the first N events.
bool m_dumpAfterEachInterval
Dump profile after each interval.
ServiceHandle< IValgrindSvc > m_valSvc
Handle to ValgrindSvc.
virtual void do_beforeExecute(const std::string &name)
Start callgrind instrumentation.
virtual void do_afterExecute(const std::string &name)
Stop callgrind instrumentation.
std::vector< boost::regex > m_algsRegEx
Regular expressions for algorithm name matching.
virtual void before(const std::string &event, const std::string &name, const EventContext &ctx) override