iterator_pair_as_a_range

record_vertices

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
/*
 *
 */

#include <cassert>
#include <iostream>
#include <string>
#include <type_traits>
#include <vector>

// boost optional (used by boost graph) results in some false positives with -Wmaybe-uninitialized
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#include <boost/graph/depth_first_search.hpp>
#pragma GCC diagnostic pop

#include "DataFormats/Provenance/interface/ModuleDescription.h"
#include "FWCore/Framework/interface/TriggerNamesService.h"
#include "FWCore/MessageLogger/interface/MessageLogger.h"
#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h"
#include "FWCore/ParameterSet/interface/ParameterSet.h"
#include "FWCore/ParameterSet/interface/ParameterSetDescription.h"
#include "FWCore/ParameterSet/interface/Registry.h"
#include "FWCore/ServiceRegistry/interface/ActivityRegistry.h"
#include "FWCore/ServiceRegistry/interface/PathsAndConsumesOfModulesBase.h"
#include "FWCore/ServiceRegistry/interface/ProcessContext.h"
#include "FWCore/ServiceRegistry/interface/Service.h"
#include "FWCore/Utilities/interface/EDMException.h"
#include "HLTrigger/Timer/interface/ProcessCallGraph.h"

// adaptor to use range-based for loops with boost::graph edges(...) and vertices(...) functions
template <typename I>
struct iterator_pair_as_a_range : std::pair<I, I> {
public:
  using std::pair<I, I>::pair;

  I begin() { return this->first; }
  I end() { return this->second; }
};

template <typename I>
iterator_pair_as_a_range<I> make_range(std::pair<I, I> p) {
  return iterator_pair_as_a_range<I>(p);
}

void ProcessCallGraph::preSourceConstruction(edm::ModuleDescription const& module) {
  // check that the Source has not already been added
  assert(source_ == edm::ModuleDescription::invalidID());

  // keep track of the Source module id
  source_ = module.id();

  // create graph vertex for the source module
  boost::add_vertex(graph_);
  graph_.m_graph[module.id()] = {module, edm::EDMModuleType::kSource, true};
}

// FIXME
//  - check that all module ids are valid (e.g. subprocesses are not being added in
//    the wrong order)
void ProcessCallGraph::lookupInitializationComplete(edm::PathsAndConsumesOfModulesBase const& pathsAndConsumes,
                                                    edm::ProcessContext const& context) {
  unsigned int pid = registerProcess(context);

  // check that the Source has already been added
  assert(source_ != edm::ModuleDescription::invalidID());

  // work on the full graph (for the main process) or a subgraph (for a subprocess)
  GraphType& graph = context.isSubProcess() ? graph_.create_subgraph() : graph_.root();

  // set the graph name property to the process name
  boost::get_property(graph, boost::graph_name) = context.processName();

  // create graph vertices associated to all modules in the process
  unsigned int size = pathsAndConsumes.largestModuleID() - boost::num_vertices(graph) + 1;
  for (size_t i = 0; i < size; ++i)
    boost::add_vertex(graph);

  // set the vertices properties (use the module id as the global index into the graph)
  std::vector<unsigned int> modules;
  modules.reserve(size);
  for (edm::ModuleDescription const* module : pathsAndConsumes.allModules()) {
    modules.push_back(module->id());
    graph_.m_graph[module->id()] = {*module, edmModuleTypeEnum(*module), false};
  }

  // add graph edges associated to module dependencies
  for (edm::ModuleDescription const* consumer : pathsAndConsumes.allModules()) {
    for (edm::ModuleDescription const* module : pathsAndConsumes.modulesWhoseProductsAreConsumedBy(consumer->id())) {
      // module `consumer' depends on module `module'
      boost::add_edge(consumer->id(), module->id(), graph_);
    }
  }

  // extract path names from the TriggerNamesService
  edm::service::TriggerNamesService const& tns = *edm::Service<edm::service::TriggerNamesService>();

  // extract the details of the paths and endpaths: name, modules on the path, and their dependencies
  size = pathsAndConsumes.paths().size();
  assert(tns.getTrigPaths().size() == size);
  std::vector<PathType> paths;
  paths.reserve(size);
  for (unsigned int i = 0; i < size; ++i) {
    std::vector<unsigned int> modules;
    for (edm::ModuleDescription const* module : pathsAndConsumes.modulesOnPath(i)) {
      modules.push_back(module->id());
      // mark the modules in the Paths as scheduled
      graph_.m_graph[module->id()].scheduled_ = true;
    }
    auto deps = dependencies(modules);
    paths.emplace_back(tns.getTrigPath(i), modules, deps.first, deps.second);
  }
  size = pathsAndConsumes.endPaths().size();
  std::vector<PathType> endPaths;
  endPaths.reserve(size);
  for (unsigned int i = 0; i < size; ++i) {
    std::vector<unsigned int> modules;
    for (edm::ModuleDescription const* module : pathsAndConsumes.modulesOnEndPath(i)) {
      modules.push_back(module->id());
      // mark the modules in the EndPaths as scheduled
      graph_.m_graph[module->id()].scheduled_ = true;
    }
    auto deps = dependencies(modules);
    endPaths.emplace_back(tns.getEndPath(i), modules, deps.first, deps.second);
  }

  // store the description of process, modules and paths
  process_description_.emplace_back(context.processName(), graph, modules, paths, endPaths);
  assert(process_description_.size() == pid + 1);

  // attach a subprocess to its parent
  if (context.isSubProcess()) {
    unsigned int parent_pid = processId(context.parentProcessContext());
    process_description_[parent_pid].subprocesses_.push_back(pid);
  }
}

