diff --git a/GaudiProfiling/doc/jemallocprofiler.md b/GaudiProfiling/doc/jemallocprofiler.md index 94cff97cf3b813ac44c09b4dbdb43a8782f2e95a..3222cfedb761da8d9c04843c51ad25a4ff625177 100644 --- a/GaudiProfiling/doc/jemallocprofiler.md +++ b/GaudiProfiling/doc/jemallocprofiler.md @@ -18,7 +18,10 @@ A Gaudi algorithm has also been developped to perform memory heap dumps at vario and is configured using the StartFromEventN, StopAtEventN and DumpPeriod, as described in the example below. -Run +A Gaudi service is also available to provide the same functionality, with the advantage +of being able to profile without modifying the algorithm sequence. + +Running with the JemallocProfile algorithm -------------------------------------------------------------------------------- ### Change Options File @@ -47,6 +50,36 @@ Please note the the `--profilerName=jemalloc` to enbale the profiling, and the ` a file containing information useful to interpret the results (process id of the Gaudi job, and the absolute path of the executable, necessary to run the pprof analysis tool). +Running with the JemallocProfileSvc service +-------------------------------------------------------------------------------- + +### Change Options File + +Simple example of using the JemallocProfileSvc service in a Gaudi configurable: + +~~~~~~~{.py} +#!/usr/bin/env gaudirun.py +from Configurables import JemallocProfileSvc +#... +ApplicationMgr().ExtSvc += { "JemallocProfileSvc" } +JemallocProfileSvc().StartFromEventN=3 +JemallocProfileSvc().StopAtEventN=7 +JemallocProfileSvc().OutputLevel=DEBUG +~~~~~~~ + +It is also possible to trigger the profiling using incidents: + +~~~~~~~{.py} +from Configurables import JemallocProfileSvc +ApplicationMgr().ExtSvc += { "JemallocProfileSvc" } +JemallocProfileSvc().StartFromIncidents= [ "MyStartIncident1", "MyStartIncident2" ] +JemallocProfileSvc().StopAtIncidents= [ "MyStopIncident" ] +~~~~~~~ + +### Run the job +This is identical as for the algorithm version. + + Analyze -------------------------------------------------------------------------------- ### With text output diff --git a/GaudiProfiling/src/component/jemalloc/JemallocProfileSvc.cpp b/GaudiProfiling/src/component/jemalloc/JemallocProfileSvc.cpp new file mode 100644 index 0000000000000000000000000000000000000000..181e5e6f729746a66e9878e2ef86e67d871b2ace --- /dev/null +++ b/GaudiProfiling/src/component/jemalloc/JemallocProfileSvc.cpp @@ -0,0 +1,237 @@ +// Include files +#include "GaudiKernel/Service.h" +#include "GaudiKernel/IIncidentListener.h" +#include "GaudiKernel/IIncidentSvc.h" +#include <iostream> +#include <climits> + +// local +#include "JemallocProfileSvc.h" + +// including jemmalloc.h is difficult as the malloc signature is not exactly identical +// to the system one (issue with throw). +// We therefore declare mallctl here. +extern "C" +{ + int mallctl(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen); +} + +//----------------------------------------------------------------------------- +// Implementation file for class : JemallocProfileSvc +// +// 2016-01-11 : Ben Couturier +//----------------------------------------------------------------------------- + +//============================================================================= +// Standard constructor, initializes variables +//============================================================================= +JemallocProfileSvc::JemallocProfileSvc(const std::string& name, ISvcLocator* svcLoc): + base_class(name, svcLoc), m_eventNumber(0), m_startFromIncidents({}), + m_stopAtIncidents({}), m_hasStartIncident(false), m_hasStopIncident(false), + m_profiling(false) { + + declareProperty("StartFromEventN", m_nStartFromEvent = 0, + "After what event we start profiling. " + ); + + declareProperty("StartFromIncidents", m_startFromIncidents = {}, + "Incidents that trigger profiling start" + ); + + declareProperty("StopAtEventN", m_nStopAtEvent = 0, + "After what event we stop profiling. " + "If 0 than we also profile finalization stage. Default = 0." + ); + + declareProperty("StopAtIncidents", m_stopAtIncidents = {}, + "Incidents that trigger profiling start" + ); + + declareProperty("DumpPeriod", m_dumpPeriod = 100, + "Period for dumping head to a file. Default=100" + ); + +} + +//============================================================================= +// Initializer +//============================================================================= +StatusCode JemallocProfileSvc::initialize() { + StatusCode sc = base_class::initialize(); + if (sc.isFailure()) return sc; + + // register to the incident service + static const std::string serviceName = "IncidentSvc"; + m_incidentSvc = serviceLocator()->service(serviceName); + if ( ! m_incidentSvc ) { + error() << "Cannot retrieve " << serviceName << endmsg; + return StatusCode::FAILURE; + } + + debug() << "Register to the IncidentSvc" << endmsg; + m_incidentSvc->addListener(this, IncidentType::BeginEvent); + m_incidentSvc->addListener(this, IncidentType::EndEvent); + for (std::string incident: m_startFromIncidents) + { + m_incidentSvc->addListener(this, incident); + } + for (std::string incident: m_stopAtIncidents) + { + m_incidentSvc->addListener(this, incident); + } + + // Resetting the event counter + m_eventNumber = 0; + m_profiling = false; + + // Cache whether we have start/stop incidents + m_hasStartIncident = (m_startFromIncidents.size() > 0); + m_hasStopIncident = (m_stopAtIncidents.size() > 0); + + // Checking the consistency of the start/stop events + if (m_nStartFromEvent == 0 && !m_hasStartIncident + && (m_hasStopIncident || m_nStopAtEvent > 0)) + { + info() << "Stop profiling trigger was specified but no start. Defaulting to first events" << endmsg; + m_nStartFromEvent = 1; + } + + + + return StatusCode::SUCCESS; +} + +// Finalization of the service. +StatusCode JemallocProfileSvc::finalize() { + + // unregistering from the IncidentSvc + m_incidentSvc->removeListener(this, IncidentType::BeginEvent); + m_incidentSvc->removeListener(this, IncidentType::EndEvent); + m_incidentSvc.reset(); + return base_class::finalize(); +} + +//============================================================================= +// Event handling methods + +//============================================================================= + +// Handler for incidents +void JemallocProfileSvc::handle(const Incident& incident) +{ + if (IncidentType::BeginEvent == incident.type()) + { + handleBegin(); + } else if (IncidentType::EndEvent == incident.type()) + { + handleEnd(); + } + + // If already processing we can ignore the incidents for start + if (!m_profiling && m_hasStartIncident) + { + for(std::string startincident: m_startFromIncidents) + { + if (startincident == incident.type()) + { + info() << "Starting Jemalloc profile at incident " + << incident.type() << endmsg; + startProfiling(); + break; + } + } // Loop on incidents + } // If checking incidents to start + + // If already processing we can ignore the incidents for start + if (m_profiling && m_hasStopIncident) + { + for(std::string stopincident: m_stopAtIncidents) + { + if (stopincident == incident.type()) + { + info() << "Stopping Jemalloc profile at incident " + << incident.type() << endmsg; + stopProfiling(); + break; + } + } // Loop on incidents + } // If checking incidents to stop + + +} + +// Handler for incidents +// Called on at begin events +inline void JemallocProfileSvc::handleBegin() +{ + m_eventNumber += 1; + + if (m_eventNumber == m_nStartFromEvent) + { + startProfiling(); + } +} + +// Handler for incidents +// Called on at End events +inline void JemallocProfileSvc::handleEnd() +{ + if (m_profiling + && m_eventNumber != m_nStartFromEvent + && ((m_eventNumber - m_nStartFromEvent) % m_dumpPeriod == 0)) + { + dumpProfile(); + } + + if (m_eventNumber == m_nStopAtEvent) + { + stopProfiling(); + } +} + +//============================================================================= +// Utilities calling mallctl + +//============================================================================= + +/** + * Utility method to start profiling with jemalloc + */ +inline void JemallocProfileSvc::startProfiling() +{ + m_profiling = true; + info() << "Starting Jemalloc profile at event " + << m_eventNumber << endmsg; + mallctl("prof.dump", NULL, NULL, NULL, 0); +} + +/** + * Utility method to stop profiling with jemalloc + */ +inline void JemallocProfileSvc::stopProfiling() +{ + m_profiling = false; + dumpProfile(); +} + +/** + * Utility method to dump profile with jemalloc + */ +inline void JemallocProfileSvc::dumpProfile() +{ + info() << "Dumping Jemalloc profile at event " + << m_eventNumber << endmsg; + mallctl("prof.dump", NULL, NULL, NULL, 0); + +} + + +//============================================================================= +// Destructor + +//============================================================================= + JemallocProfileSvc::~JemallocProfileSvc() {} + +//============================================================================= +// Declaration of the factory +DECLARE_COMPONENT(JemallocProfileSvc) diff --git a/GaudiProfiling/src/component/jemalloc/JemallocProfileSvc.h b/GaudiProfiling/src/component/jemalloc/JemallocProfileSvc.h new file mode 100644 index 0000000000000000000000000000000000000000..2afff6ac61a5edcb193cf26ebff3260e0384f721 --- /dev/null +++ b/GaudiProfiling/src/component/jemalloc/JemallocProfileSvc.h @@ -0,0 +1,62 @@ +#ifndef JEMALLOC_JEMALLOCPROFILESVC_H +#define JEMALLOC_JEMALLOCPROFILESVC_H 1 + +// Include files +#include "GaudiKernel/Service.h" +#include "GaudiKernel/IIncidentListener.h" + +/** @class JemallocProfileSvc JemallocProfileSvc.h jemalloc/JemallocProfileSvc.h + * + * Service that enables the Jemalloc profiler on demand. + * By default, the profiling is diabled, the user needs + * to set incidents at which to start/stop the profiling, + * or specify incidents for that purpose. + * + * @author Ben Couturier + * @date 2016-01-12 + */ +class JemallocProfileSvc : public extends1<Service, IIncidentListener> { + +public: + /// Standard constructor + JemallocProfileSvc(const std::string &name, ISvcLocator *svcLoc); + + /// Initializer + StatusCode initialize(); + + /// Finalizer + StatusCode finalize(); + + // Handler for incident + void handle(const Incident &incident); + + ///< Destructor + virtual ~JemallocProfileSvc(); + +protected: + void handleBegin(); + void handleEnd(); + void startProfiling(); + void stopProfiling(); + void dumpProfile(); + + +private: + /// Start, End event and counter + int m_nStartFromEvent; // Event to start profiling at + int m_nStopAtEvent; // Event to stop profiling at + int m_dumpPeriod; // Period at which to dump the heap + int m_eventNumber; // Current event number + std::vector<std::string> m_startFromIncidents; // Incidents to use as trigger + std::vector<std::string> m_stopAtIncidents; // Incidents to use as trigger + bool m_hasStartIncident; + bool m_hasStopIncident; + + + /// Status of the profiling + bool m_profiling; + + /// Pointer to the incident service. + SmartIF<IIncidentSvc> m_incidentSvc; +}; +#endif // JEMALLOC_JEMALLOCPROFILESVC_H