CMS 3D CMS Logo

DQMStore.cc
Go to the documentation of this file.
1 // silence deprecation warnings for the DQMStore itself.
2 #define DQM_DEPRECATED
7 #include <string>
8 #include <regex>
9 #include <csignal>
10 
11 #include <execinfo.h>
12 #include <cxxabi.h>
13 
14 namespace dqm::implementation {
15 
16  // list of acceptable characters for ME path names, in order to be able to upload to the CMS DQM GUI
17  // See https://github.com/cms-DQM/dqmgui_prod/blob/af0a388e8f57c60e51111585d298aeeea943367f/src/cpp/DQM/DQMStore.cc#L56
18  static const std::string s_safe = "/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+=_()# ";
19 
21  if (cwd_.empty()) {
22  return "";
23  } else {
24  // strip trailing slash.
25  // This is inefficient and error prone (callers need to do the same
26  // branching to re-add the "/"!) but some legacy code expects it like
27  // that and is to complicated to change.
28  assert(cwd_[cwd_.size() - 1] == '/');
29  auto pwd = cwd_.substr(0, cwd_.size() - 1);
30  return pwd;
31  }
32  }
35  void NavigatorBase::goUp() { cd(cwd_ + ".."); }
39  assert(this);
40  cwd_ = path.getDirname();
41  }
42 
44  store_ = store;
45  scope_ = MonitorElementData::Scope::JOB;
46  }
47 
49 
51  auto oldscope = scope_;
52  scope_ = newscope;
53  return oldscope;
54  }
56  auto oldid = moduleID_;
57  moduleID_ = moduleID;
58  return oldid;
59  }
60 
62  auto oldrunlumi = runlumi_;
63  runlumi_ = runlumi;
64  return oldrunlumi;
65  }
66 
69  std::function<TH1*()> makeobject,
70  bool forceReplace /* = false */) {
73 
74  auto pathToCheck{fullpath};
75  std::string limiter{".root:/"}; // this indicates that all the substring before is a file name
76  size_t pos = pathToCheck.find(limiter); //find location of limiter
77  //delete everything prior to location found as it might contain illegal chars
78  pathToCheck.erase(0, pos + limiter.size());
79 
80  if (pathToCheck.find_first_not_of(s_safe) != std::string::npos) {
81  throw cms::Exception("BadMonitorElementPathName")
82  << " Monitor element path name: '" << pathToCheck.c_str() << "' uses unacceptable characters."
83  << "\n Acceptable characters are: " << s_safe.c_str();
84  }
85 
87 
88  // We should check if there is a local ME for this module and name already.
89  // However, it is easier to do that in putME().
90 
92  store_->printTrace("Booking " + std::string(name) + (me ? " (existing)" : " (new)"));
93 
94  if (me == nullptr) {
95  // no existing global ME found. We need to instantiate one, and put it
96  // into the DQMStore. This will typically be a prototype, unless run and
97  // lumi are set and we proces a legacy booking call.
98  TH1* th1 = makeobject();
99  MonitorElementData medata;
100  medata.key_.path_ = path;
101  medata.key_.kind_ = kind;
102 
103  const auto& MEs = store_->getMEsToSave();
104 
105  if (not MEs.empty() && not store_->getMode()) {
106  bool pathInList = false;
107  for (const auto& thepath : MEs) {
108  if (fullpath == thepath) {
109  medata.key_.scope_ = MonitorElementData::Scope::LUMI;
110  pathInList = true;
111  break;
112  }
113  }
114  if (not pathInList)
115  medata.key_.scope_ = this->scope_;
116  } else
117  medata.key_.scope_ = this->scope_;
118 
119  // will be (0,0) ( = prototype) in the common case.
120  // This branching is for harvesting, where we have run/lumi in the booker.
121  if (medata.key_.scope_ == MonitorElementData::Scope::JOB) {
122  medata.key_.id_ = edm::LuminosityBlockID();
123  } else if (medata.key_.scope_ == MonitorElementData::Scope::RUN) {
124  medata.key_.id_ = edm::LuminosityBlockID(this->runlumi_.run(), 0);
125  } else if (medata.key_.scope_ == MonitorElementData::Scope::LUMI) {
126  // In the messy case of legacy-booking a LUMI ME in beginRun (or
127  // similar), where we don't have a valid lumi number yet, make sure to
128  // book a prototype instead.
129  if (this->runlumi_.run() != 0 && this->runlumi_.luminosityBlock() != 0) {
130  medata.key_.id_ = this->runlumi_;
131  } else {
132  medata.key_.id_ = edm::LuminosityBlockID();
133  }
134  } else {
135  assert(!"Illegal scope");
136  }
137 
138  medata.value_.object_ = std::unique_ptr<TH1>(th1);
139  MonitorElement* me_ptr = new MonitorElement(std::move(medata));
140  me = store_->putME(me_ptr);
141  } else {
142  if (forceReplace) {
143  TH1* th1 = makeobject();
144  assert(th1);
145  store_->debugTrackME("bookME (forceReplace)", nullptr, me);
146  // surgically replace Histogram
147  me->switchObject(std::unique_ptr<TH1>(th1));
148  }
149  }
150 
151  // me now points to a global ME owned by the DQMStore.
152  assert(me);
153 
154  // each booking call returns a unique "local" ME, which the DQMStore keeps
155  // in a container associated with the module (and potentially run, for
156  // DQMGlobalEDAnalyzer). This will later be update to point to different
157  // MEData (kept in a global ME) as needed.
158  // putME creates the local ME object as needed.
159  auto localme = store_->putME(me, this->moduleID_);
160  // me now points to a local ME owned by the DQMStore.
161  assert(localme);
162 
163  if (this->moduleID_ == 0) {
164  // this is a legacy/global/harvesting booking. In this case, we return
165  // the global directly. It is not advisable to hold this pointer, as we
166  // may delete the global ME later, but we promise to keep it valid for
167  // the entire job if there are no concurrent runs/lumis. (see
168  // assertLegacySafe option).
169  // We still created a local ME, so we can drive the lumi-changing for
170  // legacy modules in watchPreGlobalBeginLumi.
171  store_->debugTrackME("bookME (legacy)", localme, me);
172  return me;
173  } else {
174  // the normal case.
175  store_->debugTrackME("bookME (normal)", localme, me);
176  return localme;
177  }
178  }
179 
181  auto lock = std::scoped_lock(this->booking_mutex_);
182  assert(me);
183  auto existing_new = globalMEs_[me->getRunLumi()].insert(me);
184  if (existing_new.second == true) {
185  // successfully inserted, return new object
186  debugTrackME("putME (global)", nullptr, me);
187  return me;
188  } else {
189  // already present, return old object
190  delete me;
191  assert(!"Currently, this should never happen.");
192  return *(existing_new.first);
193  }
194  }
195 
197  auto lock = std::scoped_lock(this->booking_mutex_);
198  assert(me);
199  auto& localmes = localMEs_[moduleID];
200  auto existing = localmes.find(me);
201  if (existing == localmes.end()) {
202  // insert new local ME
203  MonitorElement* local_me = new MonitorElement(me);
204  auto existing_new = localmes.insert(local_me);
205  // successfully inserted, return new object
206  assert(existing_new.second == true); // insert successful
207  debugTrackME("putME (local, new)", local_me, me);
208  return local_me;
209  } else {
210  // already present, return old object
211  auto local_me = *existing;
212  edm::LogInfo("DQMStore") << "ME " << me->getFullname() << " booked twice in the same module.";
213  // the existing local ME might not have data attached (e.g. in 2nd run)
214  // in that case, we attach the global ME provided by booking above.
215  // This may be a prototype or of a random run/lumi, but it ensures that
216  // even LUMI histos are always valid after booking (as we promise for
217  // legacy modules -- for sequential runs/lumis, there is only ever one
218  // global ME, and the local one points to it).
219  if (!local_me->isValid()) {
220  local_me->switchData(me);
221  }
222  debugTrackME("putME (local, existing)", local_me, me);
223  return local_me;
224  }
225  }
226 
227  template <typename MELIKE>
229  auto lock = std::scoped_lock(this->booking_mutex_);
230  for (auto& [runlumi, meset] : this->globalMEs_) {
231  auto it = meset.find(path);
232  if (it != meset.end()) {
233  debugTrackME("findME (found)", nullptr, *it);
234  // no guarantee on which ME we return here -- only that clone'ing this
235  // would give a valid ME for that path.
236  return *it;
237  }
238  }
239  return nullptr;
240  }
241 
242  void DQMStore::printTrace(std::string const& message) {
243  if (verbose_ < 3)
244  return;
245  edm::LogWarning("DQMStoreBooking").log([&](auto& logger) {
246  std::regex s_rxtrace{"(.*)\\((.*)\\+0x.*\\).*(\\[.*\\])"};
247  std::regex s_rxself{"^[^()]*dqm::implementation::.*|^[^()]*edm::.*|.*edm::convertException::wrap.*"};
248 
249  void* array[10];
250  size_t size;
251  char** strings;
252  int demangle_status = 0;
253  std::vector<std::string> clean_trace;
254 
255  // glibc/libgcc backtrace functionality, declared in execinfo.h.
256  size = backtrace(array, 10);
257  strings = backtrace_symbols(array, size);
258 
259  size_t level = 1;
260  char* demangled = nullptr;
261  for (; level < size; ++level) {
262  std::cmatch match;
263  bool ok = std::regex_match(strings[level], match, s_rxtrace);
264 
265  if (!ok) {
266  edm::LogWarning("DQMStoreBacktrace") << "failed match" << level << strings[level];
267  continue;
268  }
269 
270  if (match[2].length() == 0) {
271  // no symbol, ignore.
272  continue;
273  }
274 
275  // demangle name to human readable form
276  demangled = abi::__cxa_demangle(std::string(match[2]).c_str(), nullptr, nullptr, &demangle_status);
277  if (!demangled || demangle_status != 0) {
278  edm::LogWarning("DQMStoreBacktrace") << "failed demangle! status " << demangle_status << " on " << match[2];
279  continue;
280  }
281 
282  if (std::regex_match(demangled, s_rxself)) {
283  // ignore framework/internal methods
284  free(demangled);
285  demangled = nullptr;
286  continue;
287  } else {
288  // keep the demangled name and the address.
289  // The address can be resolved to a line number in gdb attached to
290  // the process, using `list *0x<addr>`, but it can only be done in
291  // the running process and we can"t easily do it in this code.
292  clean_trace.push_back(std::string(demangled) + std::string(match[3]));
293  free(demangled);
294  demangled = nullptr;
295  }
296  }
297 
298  if (!clean_trace.empty()) {
299  logger << message << " at ";
300  for (auto const& s : clean_trace) {
301  logger << s << "; ";
302  }
303  } else {
304  logger << message << " : failed to collect stack trace.";
305  }
306 
307  free(strings);
308  });
309  }
310 
311  void DQMStore::debugTrackME(const char* message, MonitorElement* me_local, MonitorElement* me_global) const {
312  const char* scopename[] = {"INVALID", "JOB", "RUN", "LUMI"};
313  if (!this->trackME_.empty() && (me_local || me_global)) {
314  std::string name = me_global ? me_global->getFullname() : me_local->getFullname();
315  if (name.find(this->trackME_) != std::string::npos) {
316  edm::LogWarning("DQMStoreTrackME").log([&](auto& logger) {
317  logger << message << " for " << name << "(" << me_local << "," << me_global << ")";
318  auto writeme = [&](MonitorElement* me) {
319  if (me->isValid()) {
320  logger << " " << me->getRunLumi() << " scope " << scopename[me->getScope()];
321  if (me->kind() >= MonitorElement::Kind::TH1F) {
322  logger << " entries " << me->getEntries();
323  } else if (me->kind() == MonitorElement::Kind::STRING) {
324  logger << " value " << me->getStringValue();
325  } else if (me->kind() == MonitorElement::Kind::REAL) {
326  logger << " value " << me->getFloatValue();
327  } else if (me->kind() == MonitorElement::Kind::INT) {
328  logger << " value " << me->getIntValue();
329  }
330  } else {
331  logger << " (invalid)";
332  }
333  };
334  if (me_local) {
335  logger << " local:";
336  writeme(me_local);
337  }
338  if (me_global) {
339  logger << " global:";
340  writeme(me_global);
341  }
342  });
343  // A breakpoint can be useful here.
344  //std::raise(SIGINT);
345  }
346  }
347  }
348 
350  // This is specifically for DQMRootSource, or other input modules. These
351  // are special in that they use the legacy interface (no moduleID, no local
352  // MEs) but need to be able to handle concurrent lumisections correctly.
353  // The logic is very similar to that in enterLumi; this is enterLumi for
354  // Input Modules.
355  auto lock = std::scoped_lock(this->booking_mutex_);
356  auto existing = this->get(key);
357  if (existing) {
358  // exactly matching ME found, needs merging with the new data.
359  debugTrackME("findOrRecycle (found)", nullptr, existing);
360  return existing;
361  } // else
362 
363  // this is where we'd expect the ME.
364  auto& targetset = this->globalMEs_[key.id_];
365  // this is where we can get MEs to reuse.
366  auto& prototypes = this->globalMEs_[edm::LuminosityBlockID()];
367 
368  auto proto = prototypes.find(key.path_);
369  if (proto != prototypes.end()) {
370  MonitorElement* oldme = *proto;
371  assert(oldme->getScope() == key.scope_);
372  prototypes.erase(proto);
373  auto medata = oldme->release(); // destroy the ME, get its data.
374  // in this situation, nobody should be filling the ME concurrently.
375  medata->data_.key_.id_ = key.id_;
376  // We reuse the ME object here, even if we don't have to. This ensures
377  // that when running single-threaded without concurrent lumis/runs,
378  // the global MEs will also live forever and allow legacy usages.
379  oldme->switchData(medata);
380  auto result = targetset.insert(oldme);
381  assert(result.second); // was new insertion
382  auto newme = *result.first; // iterator to new ME
383  assert(oldme == newme); // recycling!
384  // newme is reset and ready to accept data.
385  debugTrackME("findOrRecycle (recycled)", nullptr, newme);
386  return newme;
387  } // else
388 
389  return nullptr;
390  }
391 
393  // Call initLumi for all modules, as a global operation.
394  auto lock = std::scoped_lock(this->booking_mutex_);
395  for (auto& kv : this->localMEs_) {
396  initLumi(run, lumi, kv.first);
397  }
398  }
399 
401  // Make sure global MEs for the run/lumi exist (depending on scope)
402 
403  auto lock = std::scoped_lock(this->booking_mutex_);
404 
405  // these are the MEs we need to update.
406  auto& localset = this->localMEs_[moduleID];
407  // this is where they need to point to.
408  // This could be a per-run or per-lumi set (depending on lumi == 0)
409  auto& targetset = this->globalMEs_[edm::LuminosityBlockID(run, lumi)];
410  // this is where we can get MEs to reuse.
411  auto& prototypes = this->globalMEs_[edm::LuminosityBlockID()];
412 
413  auto checkScope = [run, lumi](MonitorElementData::Scope scope) {
414  if (scope == MonitorElementData::Scope::JOB) {
415  return (run == 0 && lumi == 0);
416  } else if (scope == MonitorElementData::Scope::RUN) {
417  return (run != 0 && lumi == 0);
418  } else if (scope == MonitorElementData::Scope::LUMI) {
419  return (lumi != 0);
420  }
421  assert(!"Impossible Scope.");
422  return false;
423  };
424 
425  for (MonitorElement* me : localset) {
426  auto target = targetset.find(me); // lookup by path, thanks to MEComparison
427  if (target != targetset.end()) {
428  // we already have a ME, just use it!
429  debugTrackME("initLumi (existing)", nullptr, *target);
430  } else {
431  // look for a prototype to reuse.
432  auto proto = prototypes.find(me);
433  if (proto != prototypes.end()) {
434  // first, check if this ME needs updating at all. We can only check
435  // the scope once we have an actual global ME instance, the local ME
436  // might not have any data attached!
437  if (checkScope((*proto)->getScope()) == false) {
438  continue;
439  } // else
440  // reuse that.
441  MonitorElement* oldme = *proto;
442  prototypes.erase(proto);
443  auto medata = oldme->release(); // destroy the ME, get its data.
444  // in this situation, nobody should be filling the ME concurrently.
445  medata->data_.key_.id_ = edm::LuminosityBlockID(run, lumi);
446  // We reuse the ME object here, even if we don't have to. This ensures
447  // that when running single-threaded without concurrent lumis/runs,
448  // the global MEs will also live forever and allow legacy usages.
449  oldme->switchData(medata);
450  auto result = targetset.insert(oldme);
451  assert(result.second); // was new insertion
452  target = result.first; // iterator to new ME
453  debugTrackME("initLumi (reused)", nullptr, *target);
454  } else {
455  // no prototype available. That means we have concurrent Lumis/Runs,
456  // and need to make a clone now.
457  auto anyme = this->findME(me);
458  assert(anyme || !"local ME without any global ME!");
459  if (checkScope(anyme->getScope()) == false) {
460  continue;
461  } // else
462 
463  // whenever we clone global MEs, it is no longer safe to hold
464  // pointers to them.
466 
467  MonitorElementData newdata = anyme->cloneMEData();
468  newdata.key_.id_ = edm::LuminosityBlockID(run, lumi);
469  auto newme = new MonitorElement(std::move(newdata));
470  newme->Reset(); // we cloned a ME in use, not an empty prototype
471  auto result = targetset.insert(newme);
472  assert(result.second); // was new insertion
473  target = result.first; // iterator to new ME
474  debugTrackME("initLumi (allocated)", nullptr, *target);
475  }
476  }
477  }
478  }
479 
481  // point the local MEs for this module to these global MEs.
482 
483  // This needs to happen before we can use the global MEs for this run/lumi here.
484  // We could do it lazyly here, or eagerly globally in global begin lumi.
485  //initLumi(run, lumi, moduleID);
486 
487  auto lock = std::scoped_lock(this->booking_mutex_);
488 
489  // these are the MEs we need to update.
490  auto& localset = this->localMEs_[moduleID];
491  // this is where they need to point to.
492  auto& targetset = this->globalMEs_[edm::LuminosityBlockID(run, lumi)];
493 
494  // only for a sanity check
495  auto checkScope = [run, lumi](MonitorElementData::Scope scope) {
496  if (scope == MonitorElementData::Scope::JOB) {
497  return (run == 0 && lumi == 0);
498  } else if (scope == MonitorElementData::Scope::RUN) {
499  return (run != 0 && lumi == 0);
500  } else if (scope == MonitorElementData::Scope::LUMI) {
501  return (lumi != 0);
502  }
503  assert(!"Impossible Scope.");
504  return false;
505  };
506 
507  for (MonitorElement* me : localset) {
508  auto target = targetset.find(me); // lookup by path, thanks to MEComparison
509  if (target == targetset.end()) {
510  auto anyme = this->findME(me);
511  debugTrackME("enterLumi (nothingtodo)", me, nullptr);
512  assert(anyme && checkScope(anyme->getScope()) == false);
513  continue;
514  }
515  assert(target != targetset.end()); // initLumi should have taken care of this.
516  // now we have the proper global ME in the right place, point the local there.
517  // This is only safe if the name is exactly the same -- else it might corrupt
518  // the tree structure of the set!
519  me->switchData(*target);
520  debugTrackME("enterLumi (switchdata)", me, *target);
521  }
522  }
523 
525  // here, we remove the pointers in the local MEs. No deletion or recycling
526  // yet -- this has to happen after the output module had a chance to do its
527  // work. We just leave the global MEs where they are. This is purely an
528  // accounting step, the cleanup code has to check that nobody is using the
529  // ME any more, and here we make sure that is the case.
530 
531  auto lock = std::scoped_lock(this->booking_mutex_);
532 
533  // these are the MEs we need to update.
534  auto& localset = this->localMEs_[moduleID];
535 
536  auto checkScope = [run, lumi](MonitorElementData::Scope scope) {
537  if (scope == MonitorElementData::Scope::JOB) {
538  return (run == 0 && lumi == 0);
539  } else if (scope == MonitorElementData::Scope::RUN) {
540  return (run != 0 && lumi == 0);
541  } else if (scope == MonitorElementData::Scope::LUMI) {
542  return (lumi != 0);
543  }
544  assert(!"Impossible Scope.");
545  return false;
546  };
547 
548  for (MonitorElement* me : localset) {
549  // we have to be very careful with the ME here, it might not be backed by data at all.
550  if (me->isValid() && checkScope(me->getScope()) == true) {
551  // if we left the scope, simply release the data.
552  debugTrackME("leaveLumi (release)", me, nullptr);
553  me->release();
554  }
555  }
556  }
557 
559  // now, we are done with the lumi, no modules have any work to do on these
560  // MEs, and the output modules have saved this lumi/run. Remove/recycle
561  // the MEs here.
562 
563  auto lock = std::scoped_lock(this->booking_mutex_);
564 
565  // in case of end-job cleanup we need different logic because of the
566  // prototype set.
567  assert(run != 0 || lumi != 0);
568  auto& prototypes = this->globalMEs_[edm::LuminosityBlockID()];
569 
570  // these are the MEs we need to get rid of...
571  auto meset = std::set<MonitorElement*, MonitorElement::MEComparison>();
572  // ... we take them out first.
573  meset.swap(this->globalMEs_[edm::LuminosityBlockID(run, lumi)]);
574 
575  // temporary buffer for the MEs to recycle, we must not change the key
576  // while they are in a set.
577  auto torecycle = std::vector<MonitorElement*>();
578 
579  // here, this is only a sanity check and not functionally needed.
580  auto checkScope = [run, lumi](MonitorElementData::Scope scope) {
581  if (scope == MonitorElementData::Scope::JOB) {
582  assert(run == 0 && lumi == 0);
583  } else if (scope == MonitorElementData::Scope::RUN) {
584  assert(run != 0 && lumi == 0);
585  } else if (scope == MonitorElementData::Scope::LUMI) {
586  assert(lumi != 0);
587  } else {
588  assert(!"Impossible Scope.");
589  }
590  };
591 
592  for (MonitorElement* me : meset) {
593  assert(me->isValid()); // global MEs should always be valid.
594  checkScope(me->getScope()); // we should only see MEs of one scope here.
595  auto other = this->findME(me);
596  if (other) {
597  // we still have a global one, so we can just remove this.
598  debugTrackME("cleanupLumi (delete)", nullptr, me);
599  delete me;
600  } else {
601  // we will modify the ME, so it needs to be out of the set.
602  // use a temporary vector to be save.
603  debugTrackME("cleanupLumi (recycle)", nullptr, me);
604  torecycle.push_back(me);
605  }
606  }
607 
608  meset.clear();
609 
610  for (MonitorElement* me : torecycle) {
611  auto medata = me->release(); // destroy the ME, get its data.
612  medata->data_.key_.id_ = edm::LuminosityBlockID(); // prototype
613  // We reuse the ME object here, even if we don't have to. This ensures
614  // that when running single-threaded without concurrent lumis/runs,
615  // the global MEs will also live forever and allow legacy usages.
616  me->switchData(medata);
617  // reset here (not later) to still catch random legacy fill calls.
618  me->Reset();
619  auto result = prototypes.insert(me);
620  assert(result.second); // was new insertion, else findME should succeed
621  debugTrackME("cleanupLumi (reset)", nullptr, me);
622  }
623  }
624 
625  std::vector<dqm::harvesting::MonitorElement*> IGetter::getContents(std::string const& pathname) const {
626  auto lock = std::scoped_lock(store_->booking_mutex_);
627  std::vector<MonitorElement*> out;
630  for (auto& [runlumi, meset] : store_->globalMEs_) {
631  auto it = meset.lower_bound(path);
632  while (it != meset.end() && (*it)->getPathname() == path.getDirname()) {
633  store_->debugTrackME("getContents (match)", nullptr, *it);
634  out.push_back(*it);
635  ++it;
636  }
637  }
638  return out;
639  }
640 
641  std::vector<dqm::harvesting::MonitorElement*> IGetter::getAllContents(std::string const& pathname) const {
642  auto lock = std::scoped_lock(store_->booking_mutex_);
643  std::vector<MonitorElement*> out;
646  // make sure this is normalized by getting it from Path object.
647  auto path_str = path.getFullname();
648  for (auto& [runlumi, meset] : store_->globalMEs_) {
649  auto it = meset.lower_bound(path);
650  // rfind can be used as a prefix match.
651  while (it != meset.end() && (*it)->getPathname().rfind(path_str, 0) == 0) {
652  if (runlumi == edm::LuminosityBlockID() && (*it)->getScope() != MonitorElementData::Scope::JOB) {
653  // skip prototypes
654  } else {
655  store_->debugTrackME("getAllContents (match)", nullptr, *it);
656  out.push_back(*it);
657  }
658  ++it;
659  }
660  }
661  return out;
662  }
663  std::vector<dqm::harvesting::MonitorElement*> IGetter::getAllContents(std::string const& pathname,
664  uint32_t runNumber,
665  uint32_t lumi) const {
666  auto lock = std::scoped_lock(store_->booking_mutex_);
667  std::vector<MonitorElement*> out;
670  // make sure this is normalized by getting it from Path object.
671  auto path_str = path.getFullname();
672  auto const& meset = store_->globalMEs_[edm::LuminosityBlockID(runNumber, lumi)];
673  auto it = meset.lower_bound(path);
674 
675  // decide if the ME should be saved in DQMIO and/or nanoDQMIO
676  // if doSaveByLumi_ is false: store all monitoring elements (needed for harvesting step!)
677  // if doSaveByLumi_ is true: store only selected monitoring elements (i.e. "nanoDQMIO")
678  while (it != meset.end() && (*it)->getFullname().rfind(path_str, 0) == 0) {
679  bool saveIt = true; // default value if doSaveByLumi_ is false
680 
681  if (store_->doSaveByLumi_ && not store_->MEsToSave_.empty()) {
682  std::string name = (*it)->getFullname();
683  saveIt = false; // default value if doSaveByLumi_ is true
684  for (std::vector<std::string>::const_iterator ipath = store_->MEsToSave_.begin();
685  ipath != store_->MEsToSave_.end();
686  ++ipath) {
687  const std::string& nameToSave = *ipath;
688  // option 1 (used in the past): inclusive selection
689  // (store all MEs that contain any of the requested patterns)
690  // if (name.find(nameToSave) != std::string::npos) {
691  // option 2 (current criterion): exact selection
692  // (store only MEs that exactly match a requested pattern)
693  if (name == nameToSave) {
694  saveIt = true;
695  // std::cout<<name<<" compared to"<<ipath->data()<<std::endl;
696  break;
697  }
698  }
699  }
700 
701  store_->debugTrackME("getAllContents (run/lumi match)", nullptr, *it);
702  if (saveIt) {
703  out.push_back(*it);
704  if (store_->doSaveByLumi_)
705  store_->debugTrackME("getAllContents (run/lumi saved)", nullptr, *it);
706  }
707  ++it;
708  }
709  return out;
710  }
711 
715  // this only really makes sense if there is only one instance of this ME,
716  // but the signature of this method also only makes sense in that case.
717  return store_->findME(path);
718  }
719 
721  auto const& meset = store_->globalMEs_[key.id_];
722  auto it = meset.find(key.path_);
723  if (it != meset.end()) {
724  assert((*it)->getScope() == key.scope_);
725  store_->debugTrackME("get (key found)", nullptr, *it);
726  return *it;
727  }
728  return nullptr;
729  }
730 
732  auto result = this->get(path);
733  if (result == nullptr) {
734  throw cms::Exception("iGetter Error") << "ME " << path << " was requested but not found.";
735  }
736  return result;
737  }
738 
739  std::vector<std::string> IGetter::getSubdirs() const {
740  // This is terribly inefficient, esp. if this method is then used to
741  // recursively enumerate whatever getAllContents would return anyways.
742  // But that is fine, any such code should just use getAllContents instead.
743  std::set<std::string> subdirs;
744  for (auto me : this->getAllContents(this->cwd_)) {
745  const auto& name = me->getPathname();
746  auto subdirname = name.substr(this->cwd_.length(), std::string::npos);
747  auto dirname = subdirname.substr(0, subdirname.find('/'));
748  subdirs.insert(dirname);
749  }
750  std::vector<std::string> out;
751  for (const auto& dir : subdirs) {
752  if (dir.length() == 0)
753  continue;
754  out.push_back(this->cwd_ + dir);
755  }
756  return out;
757  }
758 
759  std::vector<std::string> IGetter::getMEs() const {
760  auto mes = this->getContents(this->cwd_);
761  std::vector<std::string> out;
762  out.reserve(mes.size());
763  for (auto me : mes) {
764  out.push_back(me->getName());
765  }
766  return out;
767  }
768 
769  bool IGetter::dirExists(std::string const& path) const {
770  // we don't claim this is fast.
771  return !this->getAllContents(path).empty();
772  }
773 
774  IGetter::IGetter(DQMStore* store) { store_ = store; }
775 
777 
779  verbose_ = pset.getUntrackedParameter<int>("verbose", 0);
780  assertLegacySafe_ = pset.getUntrackedParameter<bool>("assertLegacySafe", false);
781  doSaveByLumi_ = pset.getUntrackedParameter<bool>("saveByLumi", false);
782  MEsToSave_ = pset.getUntrackedParameter<std::vector<std::string>>("MEsToSave", std::vector<std::string>());
783  trackME_ = pset.getUntrackedParameter<std::string>("trackME", "");
784  onlineMode_ = pset.getUntrackedParameter<bool>("onlineMode", false);
785 
786  // Set lumi and run for legacy booking.
787  // This is no more than a guess with concurrent runs/lumis, but should be
788  // correct for purely sequential legacy stuff.
789  // Also reset Scope, such that legacy modules can expect it to be JOB.
790  // initLumi and leaveLumi are needed for all module types: these handle
791  // creating and deleting global MEs as needed, which has to happen even if
792  // a module does not see lumi transitions.
793  ar.watchPreGlobalBeginRun([this](edm::GlobalContext const& gc) {
794  this->setRunLumi(gc.luminosityBlockID());
795  this->initLumi(gc.luminosityBlockID().run(), /* lumi */ 0);
796  this->enterLumi(gc.luminosityBlockID().run(), /* lumi */ 0, /* moduleID */ 0);
797  this->setScope(MonitorElementData::Scope::JOB);
798  });
799  ar.watchPreGlobalBeginLumi([this](edm::GlobalContext const& gc) {
800  this->setRunLumi(gc.luminosityBlockID());
802  this->enterLumi(gc.luminosityBlockID().run(), gc.luminosityBlockID().luminosityBlock(), /* moduleID */ 0);
803  });
804  ar.watchPostGlobalEndRun([this](edm::GlobalContext const& gc) {
805  this->leaveLumi(gc.luminosityBlockID().run(), /* lumi */ 0, /* moduleID */ 0);
806  });
807  ar.watchPostGlobalEndLumi([this](edm::GlobalContext const& gc) {
808  this->leaveLumi(gc.luminosityBlockID().run(), gc.luminosityBlockID().luminosityBlock(), /* moduleID */ 0);
809  });
810 
811  // Trigger cleanup after writing. This is needed for all modules; we can
812  // only run the cleanup after all output modules have run.
813  ar.watchPostGlobalWriteLumi([this](edm::GlobalContext const& gc) {
815  });
817  [this](edm::GlobalContext const& gc) { this->cleanupLumi(gc.luminosityBlockID().run(), 0); });
818 
819  // no cleanup at end of job, we don't really need it.
820  }
821 
823 
825  LegacyIOHelper h(this);
826  // no run number passed, will save a flat ROOT file (rather than 'Run xxxxxx/.../Run Summary/...')
827  h.save(filename, path);
828  }
829 
831  bool overwrite,
832  std::string const& path,
833  std::string const& prepend,
834  OpenRunDirs stripdirs,
835  bool fileMustExist) {
836  assert(!"NIY");
837  }
838 
839 } // namespace dqm::implementation
DQMStore(edm::ParameterSet const &pset, edm::ActivityRegistry &)
Definition: DQMStore.cc:778
LuminosityBlockNumber_t luminosityBlock() const
virtual DQM_DEPRECATED MonitorElement * getElement(std::string const &path) const
Definition: DQMStore.cc:731
virtual void setCurrentFolder(std::string const &fullpath)
Definition: DQMStore.cc:36
const auto & getMEsToSave() const
Definition: DQMStore.h:756
virtual std::vector< std::string > getMEs() const
Definition: DQMStore.cc:759
virtual std::string pwd()
Definition: DQMStore.cc:20
edm::propagate_const< std::unique_ptr< TH1 > > object_
virtual edm::LuminosityBlockID setRunLumi(edm::LuminosityBlockID runlumi)
Definition: DQMStore.cc:61
virtual MonitorElementData::Scope setScope(MonitorElementData::Scope newscope)
Definition: DQMStore.cc:50
void watchPreGlobalBeginLumi(PreGlobalBeginLumi::slot_type const &iSlot)
virtual bool dirExists(std::string const &path) const
Definition: DQMStore.cc:769
void printTrace(std::string const &message)
Definition: DQMStore.cc:242
std::map< uint64_t, std::set< MonitorElement *, MonitorElement::MEComparison > > localMEs_
Definition: DQMStore.h:784
virtual MonitorElement * bookME(TString const &name, MonitorElementData::Kind kind, std::function< TH1 *()> makeobject, bool forceReplace=false)
Definition: DQMStore.cc:67
edm::LuminosityBlockID runlumi_
Definition: DQMStore.h:559
MonitorElementData::Scope scope_
Definition: DQMStore.h:556
assert(be >=bs)
unsigned int LuminosityBlockNumber_t
MonitorElement * findOrRecycle(MonitorElementData::Key const &)
Definition: DQMStore.cc:349
void free(void *ptr) noexcept
void leaveLumi(edm::RunNumber_t run, edm::LuminosityBlockNumber_t lumi, uint64_t moduleID)
Definition: DQMStore.cc:524
void initLumi(edm::RunNumber_t run, edm::LuminosityBlockNumber_t lumi)
Definition: DQMStore.cc:392
void enterLumi(edm::RunNumber_t run, edm::LuminosityBlockNumber_t lumi, uint64_t moduleID)
Definition: DQMStore.cc:480
std::recursive_mutex booking_mutex_
Definition: DQMStore.h:788
virtual std::vector< dqm::harvesting::MonitorElement * > getAllContents(std::string const &path) const
Definition: DQMStore.cc:641
IGetter(DQMStore *store)
Definition: DQMStore.cc:774
IBooker(DQMStore *store)
Definition: DQMStore.cc:43
key
prepare the HTCondor submission files and eventually submit them
void watchPostGlobalEndLumi(PostGlobalEndLumi::slot_type const &iSlot)
std::map< edm::LuminosityBlockID, std::set< MonitorElement *, MonitorElement::MEComparison > > globalMEs_
Definition: DQMStore.h:780
Definition: logger.py:1
virtual uint64_t setModuleID(uint64_t moduleID)
Definition: DQMStore.cc:55
LuminosityBlockID const & luminosityBlockID() const
Definition: GlobalContext.h:62
MonitorElementData::Scope getScope()
RunNumber_t run() const
void watchPreGlobalBeginRun(PreGlobalBeginRun::slot_type const &iSlot)
Log< level::Info, false > LogInfo
dqm::legacy::MonitorElement MonitorElement
Definition: DQMStore.h:20
demangled
Definition: symbols.py:70
std::string getFullname() const
get full name of ME including Pathname
unsigned long long uint64_t
Definition: Time.h:13
void watchPostGlobalWriteRun(PostGlobalWriteRun::slot_type const &iSlot)
MonitorElement * findME(MELIKE const &path)
Definition: DQMStore.cc:228
void watchPostGlobalEndRun(PostGlobalEndRun::slot_type const &iSlot)
virtual MonitorElement * get(std::string const &fullpath) const
Definition: DQMStore.cc:712
std::vector< std::string > MEsToSave_
Definition: DQMStore.h:801
MonitorElement * putME(MonitorElement *me)
Definition: DQMStore.cc:180
DQM_DEPRECATED void save(std::string const &filename, std::string const &path="")
Definition: DQMStore.cc:824
void watchPostGlobalWriteLumi(PostGlobalEndLumi::slot_type const &iSlot)
const bool & getMode() const
Definition: DQMStore.h:758
static const std::string s_safe
Definition: DQMStore.cc:18
void cleanupLumi(edm::RunNumber_t run, edm::LuminosityBlockNumber_t lumi)
Definition: DQMStore.cc:558
unsigned int RunNumber_t
Log< level::Warning, false > LogWarning
The Signals That Services Can Subscribe To This is based on ActivityRegistry h
Helper function to determine trigger accepts.
Definition: Activities.doc:4
void debugTrackME(const char *message, MonitorElement *me_local, MonitorElement *me_global) const
Definition: DQMStore.cc:311
def move(src, dest)
Definition: eostools.py:511
DQM_DEPRECATED bool open(std::string const &filename, bool overwrite=false, std::string const &path="", std::string const &prepend="", OpenRunDirs stripdirs=KeepRunDirs, bool fileMustExist=true)
Definition: DQMStore.cc:830
virtual std::vector< dqm::harvesting::MonitorElement * > getContents(std::string const &path) const
Definition: DQMStore.cc:625
virtual DQM_DEPRECATED std::vector< std::string > getSubdirs() const
Definition: DQMStore.cc:739