// number of modules stored in the call graph
unsigned int ProcessCallGraph::size() const { return boost::num_vertices(graph_); }

// retrieve the ModuleDescriptio associated to the given id and vertex
edm::ModuleDescription const& ProcessCallGraph::source() const { return graph_.m_graph[source_].module_; }

// retrieve the ModuleDescription associated to the given id and vertex
edm::ModuleDescription const& ProcessCallGraph::module(unsigned int module) const {
  return graph_.m_graph[module].module_;
}

// retrieve the full information for a given module
ProcessCallGraph::NodeType const& ProcessCallGraph::operator[](unsigned int module) const {
  return graph_.m_graph[module];
}

// find the dependencies of the given module
std::vector<unsigned int> ProcessCallGraph::depends(unsigned int module) const {
  std::vector<unsigned int> colors(boost::num_vertices(graph_));
  auto colormap = boost::make_container_vertex_map(colors);

  // depht-first visit all vertices starting from the given module
  boost::default_dfs_visitor visitor;
  boost::depth_first_visit(graph_, module, visitor, colormap);

  // count the visited vertices (the `black' ones) in order to properly size the
  // output vector; then fill the dependencies with the list of visited nodes
  unsigned int size = 0;
  for (unsigned int color : colors)
    if (boost::black_color == color)
      ++size;
  std::vector<unsigned int> dependencies(size);
  unsigned j = 0;
  for (unsigned int i = 0; i < colors.size(); ++i)
    if (boost::black_color == colors[i])
      dependencies[j++] = i;
  assert(size == j);

  return dependencies;
}

