File indexing completed on 2023-12-09 05:44:43
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #include <iostream>
0014 #include <vector>
0015 #include <string>
0016 #include <type_traits>
0017
0018
0019 #pragma GCC diagnostic push
0020 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
0021 #include <boost/graph/adjacency_list.hpp>
0022 #include <boost/graph/graphviz.hpp>
0023 #include <boost/graph/lookup_edge.hpp>
0024 #pragma GCC diagnostic pop
0025
0026 #include "DataFormats/Provenance/interface/ModuleDescription.h"
0027 #include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h"
0028 #include "FWCore/ParameterSet/interface/ParameterSet.h"
0029 #include "FWCore/ParameterSet/interface/ParameterSetDescription.h"
0030 #include "FWCore/ParameterSet/interface/Registry.h"
0031 #include "FWCore/ServiceRegistry/interface/ActivityRegistry.h"
0032 #include "FWCore/ServiceRegistry/interface/ConsumesInfo.h"
0033 #include "FWCore/ServiceRegistry/interface/PathsAndConsumesOfModulesBase.h"
0034 #include "FWCore/ServiceRegistry/interface/ProcessContext.h"
0035 #include "FWCore/Utilities/interface/Exception.h"
0036 #include "FWCore/MessageLogger/interface/MessageLogger.h"
0037
0038 using namespace edm;
0039 using namespace edm::service;
0040
0041 namespace {
0042 namespace {
0043
0044 template <typename T>
0045 std::unordered_set<T> make_unordered_set(std::vector<T> &&entries) {
0046 std::unordered_set<T> u;
0047 for (T &entry : entries)
0048 u.insert(std::move(entry));
0049 return u;
0050 }
0051
0052 }
0053 }
0054
0055 class DependencyGraph {
0056 public:
0057 DependencyGraph(const ParameterSet &, ActivityRegistry &);
0058
0059 static void fillDescriptions(edm::ConfigurationDescriptions &descriptions);
0060
0061 void preSourceConstruction(ModuleDescription const &);
0062 void preBeginJob(PathsAndConsumesOfModulesBase const &, ProcessContext const &);
0063 void postBeginJob();
0064
0065 private:
0066 bool highlighted(std::string const &module) { return (m_highlightModules.find(module) != m_highlightModules.end()); }
0067
0068 enum class EDMModuleType { Unknown, Source, ESSource, ESProducer, EDAnalyzer, EDProducer, EDFilter, OutputModule };
0069
0070 static constexpr const char *module_type_desc[]{
0071 "Unknown", "Source", "ESSource", "ESProducer", "EDAnalyzer", "EDProducer", "EDFilter", "OutputModule"};
0072
0073 static constexpr const char *shapes[]{
0074 "note",
0075 "oval",
0076 "cylinder",
0077 "cylinder",
0078 "oval",
0079 "box",
0080 "diamond",
0081 "oval",
0082 };
0083
0084 static EDMModuleType edmModuleTypeEnum(edm::ModuleDescription const &module);
0085
0086 static const char *edmModuleType(edm::ModuleDescription const &module);
0087
0088 struct node {
0089 std::string label;
0090 std::string class_;
0091 unsigned int id;
0092 EDMModuleType type;
0093 bool scheduled;
0094 };
0095
0096 using GraphvizAttributes = std::map<std::string, std::string>;
0097
0098
0099 using GraphType = boost::subgraph<boost::adjacency_list<
0100
0101 boost::vecS,
0102
0103 boost::vecS,
0104 boost::directedS,
0105
0106 boost::property<boost::vertex_attribute_t,
0107 GraphvizAttributes,
0108 node>,
0109
0110 boost::property<boost::edge_index_t,
0111 int,
0112 boost::property<boost::edge_attribute_t, GraphvizAttributes>>,
0113
0114 boost::property<
0115 boost::graph_name_t,
0116 std::string,
0117 boost::property<boost::graph_graph_attribute_t,
0118 GraphvizAttributes,
0119 boost::property<boost::graph_vertex_attribute_t,
0120 GraphvizAttributes,
0121 boost::property<boost::graph_edge_attribute_t, GraphvizAttributes>>>>>>;
0122 GraphType m_graph;
0123
0124 std::string m_filename;
0125 std::unordered_set<std::string> m_highlightModules;
0126
0127 bool m_showPathDependencies;
0128 bool m_initialized;
0129 };
0130
0131 constexpr const char *DependencyGraph::module_type_desc[];
0132
0133 constexpr const char *DependencyGraph::shapes[];
0134
0135 DependencyGraph::EDMModuleType DependencyGraph::edmModuleTypeEnum(edm::ModuleDescription const &module) {
0136 auto const ®istry = *edm::pset::Registry::instance();
0137 auto const &pset = *registry.getMapped(module.parameterSetID());
0138
0139 if (not pset.existsAs<std::string>("@module_edm_type"))
0140 return EDMModuleType::Unknown;
0141
0142 std::string const &t = pset.getParameter<std::string>("@module_edm_type");
0143 for (EDMModuleType v : {EDMModuleType::Source,
0144 EDMModuleType::ESSource,
0145 EDMModuleType::ESProducer,
0146 EDMModuleType::EDAnalyzer,
0147 EDMModuleType::EDProducer,
0148 EDMModuleType::EDFilter,
0149 EDMModuleType::OutputModule}) {
0150 if (t == module_type_desc[static_cast<std::underlying_type_t<EDMModuleType>>(v)])
0151 return v;
0152 }
0153 return EDMModuleType::Unknown;
0154 }
0155
0156 const char *DependencyGraph::edmModuleType(edm::ModuleDescription const &module) {
0157 return module_type_desc[static_cast<std::underlying_type_t<EDMModuleType>>(edmModuleTypeEnum(module))];
0158 }
0159
0160 void DependencyGraph::fillDescriptions(edm::ConfigurationDescriptions &descriptions) {
0161 edm::ParameterSetDescription desc;
0162 desc.addUntracked<std::string>("fileName", "dependency.dot");
0163 desc.addUntracked<std::vector<std::string>>("highlightModules", {});
0164 desc.addUntracked<bool>("showPathDependencies", true);
0165 descriptions.add("DependencyGraph", desc);
0166 }
0167
0168 DependencyGraph::DependencyGraph(ParameterSet const &config, ActivityRegistry ®istry)
0169 : m_filename(config.getUntrackedParameter<std::string>("fileName")),
0170 m_highlightModules(
0171 make_unordered_set(config.getUntrackedParameter<std::vector<std::string>>("highlightModules"))),
0172 m_showPathDependencies(config.getUntrackedParameter<bool>("showPathDependencies")),
0173 m_initialized(false) {
0174 registry.watchPreSourceConstruction(this, &DependencyGraph::preSourceConstruction);
0175 registry.watchPreBeginJob(this, &DependencyGraph::preBeginJob);
0176 registry.watchPostBeginJob(this, &DependencyGraph::postBeginJob);
0177 }
0178
0179
0180 template <typename I>
0181 struct iterator_pair_as_a_range : std::pair<I, I> {
0182 public:
0183 using std::pair<I, I>::pair;
0184
0185 I begin() { return this->first; }
0186 I end() { return this->second; }
0187 };
0188
0189 template <typename I>
0190 iterator_pair_as_a_range<I> make_range(std::pair<I, I> p) {
0191 return iterator_pair_as_a_range<I>(p);
0192 }
0193
0194 void DependencyGraph::preSourceConstruction(ModuleDescription const &module) {
0195
0196 boost::add_vertex(m_graph);
0197 m_graph.m_graph[module.id()] =
0198 node{module.moduleLabel(), module.moduleName(), module.id(), EDMModuleType::Source, true};
0199 auto &attributes = boost::get(boost::get(boost::vertex_attribute, m_graph), 0);
0200 attributes["label"] = module.moduleLabel();
0201 attributes["tooltip"] = module.moduleName();
0202 attributes["shape"] = shapes[static_cast<std::underlying_type_t<EDMModuleType>>(EDMModuleType::Source)];
0203 attributes["style"] = "filled";
0204 attributes["color"] = "black";
0205 attributes["fillcolor"] = highlighted(module.moduleLabel()) ? "lightgreen" : "white";
0206 }
0207
0208 void DependencyGraph::preBeginJob(PathsAndConsumesOfModulesBase const &pathsAndConsumes,
0209 ProcessContext const &context) {
0210
0211 if (context.isSubProcess() and not m_initialized) {
0212 edm::LogError("DependencyGraph") << "You have requested an instance of the DependencyGraph Service in the \""
0213 << context.processName()
0214 << "\" SubProcess, which is not supported.\nPlease move it to the main process.";
0215 return;
0216 }
0217
0218 if (not context.isSubProcess()) {
0219
0220 boost::get_property(m_graph, boost::graph_name) = context.processName();
0221 boost::get_property(m_graph, boost::graph_graph_attribute)["label"] = "process " + context.processName();
0222 boost::get_property(m_graph, boost::graph_graph_attribute)["labelloc"] = "top";
0223
0224
0225 auto size = pathsAndConsumes.largestModuleID() - boost::num_vertices(m_graph) + 1;
0226 for (size_t i = 0; i < size; ++i)
0227 boost::add_vertex(m_graph);
0228
0229 m_initialized = true;
0230 } else {
0231
0232 auto &graph = m_graph.create_subgraph();
0233
0234
0235 boost::get_property(graph, boost::graph_name) = "cluster" + context.processName();
0236 boost::get_property(graph, boost::graph_graph_attribute)["label"] = "subprocess " + context.processName();
0237 boost::get_property(graph, boost::graph_graph_attribute)["labelloc"] = "top";
0238
0239
0240 auto size = pathsAndConsumes.largestModuleID() - boost::num_vertices(m_graph) + 1;
0241 for (size_t i = 0; i < size; ++i)
0242 boost::add_vertex(graph);
0243 }
0244
0245
0246 for (edm::ModuleDescription const *module : pathsAndConsumes.allModules()) {
0247 m_graph.m_graph[module->id()] = {
0248 module->moduleLabel(), module->moduleName(), module->id(), edmModuleTypeEnum(*module), false};
0249
0250 auto &attributes = boost::get(boost::get(boost::vertex_attribute, m_graph), module->id());
0251 attributes["label"] = module->moduleLabel();
0252 attributes["tooltip"] = module->moduleName();
0253 attributes["shape"] = shapes[static_cast<std::underlying_type_t<EDMModuleType>>(edmModuleTypeEnum(*module))];
0254 attributes["style"] = "filled";
0255 attributes["color"] = "black";
0256 attributes["fillcolor"] = highlighted(module->moduleLabel()) ? "green" : "lightgrey";
0257 }
0258
0259
0260 auto const &paths = pathsAndConsumes.paths();
0261 auto const &endps = pathsAndConsumes.endPaths();
0262
0263
0264 for (edm::ModuleDescription const *consumer : pathsAndConsumes.allModules()) {
0265 for (edm::ModuleDescription const *module : pathsAndConsumes.modulesWhoseProductsAreConsumedBy(consumer->id())) {
0266 edm::LogInfo("DependencyGraph") << "module " << consumer->moduleLabel() << " depends on module "
0267 << module->moduleLabel();
0268 auto edge_status = boost::add_edge(consumer->id(), module->id(), m_graph);
0269
0270 if (highlighted(module->moduleLabel()) and highlighted(consumer->moduleLabel())) {
0271 auto const &edge = edge_status.first;
0272 auto &attributes = boost::get(boost::get(boost::edge_attribute, m_graph), edge);
0273 attributes["color"] = "darkgreen";
0274 }
0275 }
0276 }
0277
0278
0279 for (unsigned int i = 0; i < paths.size(); ++i) {
0280
0281 auto &graph = m_graph.create_subgraph();
0282
0283
0284 boost::get_property(graph, boost::graph_name) = paths[i];
0285 boost::get_property(graph, boost::graph_graph_attribute)["label"] = "Path " + paths[i];
0286 boost::get_property(graph, boost::graph_graph_attribute)["labelloc"] = "bottom";
0287
0288
0289 for (edm::ModuleDescription const *module : pathsAndConsumes.modulesOnPath(i)) {
0290 boost::add_vertex(module->id(), graph);
0291 }
0292 }
0293 for (unsigned int i = 0; i < endps.size(); ++i) {
0294
0295 auto &graph = m_graph.create_subgraph();
0296
0297
0298 boost::get_property(graph, boost::graph_name) = paths[i];
0299 boost::get_property(graph, boost::graph_graph_attribute)["label"] = "EndPath " + paths[i];
0300 boost::get_property(graph, boost::graph_graph_attribute)["labelloc"] = "bottom";
0301
0302
0303 for (edm::ModuleDescription const *module : pathsAndConsumes.modulesOnEndPath(i)) {
0304 boost::add_vertex(module->id(), graph);
0305 }
0306 }
0307
0308
0309 const int size = boost::num_vertices(m_graph);
0310 int triggerResults = -1;
0311 bool highlightTriggerResults = false;
0312 for (int i = 0; i < size; ++i) {
0313 if (m_graph.m_graph[i].label == "TriggerResults") {
0314 triggerResults = i;
0315 highlightTriggerResults = highlighted("TriggerResults");
0316 break;
0317 }
0318 }
0319
0320
0321 edm::ModuleDescription const *previous;
0322 for (unsigned int i = 0; i < paths.size(); ++i) {
0323 previous = nullptr;
0324 for (edm::ModuleDescription const *module : pathsAndConsumes.modulesOnPath(i)) {
0325 m_graph.m_graph[module->id()].scheduled = true;
0326 auto &attributes = boost::get(boost::get(boost::vertex_attribute, m_graph), module->id());
0327 attributes["fillcolor"] = highlighted(module->moduleLabel()) ? "lightgreen" : "white";
0328 if (previous and m_showPathDependencies) {
0329 edm::LogInfo("DependencyGraph") << "module " << module->moduleLabel() << " follows module "
0330 << previous->moduleLabel() << " in Path " << paths[i];
0331 auto edge_status = boost::lookup_edge(module->id(), previous->id(), m_graph);
0332 bool found = edge_status.second;
0333 if (not found) {
0334 edge_status = boost::add_edge(module->id(), previous->id(), m_graph);
0335 auto const &edge = edge_status.first;
0336 auto &edgeAttributes = boost::get(boost::get(boost::edge_attribute, m_graph), edge);
0337 edgeAttributes["style"] = "dashed";
0338
0339 if (highlighted(module->moduleLabel()) and highlighted(previous->moduleLabel()))
0340 edgeAttributes["color"] = "darkgreen";
0341 }
0342 }
0343 previous = module;
0344 }
0345
0346 if (previous and m_showPathDependencies) {
0347
0348 for (int j = 0; j < size; ++j) {
0349 if (m_graph.m_graph[j].label == paths[i]) {
0350 edm::LogInfo("DependencyGraph") << "module " << paths[i] << " implicitly follows module "
0351 << previous->moduleLabel() << " in Path " << paths[i];
0352
0353 auto edge_status = boost::add_edge(j, previous->id(), m_graph);
0354 auto const &edge = edge_status.first;
0355 auto &edgeAttributes = boost::get(boost::get(boost::edge_attribute, m_graph), edge);
0356 edgeAttributes["style"] = "dashed";
0357
0358 bool highlightedPath = highlighted(paths[i]);
0359 if (highlightedPath and highlighted(previous->moduleLabel()))
0360 edgeAttributes["color"] = "darkgreen";
0361 if (triggerResults > 0) {
0362
0363 auto edge_status = boost::add_edge(triggerResults, j, m_graph);
0364 auto const &edge = edge_status.first;
0365 auto &edgeAttributes = boost::get(boost::get(boost::edge_attribute, m_graph), edge);
0366 edgeAttributes["style"] = "dashed";
0367
0368 if (highlightedPath and highlightTriggerResults)
0369 edgeAttributes["color"] = "darkgreen";
0370 }
0371 break;
0372 }
0373 }
0374 }
0375 }
0376
0377
0378 for (unsigned int i = 0; i < endps.size(); ++i) {
0379 previous = nullptr;
0380 for (edm::ModuleDescription const *module : pathsAndConsumes.modulesOnEndPath(i)) {
0381 m_graph.m_graph[module->id()].scheduled = true;
0382 auto &attributes = boost::get(boost::get(boost::vertex_attribute, m_graph), module->id());
0383 attributes["fillcolor"] = highlighted(module->moduleLabel()) ? "lightgreen" : "white";
0384 if (previous and m_showPathDependencies) {
0385 edm::LogInfo("DependencyGraph") << "module " << module->moduleLabel() << " follows module "
0386 << previous->moduleLabel() << " in EndPath " << i;
0387 auto edge_status = boost::lookup_edge(module->id(), previous->id(), m_graph);
0388 bool found = edge_status.second;
0389 if (not found) {
0390 edge_status = boost::add_edge(module->id(), previous->id(), m_graph);
0391 auto const &edge = edge_status.first;
0392 auto &edgeAttributes = boost::get(boost::get(boost::edge_attribute, m_graph), edge);
0393 edgeAttributes["style"] = "dashed";
0394
0395 if (highlighted(module->moduleLabel()) and highlighted(previous->moduleLabel()))
0396 edgeAttributes["color"] = "darkgreen";
0397 }
0398 }
0399 previous = module;
0400 }
0401 }
0402 }
0403
0404 void DependencyGraph::postBeginJob() {
0405 if (not m_initialized)
0406 return;
0407
0408
0409 for (int i = boost::num_vertices(m_graph) - 1; i > 1; --i) {
0410 if (m_graph.m_graph[i].label.empty())
0411 boost::remove_vertex(i, m_graph.m_graph);
0412 }
0413
0414
0415 std::ofstream out(m_filename);
0416 boost::write_graphviz(out, m_graph);
0417 out.close();
0418 }
0419
0420 namespace edm {
0421 namespace service {
0422
0423 inline bool isProcessWideService(DependencyGraph const *) { return true; }
0424
0425 }
0426 }
0427
0428
0429 #include "FWCore/ServiceRegistry/interface/ServiceMaker.h"
0430 DEFINE_FWK_SERVICE(DependencyGraph);