CMS 3D CMS Logo

DependencyGraph.cc
Go to the documentation of this file.
1 /*
2  * Simple Service to make a GraphViz graph of the modules runtime dependencies:
3  * - 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.
6  *
7  * Use GraphViz dot to generate an SVG representation of the dependencies:
8  *
9  * dot -v -Tsvg dependency.dot -o dependency.svg
10  *
11  */
12 
13 #include <iostream>
14 #include <vector>
15 #include <string>
16 #include <type_traits>
17 
18 // boost optional (used by boost graph) results in some false positives with -Wmaybe-uninitialized
19 #pragma GCC diagnostic push
20 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
21 #include <boost/graph/adjacency_list.hpp>
22 #include <boost/graph/graphviz.hpp>
23 #include <boost/graph/lookup_edge.hpp>
24 #pragma GCC diagnostic pop
25 
37 
38 using namespace edm;
39 using namespace edm::service;
40 
41 namespace {
42  namespace {
43 
44  template <typename T>
45  std::unordered_set<T> make_unordered_set(std::vector<T> &&entries) {
46  std::unordered_set<T> u;
47  for (T &entry : entries)
48  u.insert(std::move(entry));
49  return u;
50  }
51 
52  } // namespace
53 } // namespace
54 
56 public:
58 
59  static void fillDescriptions(edm::ConfigurationDescriptions &descriptions);
60 
61  void preSourceConstruction(ModuleDescription const &);
62  void preBeginJob(PathsAndConsumesOfModulesBase const &, ProcessContext const &);
63  void postBeginJob();
64 
65 private:
66  bool highlighted(std::string const &module) { return (m_highlightModules.find(module) != m_highlightModules.end()); }
67 
68  enum class EDMModuleType { Unknown, Source, ESSource, ESProducer, EDAnalyzer, EDProducer, EDFilter, OutputModule };
69 
70  static constexpr const char *module_type_desc[]{
71  "Unknown", "Source", "ESSource", "ESProducer", "EDAnalyzer", "EDProducer", "EDFilter", "OutputModule"};
72 
73  static constexpr const char *shapes[]{
74  "note", // Unknown
75  "oval", // Source
76  "cylinder", // ESSource
77  "cylinder", // ESProducer
78  "oval", // EDAnalyzer
79  "box", // EDProducer
80  "diamond", // EDFilter
81  "oval", // OutputModule
82  };
83 
85 
86  static const char *edmModuleType(edm::ModuleDescription const &module);
87 
88  struct node {
91  unsigned int id;
93  bool scheduled;
94  };
95 
96  using GraphvizAttributes = std::map<std::string, std::string>;
97 
98  // directed graph, with `node` properties attached to each vertex
99  boost::subgraph<boost::adjacency_list<
100  // edge list
101  boost::vecS,
102  // vertex list
103  boost::vecS,
104  boost::directedS,
105  // vertex properties
106  boost::property<boost::vertex_attribute_t,
107  GraphvizAttributes, // Graphviz vertex attributes
108  node>,
109  // edge propoerties
110  boost::property<boost::edge_index_t,
111  int, // used internally by boost::subgraph
112  boost::property<boost::edge_attribute_t, GraphvizAttributes>>, // Graphviz edge attributes
113  // graph properties
114  boost::property<
115  boost::graph_name_t,
116  std::string, // name each boost::subgraph
117  boost::property<boost::graph_graph_attribute_t,
118  GraphvizAttributes, // Graphviz graph attributes
119  boost::property<boost::graph_vertex_attribute_t,
121  boost::property<boost::graph_edge_attribute_t, GraphvizAttributes>>>>>>
123 
125  std::unordered_set<std::string> m_highlightModules;
126 
129 };
130 
131 constexpr const char *DependencyGraph::module_type_desc[];
132 
133 constexpr const char *DependencyGraph::shapes[];
134 
136  auto const &registry = *edm::pset::Registry::instance();
137  auto const &pset = *registry.getMapped(module.parameterSetID());
138 
139  if (not pset.existsAs<std::string>("@module_edm_type"))
140  return EDMModuleType::Unknown;
141 
142  std::string const &t = pset.getParameter<std::string>("@module_edm_type");
144  EDMModuleType::ESSource,
146  EDMModuleType::EDAnalyzer,
147  EDMModuleType::EDProducer,
148  EDMModuleType::EDFilter,
149  EDMModuleType::OutputModule}) {
150  if (t == module_type_desc[static_cast<std::underlying_type_t<EDMModuleType>>(v)])
151  return v;
152  }
153  return EDMModuleType::Unknown;
154 }
155 
157  return module_type_desc[static_cast<std::underlying_type_t<EDMModuleType>>(edmModuleTypeEnum(module))];
158 }
159 
162  desc.addUntracked<std::string>("fileName", "dependency.dot");
163  desc.addUntracked<std::vector<std::string>>("highlightModules", {});
164  desc.addUntracked<bool>("showPathDependencies", true);
165  descriptions.add("DependencyGraph", desc);
166 }
167 
169  : m_filename(config.getUntrackedParameter<std::string>("fileName")),
170  m_highlightModules(
171  make_unordered_set(config.getUntrackedParameter<std::vector<std::string>>("highlightModules"))),
172  m_showPathDependencies(config.getUntrackedParameter<bool>("showPathDependencies")),
173  m_initialized(false) {
177 }
178 
179 // adaptor to use range-based for loops with boost::graph edges(...) and vertices(...) functions
180 template <typename I>
181 struct iterator_pair_as_a_range : std::pair<I, I> {
182 public:
183  using std::pair<I, I>::pair;
184 
185  I begin() { return this->first; }
186  I end() { return this->second; }
187 };
188 
189 template <typename I>
192 }
193 
195  // create graph vertex for the source module and fill its attributes
196  boost::add_vertex(m_graph);
197  m_graph.m_graph[module.id()] =
198  node{module.moduleLabel(), module.moduleName(), module.id(), EDMModuleType::Source, true};
199  auto &attributes = boost::get(boost::get(boost::vertex_attribute, m_graph), 0);
200  attributes["label"] = module.moduleLabel();
201  attributes["tooltip"] = module.moduleName();
202  attributes["shape"] = shapes[static_cast<std::underlying_type_t<EDMModuleType>>(EDMModuleType::Source)];
203  attributes["style"] = "filled";
204  attributes["color"] = "black";
205  attributes["fillcolor"] = highlighted(module.moduleLabel()) ? "lightgreen" : "white";
206 }
207 
209  ProcessContext const &context) {
210  // if the Service is not in the main Process do not do anything
211  if (context.isSubProcess() and not m_initialized) {
212  edm::LogError("DependencyGraph") << "You have requested an instance of the DependencyGraph Service in the \""
213  << context.processName()
214  << "\" SubProcess, which is not supported.\nPlease move it to the main process.";
215  return;
216  }
217 
218  if (not context.isSubProcess()) {
219  // set the graph name property to the process name
220  boost::get_property(m_graph, boost::graph_name) = context.processName();
221  boost::get_property(m_graph, boost::graph_graph_attribute)["label"] = "process " + context.processName();
222  boost::get_property(m_graph, boost::graph_graph_attribute)["labelloc"] = "top";
223 
224  // create graph vertices associated to all modules in the process
225  auto size = pathsAndConsumes.largestModuleID() - boost::num_vertices(m_graph) + 1;
226  for (size_t i = 0; i < size; ++i)
227  boost::add_vertex(m_graph);
228 
229  m_initialized = true;
230  } else {
231  // create a subgraph to match the subprocess
232  auto &graph = m_graph.create_subgraph();
233 
234  // set the subgraph name property to the subprocess name
235  boost::get_property(graph, boost::graph_name) = "cluster" + context.processName();
236  boost::get_property(graph, boost::graph_graph_attribute)["label"] = "subprocess " + context.processName();
237  boost::get_property(graph, boost::graph_graph_attribute)["labelloc"] = "top";
238 
239  // create graph vertices associated to all modules in the subprocess
240  auto size = pathsAndConsumes.largestModuleID() - boost::num_vertices(m_graph) + 1;
241  for (size_t i = 0; i < size; ++i)
242  boost::add_vertex(graph);
243  }
244 
245  // set the vertices properties (use the module id as the global index into the graph)
246  for (edm::ModuleDescription const *module : pathsAndConsumes.allModules()) {
247  m_graph.m_graph[module->id()] = {
248  module->moduleLabel(), module->moduleName(), module->id(), edmModuleTypeEnum(*module), false};
249 
250  auto &attributes = boost::get(boost::get(boost::vertex_attribute, m_graph), module->id());
251  attributes["label"] = module->moduleLabel();
252  attributes["tooltip"] = module->moduleName();
253  attributes["shape"] = shapes[static_cast<std::underlying_type_t<EDMModuleType>>(edmModuleTypeEnum(*module))];
254  attributes["style"] = "filled";
255  attributes["color"] = "black";
256  attributes["fillcolor"] = highlighted(module->moduleLabel()) ? "green" : "lightgrey";
257  }
258 
259  // paths and endpaths
260  auto const &paths = pathsAndConsumes.paths();
261  auto const &endps = pathsAndConsumes.endPaths();
262 
263  // add graph edges associated to module dependencies
264  for (edm::ModuleDescription const *consumer : pathsAndConsumes.allModules()) {
265  for (edm::ModuleDescription const *module : pathsAndConsumes.modulesWhoseProductsAreConsumedBy(consumer->id())) {
266  edm::LogInfo("DependencyGraph") << "module " << consumer->moduleLabel() << " depends on module "
267  << module->moduleLabel();
268  auto edge_status = boost::add_edge(consumer->id(), module->id(), m_graph);
269  // highlight the arrow between highlighted nodes
270  if (highlighted(module->moduleLabel()) and highlighted(consumer->moduleLabel())) {
271  auto const &edge = edge_status.first;
272  auto &attributes = boost::get(boost::get(boost::edge_attribute, m_graph), edge);
273  attributes["color"] = "darkgreen";
274  }
275  }
276  }
277 
278  // marke the modules in the paths as scheduled, and add a soft dependency to reflect the order of modules along each path
280  for (unsigned int i = 0; i < paths.size(); ++i) {
281  previous = nullptr;
282  for (edm::ModuleDescription const *module : pathsAndConsumes.modulesOnPath(i)) {
283  m_graph.m_graph[module->id()].scheduled = true;
284  auto &attributes = boost::get(boost::get(boost::vertex_attribute, m_graph), module->id());
285  attributes["fillcolor"] = highlighted(module->moduleLabel()) ? "lightgreen" : "white";
287  edm::LogInfo("DependencyGraph") << "module " << module->moduleLabel() << " follows module "
288  << previous->moduleLabel() << " in Path " << i;
289  auto edge_status = boost::lookup_edge(module->id(), previous->id(), m_graph);
290  bool found = edge_status.second;
291  if (not found) {
292  edge_status = boost::add_edge(module->id(), previous->id(), m_graph);
293  auto const &edge = edge_status.first;
294  auto &edgeAttributes = boost::get(boost::get(boost::edge_attribute, m_graph), edge);
295  edgeAttributes["style"] = "dashed";
296  // highlight the arrow between highlighted nodes
297  if (highlighted(module->moduleLabel()) and highlighted(previous->moduleLabel()))
298  edgeAttributes["color"] = "darkgreen";
299  }
300  }
301  previous = module;
302  }
303  }
304  for (unsigned int i = 0; i < endps.size(); ++i) {
305  previous = nullptr;
306  for (edm::ModuleDescription const *module : pathsAndConsumes.modulesOnEndPath(i)) {
307  m_graph.m_graph[module->id()].scheduled = true;
308  auto &attributes = boost::get(boost::get(boost::vertex_attribute, m_graph), module->id());
309  attributes["fillcolor"] = highlighted(module->moduleLabel()) ? "lightgreen" : "white";
311  edm::LogInfo("DependencyGraph") << "module " << module->moduleLabel() << " follows module "
312  << previous->moduleLabel() << " in EndPath " << i;
313  auto edge_status = boost::lookup_edge(module->id(), previous->id(), m_graph);
314  bool found = edge_status.second;
315  if (not found) {
316  edge_status = boost::add_edge(module->id(), previous->id(), m_graph);
317  auto const &edge = edge_status.first;
318  auto &edgeAttributes = boost::get(boost::get(boost::edge_attribute, m_graph), edge);
319  edgeAttributes["style"] = "dashed";
320  // highlight the arrow between highlighted nodes
321  if (highlighted(module->moduleLabel()) and highlighted(previous->moduleLabel()))
322  edgeAttributes["color"] = "darkgreen";
323  }
324  }
325  previous = module;
326  }
327  }
328 }
329 
331  if (not m_initialized)
332  return;
333 
334  // draw the dependency graph
335  std::ofstream out(m_filename);
336  boost::write_graphviz(out, m_graph);
337  out.close();
338 }
339 
340 namespace edm {
341  namespace service {
342 
343  inline bool isProcessWideService(DependencyGraph const *) { return true; }
344 
345  } // namespace service
346 } // namespace edm
347 
348 // define as a framework servie
ConfigurationDescriptions.h
edm::pset::Registry::instance
static Registry * instance()
Definition: Registry.cc:12
service
Definition: service.py:1
mixOne_cfi.ESProducer
ESProducer
Definition: mixOne_cfi.py:33
electrons_cff.bool
bool
Definition: electrons_cff.py:366
mps_fire.i
i
Definition: mps_fire.py:428
MessageLogger.h
funct::false
false
Definition: Factorize.h:29
iterator_pair_as_a_range
Definition: DependencyGraph.cc:181
DependencyGraph::node
Definition: DependencyGraph.cc:88
DependencyGraph::m_filename
std::string m_filename
Definition: DependencyGraph.cc:124
edm
HLT enums.
Definition: AlignableModifier.h:19
mps_splice.entry
entry
Definition: mps_splice.py:68
edm::ProcessContext
Definition: ProcessContext.h:27
DependencyGraph::m_showPathDependencies
bool m_showPathDependencies
Definition: DependencyGraph.cc:127
edm::ParameterSetDescription
Definition: ParameterSetDescription.h:52
DependencyGraph::highlighted
bool highlighted(std::string const &module)
Definition: DependencyGraph.cc:66
DEFINE_FWK_SERVICE
#define DEFINE_FWK_SERVICE(type)
Definition: ServiceMaker.h:96
callgraph.previous
previous
Definition: callgraph.py:95
DependencyGraph::EDMModuleType::Source
edm::second
U second(std::pair< T, U > const &p)
Definition: ParameterSet.cc:222
reco::Unknown
Definition: MuonSimInfo.h:32
iterator_pair_as_a_range::end
I end()
Definition: DependencyGraph.cc:186
DependencyGraph::node::type
EDMModuleType type
Definition: DependencyGraph.cc:92
edm::service::isProcessWideService
bool isProcessWideService(TFileService const *)
Definition: TFileService.h:98
edm::LogInfo
Log< level::Info, false > LogInfo
Definition: MessageLogger.h:125
findQualityFiles.v
v
Definition: findQualityFiles.py:179
newFWLiteAna.found
found
Definition: newFWLiteAna.py:118
edm::ModuleDescription
Definition: ModuleDescription.h:21
edm::EDAnalyzer
Definition: EDAnalyzer.h:28
edm::PathsAndConsumesOfModulesBase::largestModuleID
unsigned int largestModuleID() const
Definition: PathsAndConsumesOfModulesBase.h:78
ModuleDescription.h
ActivityRegistry.h
visDQMUpload.context
context
Definition: visDQMUpload.py:37
config
Definition: config.py:1
DependencyGraph
Definition: DependencyGraph.cc:55
DependencyGraph::fillDescriptions
static void fillDescriptions(edm::ConfigurationDescriptions &descriptions)
Definition: DependencyGraph.cc:160
Exhume::I
const std::complex< double > I
Definition: I.h:8
edm::ConfigurationDescriptions::add
void add(std::string const &label, ParameterSetDescription const &psetDescription)
Definition: ConfigurationDescriptions.cc:57
edm::ActivityRegistry::watchPreBeginJob
void watchPreBeginJob(PreBeginJob::slot_type const &iSlot)
convenience function for attaching to signal
Definition: ActivityRegistry.h:151
DependencyGraph::node::id
unsigned int id
Definition: DependencyGraph.cc:91
fillDescriptions
static void fillDescriptions(edm::ConfigurationDescriptions &descriptions)
edm::ActivityRegistry
Definition: ActivityRegistry.h:134
DependencyGraph::preBeginJob
void preBeginJob(PathsAndConsumesOfModulesBase const &, ProcessContext const &)
Definition: DependencyGraph.cc:208
DependencyGraph::GraphvizAttributes
std::map< std::string, std::string > GraphvizAttributes
Definition: DependencyGraph.cc:96
edm::ActivityRegistry::watchPostBeginJob
void watchPostBeginJob(PostBeginJob::slot_type const &iSlot)
convenience function for attaching to signal
Definition: ActivityRegistry.h:158
ParameterSetDescription.h
DependencyGraph::node::scheduled
bool scheduled
Definition: DependencyGraph.cc:93
edm::ActivityRegistry::watchPreSourceConstruction
void watchPreSourceConstruction(PreSourceConstruction::slot_type const &iSlot)
Definition: ActivityRegistry.h:1128
first
auto first
Definition: CAHitNtupletGeneratorKernelsImpl.h:125
ServiceMaker.h
edm::module_type_desc
constexpr const char * module_type_desc[]
Definition: EDMModuleType.h:19
DependencyGraph::postBeginJob
void postBeginJob()
Definition: DependencyGraph.cc:330
edm::ConfigurationDescriptions
Definition: ConfigurationDescriptions.h:28
iterator_pair_as_a_range::begin
I begin()
Definition: DependencyGraph.cc:185
edm::EDMModuleType
EDMModuleType
Definition: EDMModuleType.h:8
edm::ParameterSet
Definition: ParameterSet.h:47
edm::PathsAndConsumesOfModulesBase::endPaths
std::vector< std::string > const & endPaths() const
Definition: PathsAndConsumesOfModulesBase.h:40
AlCaHLTBitMon_ParallelJobs.p
def p
Definition: AlCaHLTBitMon_ParallelJobs.py:153
createfilelist.int
int
Definition: createfilelist.py:10
edm::PathsAndConsumesOfModulesBase::modulesOnPath
std::vector< ModuleDescription const * > const & modulesOnPath(unsigned int pathIndex) const
Definition: PathsAndConsumesOfModulesBase.h:46
DependencyGraph::m_highlightModules
std::unordered_set< std::string > m_highlightModules
Definition: DependencyGraph.cc:125
edm::PathsAndConsumesOfModulesBase::modulesWhoseProductsAreConsumedBy
std::vector< ModuleDescription const * > const & modulesWhoseProductsAreConsumedBy(unsigned int moduleID, BranchType branchType=InEvent) const
Definition: PathsAndConsumesOfModulesBase.h:64
trackerHitRTTI::vector
Definition: trackerHitRTTI.h:21
DependencyGraph::m_initialized
bool m_initialized
Definition: DependencyGraph.cc:128
edm::EDFilter
Definition: EDFilter.h:38
edm::service
Definition: TFileService.h:95
edm::LogError
Log< level::Error, false > LogError
Definition: MessageLogger.h:123
DependencyGraph::node::class_
std::string class_
Definition: DependencyGraph.cc:90
get
#define get
AlCaHLTBitMon_QueryRunRegistry.string
string string
Definition: AlCaHLTBitMon_QueryRunRegistry.py:256
callgraph.module
module
Definition: callgraph.py:61
Registry.h
edm::edmModuleTypeEnum
EDMModuleType edmModuleTypeEnum(edm::ModuleDescription const &module)
Definition: EDMModuleType.cc:10
DependencyGraph::preSourceConstruction
void preSourceConstruction(ModuleDescription const &)
Definition: DependencyGraph.cc:194
submitPVResolutionJobs.desc
string desc
Definition: submitPVResolutionJobs.py:251
DependencyGraph::node::label
std::string label
Definition: DependencyGraph.cc:89
eostools.move
def move(src, dest)
Definition: eostools.py:511
std
Definition: JetResolutionObject.h:76
DependencyGraph::shapes
static constexpr const char * shapes[]
Definition: DependencyGraph.cc:73
DependencyGraph::module_type_desc
static constexpr const char * module_type_desc[]
Definition: DependencyGraph.cc:70
edm::PathsAndConsumesOfModulesBase::paths
std::vector< std::string > const & paths() const
Definition: PathsAndConsumesOfModulesBase.h:39
edm::PathsAndConsumesOfModulesBase
Definition: PathsAndConsumesOfModulesBase.h:35
T
long double T
Definition: Basic3DVectorLD.h:48
edm::edmModuleType
const char * edmModuleType(edm::ModuleDescription const &module)
Definition: EDMModuleType.cc:31
DependencyGraph::m_graph
boost::subgraph< boost::adjacency_list< boost::vecS, boost::vecS, boost::directedS, boost::property< boost::vertex_attribute_t, GraphvizAttributes, node >, boost::property< boost::edge_index_t, int, boost::property< boost::edge_attribute_t, GraphvizAttributes > >, boost::property< boost::graph_name_t, std::string, boost::property< boost::graph_graph_attribute_t, GraphvizAttributes, boost::property< boost::graph_vertex_attribute_t, GraphvizAttributes, boost::property< boost::graph_edge_attribute_t, GraphvizAttributes > > > > > > m_graph
Definition: DependencyGraph.cc:122
DependencyGraph::edmModuleType
static const char * edmModuleType(edm::ModuleDescription const &module)
Definition: DependencyGraph.cc:156
make_range
iterator_pair_as_a_range< I > make_range(std::pair< I, I > p)
Definition: DependencyGraph.cc:190
edm::EDProducer
Definition: EDProducer.h:35
Skims_PA_cff.paths
paths
Definition: Skims_PA_cff.py:18
Exception.h
OfflineOutput_cfi.consumer
consumer
Definition: OfflineOutput_cfi.py:3
edm::PathsAndConsumesOfModulesBase::modulesOnEndPath
std::vector< ModuleDescription const * > const & modulesOnEndPath(unsigned int endPathIndex) const
Definition: PathsAndConsumesOfModulesBase.h:50
MillePedeFileConverter_cfg.out
out
Definition: MillePedeFileConverter_cfg.py:31
DependencyGraph::edmModuleTypeEnum
static EDMModuleType edmModuleTypeEnum(edm::ModuleDescription const &module)
Definition: DependencyGraph.cc:135
ParameterSet.h
ProcessContext.h
PathsAndConsumesOfModulesBase.h
submitPVValidationJobs.t
string t
Definition: submitPVValidationJobs.py:644
ConsumesInfo.h
edm::PathsAndConsumesOfModulesBase::allModules
std::vector< ModuleDescription const * > const & allModules() const
Definition: PathsAndConsumesOfModulesBase.h:42
DependencyGraph::DependencyGraph
DependencyGraph(const ParameterSet &, ActivityRegistry &)
Definition: DependencyGraph.cc:168
muonDTDigis_cfi.pset
pset
Definition: muonDTDigis_cfi.py:27
findQualityFiles.size
size
Write out results.
Definition: findQualityFiles.py:443
CollectionTags_cfi.Source
Source
Definition: CollectionTags_cfi.py:11
DependencyGraph::EDMModuleType
EDMModuleType
Definition: DependencyGraph.cc:68