11/*
22 * Simple Service to make a GraphViz graph of the modules runtime dependencies:
33 * - draw hard dependencies according to the "consumes" dependencies;
4- * - draw soft dependencies to reflect the order of scheduled modue in each path;
5- * - draw SubProcesses in subgraphs.
4+ * (only event dependences included, not run/lumi/process block dependences)
5+ * - draw soft dependencies to reflect the order of scheduled modules in each path;
66 *
77 * Use GraphViz dot to generate an SVG representation of the dependencies:
88 *
99 * dot -v -Tsvg dependency.dot -o dependency.svg
1010 *
1111 */
1212
13- #include < iostream>
14- #include < vector>
13+ #include < cstddef>
14+ #include < fstream>
15+ #include < limits>
16+ #include < map>
1517#include < string>
1618#include < type_traits>
19+ #include < unordered_set>
20+ #include < utility>
21+ #include < vector>
1722
1823// boost optional (used by boost graph) results in some false positives with -Wmaybe-uninitialized
1924#pragma GCC diagnostic push
3136#include " FWCore/ServiceRegistry/interface/ActivityRegistry.h"
3237#include " FWCore/ServiceRegistry/interface/PathsAndConsumesOfModulesBase.h"
3338#include " FWCore/ServiceRegistry/interface/ProcessContext.h"
34- #include " FWCore/Utilities/interface/Exception.h"
3539#include " FWCore/MessageLogger/interface/MessageLogger.h"
3640
3741using namespace edm ;
@@ -40,6 +44,8 @@ using namespace edm::service;
4044namespace {
4145 namespace {
4246
47+ constexpr std::size_t kInvalidVertex = std::numeric_limits<std::size_t >::max();
48+
4349 template <typename T>
4450 std::unordered_set<T> make_unordered_set (std::vector<T> &&entries) {
4551 std::unordered_set<T> u;
@@ -55,7 +61,7 @@ class DependencyGraph {
5561public:
5662 DependencyGraph (const ParameterSet &, ActivityRegistry &);
5763
58- static void fillDescriptions (edm::ConfigurationDescriptions &descriptions );
64+ static void fillDescriptions (edm::ConfigurationDescriptions &);
5965
6066 void preSourceConstruction (ModuleDescription const &);
6167 void lookupInitializationComplete (PathsAndConsumesOfModulesBase const &, ProcessContext const &);
@@ -123,7 +129,9 @@ class DependencyGraph {
123129 std::unordered_set<std::string> m_highlightModules;
124130
125131 bool m_showPathDependencies;
126- bool m_initialized;
132+
133+ std::vector<std::size_t > m_moduleIDToGraphIndex;
134+ std::size_t m_nextGraphIndexToAdd = 0 ;
127135};
128136
129137constexpr const char *DependencyGraph::module_type_desc[];
@@ -167,8 +175,7 @@ DependencyGraph::DependencyGraph(ParameterSet const &config, ActivityRegistry &r
167175 : m_filename(config.getUntrackedParameter<std::string>(" fileName" )),
168176 m_highlightModules(
169177 make_unordered_set (config.getUntrackedParameter<std::vector<std::string>>(" highlightModules" ))),
170- m_showPathDependencies(config.getUntrackedParameter<bool >(" showPathDependencies" )),
171- m_initialized(false ) {
178+ m_showPathDependencies(config.getUntrackedParameter<bool >(" showPathDependencies" )) {
172179 registry.watchPreSourceConstruction (this , &DependencyGraph::preSourceConstruction);
173180 registry.watchLookupInitializationComplete (this , &DependencyGraph::lookupInitializationComplete);
174181}
@@ -189,11 +196,19 @@ iterator_pair_as_a_range<I> make_range(std::pair<I, I> p) {
189196}
190197
191198void DependencyGraph::preSourceConstruction (ModuleDescription const &module ) {
192- // create graph vertex for the source module and fill its attributes
199+ if (module .id () >= m_moduleIDToGraphIndex.size ()) {
200+ m_moduleIDToGraphIndex.resize (module .id () + 1 , kInvalidVertex );
201+ }
202+ m_moduleIDToGraphIndex[module .id ()] = m_nextGraphIndexToAdd;
203+ ++m_nextGraphIndexToAdd;
204+
205+ auto graphIndex = m_moduleIDToGraphIndex[module .id ()];
206+
207+ // Create graph vertex for the source module and fill its attributes
193208 boost::add_vertex (m_graph);
194- m_graph.m_graph [module . id () ] =
209+ m_graph.m_graph [graphIndex ] =
195210 node{module .moduleLabel (), module .moduleName (), module .id (), EDMModuleType::Source, true };
196- auto &attributes = boost::get (boost::get (boost::vertex_attribute, m_graph), 0 );
211+ auto &attributes = boost::get (boost::get (boost::vertex_attribute, m_graph), graphIndex );
197212 attributes[" label" ] = module .moduleLabel ();
198213 attributes[" tooltip" ] = module .moduleName ();
199214 attributes[" shape" ] = shapes[static_cast <std::underlying_type_t <EDMModuleType>>(EDMModuleType::Source)];
@@ -204,47 +219,38 @@ void DependencyGraph::preSourceConstruction(ModuleDescription const &module) {
204219
205220void DependencyGraph::lookupInitializationComplete (PathsAndConsumesOfModulesBase const &pathsAndConsumes,
206221 ProcessContext const &context) {
207- // if the Service is not in the main Process do not do anything
208- if (context.isSubProcess () and not m_initialized) {
209- edm::LogError (" DependencyGraph" ) << " You have requested an instance of the DependencyGraph Service in the \" "
210- << context.processName ()
211- << " \" SubProcess, which is not supported.\n Please move it to the main process." ;
212- return ;
213- }
214-
215- if (not context.isSubProcess ()) {
216- // set the graph name property to the process name
217- boost::get_property (m_graph, boost::graph_name) = context.processName ();
218- boost::get_property (m_graph, boost::graph_graph_attribute)[" label" ] = " process " + context.processName ();
219- boost::get_property (m_graph, boost::graph_graph_attribute)[" labelloc" ] = " top" ;
222+ // set the graph name property to the process name
223+ boost::get_property (m_graph, boost::graph_name) = context.processName ();
224+ boost::get_property (m_graph, boost::graph_graph_attribute)[" label" ] = " process " + context.processName ();
225+ boost::get_property (m_graph, boost::graph_graph_attribute)[" labelloc" ] = " top" ;
220226
221- // create graph vertices associated to all modules in the process
222- auto size = pathsAndConsumes.largestModuleID () - boost::num_vertices (m_graph) + 1 ;
223- for (size_t i = 0 ; i < size; ++i)
224- boost::add_vertex (m_graph);
225-
226- m_initialized = true ;
227- } else {
228- // create a subgraph to match the subprocess
229- auto &graph = m_graph.create_subgraph ();
230-
231- // set the subgraph name property to the subprocess name
232- boost::get_property (graph, boost::graph_name) = " cluster" + context.processName ();
233- boost::get_property (graph, boost::graph_graph_attribute)[" label" ] = " subprocess " + context.processName ();
234- boost::get_property (graph, boost::graph_graph_attribute)[" labelloc" ] = " top" ;
227+ if (pathsAndConsumes.largestModuleID () >= m_moduleIDToGraphIndex.size ()) {
228+ m_moduleIDToGraphIndex.resize (pathsAndConsumes.largestModuleID () + 1 , kInvalidVertex );
229+ }
235230
236- // create graph vertices associated to all modules in the subprocess
237- auto size = pathsAndConsumes.largestModuleID () - boost::num_vertices (m_graph) + 1 ;
238- for (size_t i = 0 ; i < size; ++i)
239- boost::add_vertex (graph);
231+ auto numberOfSourceGraphVertexes = m_nextGraphIndexToAdd;
232+ for (edm::ModuleDescription const *module : pathsAndConsumes.allModules ()) {
233+ m_moduleIDToGraphIndex[module ->id ()] = m_nextGraphIndexToAdd;
234+ ++m_nextGraphIndexToAdd;
235+ }
236+ auto numberOfGraphVertexes = m_nextGraphIndexToAdd;
237+
238+ // Create graph vertices associated to all modules in the process.
239+ // Note that we skip over source vertexes that were already made
240+ // (currently always 1 source but possibly in the future there
241+ // might be more than 1 source)
242+ for (std::size_t i = numberOfSourceGraphVertexes; i < numberOfGraphVertexes; ++i) {
243+ boost::add_vertex (m_graph);
240244 }
241245
242246 // set the vertices properties (use the module id as the global index into the graph)
243247 for (edm::ModuleDescription const *module : pathsAndConsumes.allModules ()) {
244- m_graph.m_graph [module ->id ()] = {
248+ auto graphIndex = m_moduleIDToGraphIndex[module ->id ()];
249+
250+ m_graph.m_graph [graphIndex] = {
245251 module ->moduleLabel (), module ->moduleName (), module ->id (), edmModuleTypeEnum (*module ), false };
246252
247- auto &attributes = boost::get (boost::get (boost::vertex_attribute, m_graph), module -> id () );
253+ auto &attributes = boost::get (boost::get (boost::vertex_attribute, m_graph), graphIndex );
248254 attributes[" label" ] = module ->moduleLabel ();
249255 attributes[" tooltip" ] = module ->moduleName ();
250256 attributes[" shape" ] = shapes[static_cast <std::underlying_type_t <EDMModuleType>>(edmModuleTypeEnum (*module ))];
@@ -262,7 +268,8 @@ void DependencyGraph::lookupInitializationComplete(PathsAndConsumesOfModulesBase
262268 for (edm::ModuleDescription const *module : pathsAndConsumes.modulesWhoseProductsAreConsumedBy (consumer->id ())) {
263269 edm::LogInfo (" DependencyGraph" ) << " module " << consumer->moduleLabel () << " depends on module "
264270 << module ->moduleLabel ();
265- auto edge_status = boost::add_edge (consumer->id (), module ->id (), m_graph);
271+ auto edge_status =
272+ boost::add_edge (m_moduleIDToGraphIndex[consumer->id ()], m_moduleIDToGraphIndex[module ->id ()], m_graph);
266273 // highlight the edge between highlighted nodes
267274 if (highlighted (module ->moduleLabel ()) and highlighted (consumer->moduleLabel ())) {
268275 auto const &edge = edge_status.first ;
@@ -284,7 +291,7 @@ void DependencyGraph::lookupInitializationComplete(PathsAndConsumesOfModulesBase
284291
285292 // add to the subgraph the node corresponding to the scheduled modules on the Path
286293 for (edm::ModuleDescription const *module : pathsAndConsumes.modulesOnPath (i)) {
287- boost::add_vertex (module ->id (), graph);
294+ boost::add_vertex (m_moduleIDToGraphIndex[ module ->id ()] , graph);
288295 }
289296 }
290297 for (unsigned int i = 0 ; i < endps.size (); ++i) {
@@ -298,7 +305,7 @@ void DependencyGraph::lookupInitializationComplete(PathsAndConsumesOfModulesBase
298305
299306 // add to the subgraph the node corresponding to the scheduled modules on the EndPath
300307 for (edm::ModuleDescription const *module : pathsAndConsumes.modulesOnEndPath (i)) {
301- boost::add_vertex (module ->id (), graph);
308+ boost::add_vertex (m_moduleIDToGraphIndex[ module ->id ()] , graph);
302309 }
303310 }
304311
@@ -319,16 +326,18 @@ void DependencyGraph::lookupInitializationComplete(PathsAndConsumesOfModulesBase
319326 for (unsigned int i = 0 ; i < paths.size (); ++i) {
320327 previous = nullptr ;
321328 for (edm::ModuleDescription const *module : pathsAndConsumes.modulesOnPath (i)) {
322- m_graph.m_graph [module ->id ()].scheduled = true ;
323- auto &attributes = boost::get (boost::get (boost::vertex_attribute, m_graph), module ->id ());
329+ auto graphIndex = m_moduleIDToGraphIndex[module ->id ()];
330+ m_graph.m_graph [graphIndex].scheduled = true ;
331+ auto &attributes = boost::get (boost::get (boost::vertex_attribute, m_graph), graphIndex);
324332 attributes[" fillcolor" ] = highlighted (module ->moduleLabel ()) ? " lightgreen" : " white" ;
325333 if (previous and m_showPathDependencies) {
334+ auto previousGraphIndex = m_moduleIDToGraphIndex[previous->id ()];
326335 edm::LogInfo (" DependencyGraph" ) << " module " << module ->moduleLabel () << " follows module "
327336 << previous->moduleLabel () << " in Path " << paths[i];
328- auto edge_status = boost::lookup_edge (module -> id (), previous-> id () , m_graph);
337+ auto edge_status = boost::lookup_edge (graphIndex, previousGraphIndex , m_graph);
329338 bool found = edge_status.second ;
330339 if (not found) {
331- edge_status = boost::add_edge (module -> id (), previous-> id () , m_graph);
340+ edge_status = boost::add_edge (graphIndex, previousGraphIndex , m_graph);
332341 auto const &edge = edge_status.first ;
333342 auto &edgeAttributes = boost::get (boost::get (boost::edge_attribute, m_graph), edge);
334343 edgeAttributes[" style" ] = " dashed" ;
@@ -347,7 +356,7 @@ void DependencyGraph::lookupInitializationComplete(PathsAndConsumesOfModulesBase
347356 edm::LogInfo (" DependencyGraph" ) << " module " << paths[i] << " implicitly follows module "
348357 << previous->moduleLabel () << " in Path " << paths[i];
349358 // add an edge from the PathStatusInserter module to the last module scheduled on the path
350- auto edge_status = boost::add_edge (j, previous->id (), m_graph);
359+ auto edge_status = boost::add_edge (j, m_moduleIDToGraphIndex[ previous->id ()] , m_graph);
351360 auto const &edge = edge_status.first ;
352361 auto &edgeAttributes = boost::get (boost::get (boost::edge_attribute, m_graph), edge);
353362 edgeAttributes[" style" ] = " dashed" ;
@@ -375,16 +384,18 @@ void DependencyGraph::lookupInitializationComplete(PathsAndConsumesOfModulesBase
375384 for (unsigned int i = 0 ; i < endps.size (); ++i) {
376385 previous = nullptr ;
377386 for (edm::ModuleDescription const *module : pathsAndConsumes.modulesOnEndPath (i)) {
378- m_graph.m_graph [module ->id ()].scheduled = true ;
379- auto &attributes = boost::get (boost::get (boost::vertex_attribute, m_graph), module ->id ());
387+ auto graphIndex = m_moduleIDToGraphIndex[module ->id ()];
388+ m_graph.m_graph [graphIndex].scheduled = true ;
389+ auto &attributes = boost::get (boost::get (boost::vertex_attribute, m_graph), graphIndex);
380390 attributes[" fillcolor" ] = highlighted (module ->moduleLabel ()) ? " lightgreen" : " white" ;
381391 if (previous and m_showPathDependencies) {
392+ auto previousGraphIndex = m_moduleIDToGraphIndex[previous->id ()];
382393 edm::LogInfo (" DependencyGraph" ) << " module " << module ->moduleLabel () << " follows module "
383394 << previous->moduleLabel () << " in EndPath " << i;
384- auto edge_status = boost::lookup_edge (module -> id (), previous-> id () , m_graph);
395+ auto edge_status = boost::lookup_edge (graphIndex, previousGraphIndex , m_graph);
385396 bool found = edge_status.second ;
386397 if (not found) {
387- edge_status = boost::add_edge (module -> id (), previous-> id () , m_graph);
398+ edge_status = boost::add_edge (graphIndex, previousGraphIndex , m_graph);
388399 auto const &edge = edge_status.first ;
389400 auto &edgeAttributes = boost::get (boost::get (boost::edge_attribute, m_graph), edge);
390401 edgeAttributes[" style" ] = " dashed" ;
@@ -397,26 +408,12 @@ void DependencyGraph::lookupInitializationComplete(PathsAndConsumesOfModulesBase
397408 }
398409 }
399410
400- // remove the nodes corresponding to the modules that have been removed from the process
401- for (int i = boost::num_vertices (m_graph) - 1 ; i > 1 ; --i) {
402- if (m_graph.m_graph [i].label .empty ())
403- boost::remove_vertex (i, m_graph.m_graph );
404- }
405-
406411 // draw the dependency graph
407412 std::ofstream out (m_filename);
408413 boost::write_graphviz (out, m_graph);
409414 out.close ();
410415}
411416
412- namespace edm {
413- namespace service {
414-
415- inline bool isProcessWideService (DependencyGraph const *) { return true ; }
416-
417- } // namespace service
418- } // namespace edm
419-
420417// define as a framework servie
421418#include " FWCore/ServiceRegistry/interface/ServiceMaker.h"
422419DEFINE_FWK_SERVICE (DependencyGraph);
0 commit comments