// find the dependencies of all modules in the given path
//
// return two vector:
//   - the first lists all the dependencies for the whole path
//   - the second lists the one-after-the-last dependency index into the first vector for each module
std::pair<std::vector<unsigned int>, std::vector<unsigned int>> ProcessCallGraph::dependencies(
    std::vector<unsigned int> const& path) {
  std::vector<unsigned int> colors(boost::num_vertices(graph_));
  auto colormap = boost::make_container_vertex_map(colors);

  // first, find and count all the path's modules' dependencies
  boost::default_dfs_visitor visitor;
  for (unsigned int module : path)
    boost::depth_first_visit(graph_, module, visitor, colormap);

  unsigned int size = 0;
  for (unsigned int color : colors)
    if (color == 0)
      ++size;

  // allocate the output vectors
  std::vector<unsigned int> dependencies(size);
  dependencies.resize(0);
  std::vector<unsigned int> indices(path.size());
  indices.resize(0);

  // reset the color map
  for (unsigned int& color : colors)
    color = 0;

  // find again all the dependencies, and record those associated to each module
  struct record_vertices : boost::default_dfs_visitor {
    record_vertices(std::vector<unsigned int>& vertices) : vertices_(vertices) {}

    void discover_vertex(unsigned int vertex, GraphType const& graph) { vertices_.push_back(vertex); }

    std::vector<unsigned int>& vertices_;
  };
  record_vertices recorder(dependencies);

  for (unsigned int module : path) {
    // skip modules that have already been added as dependencies
    if (colors[module] != boost::black_color)
      boost::depth_first_visit(graph_, module, recorder, colormap);
    indices.push_back(dependencies.size());
  }

  return std::make_pair(dependencies, indices);
}

// register a (sub)process and assigns it a "process id"
// throws an exception if called with a duplicate process name
unsigned int ProcessCallGraph::registerProcess(edm::ProcessContext const& context) {
  // registerProcess (called by preBeginJob) must be called for the parent process before its subprocess(es)
  if (context.isSubProcess() and process_id_.find(context.parentProcessContext().processName()) == process_id_.end()) {
    throw edm::Exception(edm::errors::LogicError)
        << "ProcessCallGraph::preBeginJob(): called for subprocess \"" << context.processName() << "\""
        << " before being called for its parent process \"" << context.parentProcessContext().processName() << "\"";
  }

  // registerProcess (called by preBeginJob) should be called once or each (sub)process
  auto id = process_id_.find(context.processName());
  if (id != process_id_.end()) {
    throw edm::Exception(edm::errors::LogicError)
        << "ProcessCallGraph::preBeginJob(): called twice for the same "
        << (context.isSubProcess() ? "subprocess" : "process") << " " << context.processName();
  }

  // this assumes that registerProcess (called by preBeginJob) is not called concurrently from different threads
  // otherwise, process_id_.size() should be replaces with an atomic counter
  std::tie(id, std::ignore) = process_id_.insert(std::make_pair(context.processName(), process_id_.size()));
  return id->second;
}

// retrieve the "process id" of a process, given its ProcessContex
// throws an exception if the (sub)process was not registered
unsigned int ProcessCallGraph::processId(edm::ProcessContext const& context) const {
  auto id = process_id_.find(context.processName());
  if (id == process_id_.end())
    throw edm::Exception(edm::errors::LogicError)
        << "ProcessCallGraph::processId(): unexpected " << (context.isSubProcess() ? "subprocess" : "process") << " "
        << context.processName();
  return id->second;
}

// retrieve the "process id" of a process, given its ProcessContex
// throws an exception if the (sub)process was not registered
unsigned int ProcessCallGraph::processId(std::string const& processName) const {
  auto id = process_id_.find(processName);
  if (id == process_id_.end())
    throw edm::Exception(edm::errors::LogicError)
        << "ProcessCallGraph::processId(): unexpected (sub)process " << processName;
  return id->second;
}

// retrieve the number of processes
std::vector<ProcessCallGraph::ProcessType> const& ProcessCallGraph::processes() const { return process_description_; }

// retrieve information about a process, given its "process id"
ProcessCallGraph::ProcessType const& ProcessCallGraph::processDescription(unsigned int pid) const {
  return process_description_.at(pid);
}

// retrieve information about a process, given its ProcessContex
ProcessCallGraph::ProcessType const& ProcessCallGraph::processDescription(edm::ProcessContext const& context) const {
  unsigned int pid = processId(context);
  return process_description_[pid];
}

// retrieve information about a process, given its ProcessContex
ProcessCallGraph::ProcessType const& ProcessCallGraph::processDescription(std::string const& processName) const {
  unsigned int pid = processId(processName);
  return process_description_[pid];
}