Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-04-06 12:10:10

0001 // silence deprecation warnings for the DQMStore itself.
0002 #define DQM_DEPRECATED
0003 #include "DQMServices/Core/interface/DQMStore.h"
0004 #include "DQMServices/Core/interface/LegacyIOHelper.h"
0005 #include "FWCore/MessageLogger/interface/MessageLogger.h"
0006 #include "FWCore/ServiceRegistry/interface/GlobalContext.h"
0007 #include <string>
0008 #include <regex>
0009 #include <csignal>
0010 
0011 #include <execinfo.h>
0012 #include <cxxabi.h>
0013 
0014 namespace dqm::implementation {
0015 
0016   // list of acceptable characters for ME path names, in order to be able to upload to the CMS DQM GUI
0017   // See https://github.com/cms-DQM/dqmgui_prod/blob/af0a388e8f57c60e51111585d298aeeea943367f/src/cpp/DQM/DQMStore.cc#L56
0018   static const std::string s_safe = "/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+=_()# ";
0019 
0020   std::string NavigatorBase::pwd() {
0021     if (cwd_.empty()) {
0022       return "";
0023     } else {
0024       // strip trailing slash.
0025       // This is inefficient and error prone (callers need to do the same
0026       // branching to re-add the "/"!) but some legacy code expects it like
0027       // that and is to complicated to change.
0028       assert(cwd_[cwd_.size() - 1] == '/');
0029       auto pwd = cwd_.substr(0, cwd_.size() - 1);
0030       return pwd;
0031     }
0032   }
0033   void NavigatorBase::cd() { setCurrentFolder(""); }
0034   void NavigatorBase::cd(std::string const& dir) { setCurrentFolder(dir); }
0035   void NavigatorBase::goUp() { cd(cwd_ + ".."); }
0036   void NavigatorBase::setCurrentFolder(std::string const& fullpath) {
0037     MonitorElementData::Path path;
0038     path.set(fullpath, MonitorElementData::Path::Type::DIR);
0039     assert(this);
0040     cwd_ = path.getDirname();
0041   }
0042 
0043   IBooker::IBooker(DQMStore* store) {
0044     store_ = store;
0045     scope_ = MonitorElementData::Scope::JOB;
0046   }
0047 
0048   IBooker::~IBooker() {}
0049 
0050   MonitorElementData::Scope IBooker::setScope(MonitorElementData::Scope newscope) {
0051     auto oldscope = scope_;
0052     scope_ = newscope;
0053     return oldscope;
0054   }
0055   uint64_t IBooker::setModuleID(uint64_t moduleID) {
0056     auto oldid = moduleID_;
0057     moduleID_ = moduleID;
0058     return oldid;
0059   }
0060 
0061   edm::LuminosityBlockID IBooker::setRunLumi(edm::LuminosityBlockID runlumi) {
0062     auto oldrunlumi = runlumi_;
0063     runlumi_ = runlumi;
0064     return oldrunlumi;
0065   }
0066 
0067   MonitorElement* IBooker::bookME(TString const& name,
0068                                   MonitorElementData::Kind kind,
0069                                   std::function<TH1*()> makeobject,
0070                                   bool forceReplace /* = false */) {
0071     MonitorElementData::Path path;
0072     std::string fullpath = cwd_ + std::string(name.View());
0073 
0074     auto pathToCheck{fullpath};
0075     std::string limiter{".root:/"};          // this indicates that all the substring before is a file name
0076     size_t pos = pathToCheck.find(limiter);  //find location of limiter
0077     //delete everything prior to location found as it might contain illegal chars
0078     pathToCheck.erase(0, pos + limiter.size());
0079 
0080     if (pathToCheck.find_first_not_of(s_safe) != std::string::npos) {
0081       throw cms::Exception("BadMonitorElementPathName")
0082           << " Monitor element path name: '" << pathToCheck.c_str() << "' uses unacceptable characters."
0083           << "\n Acceptable characters are: " << s_safe.c_str();
0084     }
0085 
0086     path.set(fullpath, MonitorElementData::Path::Type::DIR_AND_NAME);
0087 
0088     // We should check if there is a local ME for this module and name already.
0089     // However, it is easier to do that in putME().
0090 
0091     MonitorElement* me = store_->findME(path);
0092     store_->printTrace("Booking " + std::string(name) + (me ? " (existing)" : " (new)"));
0093 
0094     if (me == nullptr) {
0095       // no existing global ME found. We need to instantiate one, and put it
0096       // into the DQMStore. This will typically be a prototype, unless run and
0097       // lumi are set and we proces a legacy booking call.
0098       TH1* th1 = makeobject();
0099       MonitorElementData medata;
0100       medata.key_.path_ = path;
0101       medata.key_.kind_ = kind;
0102 
0103       const auto& MEs = store_->getMEsToSave();
0104 
0105       if (not MEs.empty() && not store_->getMode()) {
0106         bool pathInList = false;
0107         for (const auto& thepath : MEs) {
0108           if (fullpath == thepath) {
0109             medata.key_.scope_ = MonitorElementData::Scope::LUMI;
0110             pathInList = true;
0111             break;
0112           }
0113         }
0114         if (not pathInList)
0115           medata.key_.scope_ = this->scope_;
0116       } else
0117         medata.key_.scope_ = this->scope_;
0118 
0119       // will be (0,0) ( = prototype) in the common case.
0120       // This branching is for harvesting, where we have run/lumi in the booker.
0121       if (medata.key_.scope_ == MonitorElementData::Scope::JOB) {
0122         medata.key_.id_ = edm::LuminosityBlockID();
0123       } else if (medata.key_.scope_ == MonitorElementData::Scope::RUN) {
0124         medata.key_.id_ = edm::LuminosityBlockID(this->runlumi_.run(), 0);
0125       } else if (medata.key_.scope_ == MonitorElementData::Scope::LUMI) {
0126         // In the messy case of legacy-booking a LUMI ME in beginRun (or
0127         // similar), where we don't have a valid lumi number yet, make sure to
0128         // book a prototype instead.
0129         if (this->runlumi_.run() != 0 && this->runlumi_.luminosityBlock() != 0) {
0130           medata.key_.id_ = this->runlumi_;
0131         } else {
0132           medata.key_.id_ = edm::LuminosityBlockID();
0133         }
0134       } else {
0135         assert(!"Illegal scope");
0136       }
0137 
0138       medata.value_.object_ = std::unique_ptr<TH1>(th1);
0139       MonitorElement* me_ptr = new MonitorElement(std::move(medata));
0140       me = store_->putME(me_ptr);
0141     } else {
0142       if (forceReplace) {
0143         TH1* th1 = makeobject();
0144         assert(th1);
0145         store_->debugTrackME("bookME (forceReplace)", nullptr, me);
0146         // surgically replace Histogram
0147         me->switchObject(std::unique_ptr<TH1>(th1));
0148       }
0149     }
0150 
0151     // me now points to a global ME owned by the DQMStore.
0152     assert(me);
0153 
0154     // each booking call returns a unique "local" ME, which the DQMStore keeps
0155     // in a container associated with the module (and potentially run, for
0156     // DQMGlobalEDAnalyzer). This will later be update to point to different
0157     // MEData (kept in a global ME) as needed.
0158     // putME creates the local ME object as needed.
0159     auto localme = store_->putME(me, this->moduleID_);
0160     // me now points to a local ME owned by the DQMStore.
0161     assert(localme);
0162 
0163     if (this->moduleID_ == 0) {
0164       // this is a legacy/global/harvesting booking. In this case, we return
0165       // the global directly. It is not advisable to hold this pointer, as we
0166       // may delete the global ME later, but we promise to keep it valid for
0167       // the entire job if there are no concurrent runs/lumis. (see
0168       // assertLegacySafe option).
0169       // We still created a local ME, so we can drive the lumi-changing for
0170       // legacy modules in watchPreGlobalBeginLumi.
0171       store_->debugTrackME("bookME (legacy)", localme, me);
0172       return me;
0173     } else {
0174       // the normal case.
0175       store_->debugTrackME("bookME (normal)", localme, me);
0176       return localme;
0177     }
0178   }
0179 
0180   MonitorElement* DQMStore::putME(MonitorElement* me) {
0181     auto lock = std::scoped_lock(this->booking_mutex_);
0182     assert(me);
0183     auto existing_new = globalMEs_[me->getRunLumi()].insert(me);
0184     if (existing_new.second == true) {
0185       // successfully inserted, return new object
0186       debugTrackME("putME (global)", nullptr, me);
0187       return me;
0188     } else {
0189       // already present, return old object
0190       delete me;
0191       assert(!"Currently, this should never happen.");
0192       return *(existing_new.first);
0193     }
0194   }
0195 
0196   MonitorElement* DQMStore::putME(MonitorElement* me, uint64_t moduleID) {
0197     auto lock = std::scoped_lock(this->booking_mutex_);
0198     assert(me);
0199     auto& localmes = localMEs_[moduleID];
0200     auto existing = localmes.find(me);
0201     if (existing == localmes.end()) {
0202       // insert new local ME
0203       MonitorElement* local_me = new MonitorElement(me);
0204       auto existing_new = localmes.insert(local_me);
0205       // successfully inserted, return new object
0206       assert(existing_new.second == true);  // insert successful
0207       debugTrackME("putME (local, new)", local_me, me);
0208       return local_me;
0209     } else {
0210       // already present, return old object
0211       auto local_me = *existing;
0212       edm::LogInfo("DQMStore") << "ME " << me->getFullname() << " booked twice in the same module.";
0213       // the existing local ME might not have data attached (e.g. in 2nd run)
0214       // in that case, we attach the global ME provided by booking above.
0215       // This may be a prototype or of a random run/lumi, but it ensures that
0216       // even LUMI histos are always valid after booking (as we promise for
0217       // legacy modules -- for sequential runs/lumis, there is only ever one
0218       // global ME, and the local one points to it).
0219       if (!local_me->isValid()) {
0220         local_me->switchData(me);
0221       }
0222       debugTrackME("putME (local, existing)", local_me, me);
0223       return local_me;
0224     }
0225   }
0226 
0227   template <typename MELIKE>
0228   MonitorElement* DQMStore::findME(MELIKE const& path) {
0229     auto lock = std::scoped_lock(this->booking_mutex_);
0230     for (auto& [runlumi, meset] : this->globalMEs_) {
0231       auto it = meset.find(path);
0232       if (it != meset.end()) {
0233         debugTrackME("findME (found)", nullptr, *it);
0234         // no guarantee on which ME we return here -- only that clone'ing this
0235         // would give a valid ME for that path.
0236         return *it;
0237       }
0238     }
0239     return nullptr;
0240   }
0241 
0242   void DQMStore::printTrace(std::string const& message) {
0243     if (verbose_ < 3)
0244       return;
0245     edm::LogWarning("DQMStoreBooking").log([&](auto& logger) {
0246       std::regex s_rxtrace{"(.*)\\((.*)\\+0x.*\\).*(\\[.*\\])"};
0247       std::regex s_rxself{"^[^()]*dqm::implementation::.*|^[^()]*edm::.*|.*edm::convertException::wrap.*"};
0248 
0249       void* array[10];
0250       size_t size;
0251       char** strings;
0252       int demangle_status = 0;
0253       std::vector<std::string> clean_trace;
0254 
0255       // glibc/libgcc backtrace functionality, declared in execinfo.h.
0256       size = backtrace(array, 10);
0257       strings = backtrace_symbols(array, size);
0258 
0259       size_t level = 1;
0260       char* demangled = nullptr;
0261       for (; level < size; ++level) {
0262         std::cmatch match;
0263         bool ok = std::regex_match(strings[level], match, s_rxtrace);
0264 
0265         if (!ok) {
0266           edm::LogWarning("DQMStoreBacktrace") << "failed match" << level << strings[level];
0267           continue;
0268         }
0269 
0270         if (match[2].length() == 0) {
0271           // no symbol, ignore.
0272           continue;
0273         }
0274 
0275         // demangle name to human readable form
0276         demangled = abi::__cxa_demangle(std::string(match[2]).c_str(), nullptr, nullptr, &demangle_status);
0277         if (!demangled || demangle_status != 0) {
0278           edm::LogWarning("DQMStoreBacktrace") << "failed demangle! status " << demangle_status << " on " << match[2];
0279           continue;
0280         }
0281 
0282         if (std::regex_match(demangled, s_rxself)) {
0283           // ignore framework/internal methods
0284           free(demangled);
0285           demangled = nullptr;
0286           continue;
0287         } else {
0288           // keep the demangled name and the address.
0289           // The address can be resolved to a line number in gdb attached to
0290           // the process, using `list *0x<addr>`, but it can only be done in
0291           // the running process and we can"t easily do it in this code.
0292           clean_trace.push_back(std::string(demangled) + std::string(match[3]));
0293           free(demangled);
0294           demangled = nullptr;
0295         }
0296       }
0297 
0298       if (!clean_trace.empty()) {
0299         logger << message << " at ";
0300         for (auto const& s : clean_trace) {
0301           logger << s << "; ";
0302         }
0303       } else {
0304         logger << message << " : failed to collect stack trace.";
0305       }
0306 
0307       free(strings);
0308     });
0309   }
0310 
0311   void DQMStore::debugTrackME(const char* message, MonitorElement* me_local, MonitorElement* me_global) const {
0312     const char* scopename[] = {"INVALID", "JOB", "RUN", "LUMI"};
0313     if (!this->trackME_.empty() && (me_local || me_global)) {
0314       std::string name = me_global ? me_global->getFullname() : me_local->getFullname();
0315       if (name.find(this->trackME_) != std::string::npos) {
0316         edm::LogWarning("DQMStoreTrackME").log([&](auto& logger) {
0317           logger << message << " for " << name << "(" << me_local << "," << me_global << ")";
0318           auto writeme = [&](MonitorElement* me) {
0319             if (me->isValid()) {
0320               logger << " " << me->getRunLumi() << " scope " << scopename[me->getScope()];
0321               if (me->kind() >= MonitorElement::Kind::TH1F) {
0322                 logger << " entries " << me->getEntries();
0323               } else if (me->kind() == MonitorElement::Kind::STRING) {
0324                 logger << " value " << me->getStringValue();
0325               } else if (me->kind() == MonitorElement::Kind::REAL) {
0326                 logger << " value " << me->getFloatValue();
0327               } else if (me->kind() == MonitorElement::Kind::INT) {
0328                 logger << " value " << me->getIntValue();
0329               }
0330             } else {
0331               logger << " (invalid)";
0332             }
0333           };
0334           if (me_local) {
0335             logger << "  local:";
0336             writeme(me_local);
0337           }
0338           if (me_global) {
0339             logger << "  global:";
0340             writeme(me_global);
0341           }
0342         });
0343         // A breakpoint can be useful here.
0344         //std::raise(SIGINT);
0345       }
0346     }
0347   }
0348 
0349   MonitorElement* DQMStore::findOrRecycle(MonitorElementData::Key const& key) {
0350     // This is specifically for DQMRootSource, or other input modules. These
0351     // are special in that they use the legacy interface (no moduleID, no local
0352     // MEs) but need to be able to handle concurrent lumisections correctly.
0353     // The logic is very similar to that in enterLumi; this is enterLumi for
0354     // Input Modules.
0355     auto lock = std::scoped_lock(this->booking_mutex_);
0356     auto existing = this->get(key);
0357     if (existing) {
0358       // exactly matching ME found, needs merging with the new data.
0359       debugTrackME("findOrRecycle (found)", nullptr, existing);
0360       return existing;
0361     }  // else
0362 
0363     // this is where we'd expect the ME.
0364     auto& targetset = this->globalMEs_[key.id_];
0365     // this is where we can get MEs to reuse.
0366     auto& prototypes = this->globalMEs_[edm::LuminosityBlockID()];
0367 
0368     auto proto = prototypes.find(key.path_);
0369     if (proto != prototypes.end()) {
0370       MonitorElement* oldme = *proto;
0371       assert(oldme->getScope() == key.scope_);
0372       prototypes.erase(proto);
0373       auto medata = oldme->release();  // destroy the ME, get its data.
0374       // in this situation, nobody should be filling the ME concurrently.
0375       medata->data_.key_.id_ = key.id_;
0376       // We reuse the ME object here, even if we don't have to. This ensures
0377       // that when running single-threaded without concurrent lumis/runs,
0378       // the global MEs will also live forever and allow legacy usages.
0379       oldme->switchData(medata);
0380       auto result = targetset.insert(oldme);
0381       assert(result.second);       // was new insertion
0382       auto newme = *result.first;  // iterator to new ME
0383       assert(oldme == newme);      // recycling!
0384       // newme is reset and ready to accept data.
0385       debugTrackME("findOrRecycle (recycled)", nullptr, newme);
0386       return newme;
0387     }  // else
0388 
0389     return nullptr;
0390   }
0391 
0392   void DQMStore::initLumi(edm::RunNumber_t run, edm::LuminosityBlockNumber_t lumi) {
0393     // Call initLumi for all modules, as a global operation.
0394     auto lock = std::scoped_lock(this->booking_mutex_);
0395     for (auto& kv : this->localMEs_) {
0396       initLumi(run, lumi, kv.first);
0397     }
0398   }
0399 
0400   void DQMStore::initLumi(edm::RunNumber_t run, edm::LuminosityBlockNumber_t lumi, uint64_t moduleID) {
0401     // Make sure global MEs for the run/lumi exist (depending on scope)
0402 
0403     auto lock = std::scoped_lock(this->booking_mutex_);
0404 
0405     // these are the MEs we need to update.
0406     auto& localset = this->localMEs_[moduleID];
0407     // this is where they need to point to.
0408     // This could be a per-run or per-lumi set (depending on lumi == 0)
0409     auto& targetset = this->globalMEs_[edm::LuminosityBlockID(run, lumi)];
0410     // this is where we can get MEs to reuse.
0411     auto& prototypes = this->globalMEs_[edm::LuminosityBlockID()];
0412 
0413     auto checkScope = [run, lumi](MonitorElementData::Scope scope) {
0414       if (scope == MonitorElementData::Scope::JOB) {
0415         return (run == 0 && lumi == 0);
0416       } else if (scope == MonitorElementData::Scope::RUN) {
0417         return (run != 0 && lumi == 0);
0418       } else if (scope == MonitorElementData::Scope::LUMI) {
0419         return (lumi != 0);
0420       }
0421       assert(!"Impossible Scope.");
0422       return false;
0423     };
0424 
0425     for (MonitorElement* me : localset) {
0426       auto target = targetset.find(me);  // lookup by path, thanks to MEComparison
0427       if (target != targetset.end()) {
0428         // we already have a ME, just use it!
0429         debugTrackME("initLumi (existing)", nullptr, *target);
0430       } else {
0431         // look for a prototype to reuse.
0432         auto proto = prototypes.find(me);
0433         if (proto != prototypes.end()) {
0434           // first, check if this ME needs updating at all. We can only check
0435           // the scope once we have an actual global ME instance, the local ME
0436           // might not have any data attached!
0437           if (checkScope((*proto)->getScope()) == false) {
0438             continue;
0439           }  // else
0440           // reuse that.
0441           MonitorElement* oldme = *proto;
0442           prototypes.erase(proto);
0443           auto medata = oldme->release();  // destroy the ME, get its data.
0444           // in this situation, nobody should be filling the ME concurrently.
0445           medata->data_.key_.id_ = edm::LuminosityBlockID(run, lumi);
0446           // We reuse the ME object here, even if we don't have to. This ensures
0447           // that when running single-threaded without concurrent lumis/runs,
0448           // the global MEs will also live forever and allow legacy usages.
0449           oldme->switchData(medata);
0450           auto result = targetset.insert(oldme);
0451           assert(result.second);  // was new insertion
0452           target = result.first;  // iterator to new ME
0453           debugTrackME("initLumi (reused)", nullptr, *target);
0454         } else {
0455           // no prototype available. That means we have concurrent Lumis/Runs,
0456           // and need to make a clone now.
0457           auto anyme = this->findME(me);
0458           assert(anyme || !"local ME without any global ME!");
0459           if (checkScope(anyme->getScope()) == false) {
0460             continue;
0461           }  // else
0462 
0463           // whenever we clone global MEs, it is no longer safe to hold
0464           // pointers to them.
0465           assert(!assertLegacySafe_);
0466 
0467           MonitorElementData newdata = anyme->cloneMEData();
0468           newdata.key_.id_ = edm::LuminosityBlockID(run, lumi);
0469           auto newme = new MonitorElement(std::move(newdata));
0470           newme->Reset();  // we cloned a ME in use, not an empty prototype
0471           auto result = targetset.insert(newme);
0472           assert(result.second);  // was new insertion
0473           target = result.first;  // iterator to new ME
0474           debugTrackME("initLumi (allocated)", nullptr, *target);
0475         }
0476       }
0477     }
0478   }
0479 
0480   void DQMStore::enterLumi(edm::RunNumber_t run, edm::LuminosityBlockNumber_t lumi, uint64_t moduleID) {
0481     // point the local MEs for this module to these global MEs.
0482 
0483     // This needs to happen before we can use the global MEs for this run/lumi here.
0484     // We could do it lazyly here, or eagerly globally in global begin lumi.
0485     //initLumi(run, lumi, moduleID);
0486 
0487     auto lock = std::scoped_lock(this->booking_mutex_);
0488 
0489     // these are the MEs we need to update.
0490     auto& localset = this->localMEs_[moduleID];
0491     // this is where they need to point to.
0492     auto& targetset = this->globalMEs_[edm::LuminosityBlockID(run, lumi)];
0493 
0494     // only for a sanity check
0495     auto checkScope = [run, lumi](MonitorElementData::Scope scope) {
0496       if (scope == MonitorElementData::Scope::JOB) {
0497         return (run == 0 && lumi == 0);
0498       } else if (scope == MonitorElementData::Scope::RUN) {
0499         return (run != 0 && lumi == 0);
0500       } else if (scope == MonitorElementData::Scope::LUMI) {
0501         return (lumi != 0);
0502       }
0503       assert(!"Impossible Scope.");
0504       return false;
0505     };
0506 
0507     for (MonitorElement* me : localset) {
0508       auto target = targetset.find(me);  // lookup by path, thanks to MEComparison
0509       if (target == targetset.end()) {
0510         auto anyme = this->findME(me);
0511         debugTrackME("enterLumi (nothingtodo)", me, nullptr);
0512         assert(anyme && checkScope(anyme->getScope()) == false);
0513         continue;
0514       }
0515       assert(target != targetset.end());  // initLumi should have taken care of this.
0516       // now we have the proper global ME in the right place, point the local there.
0517       // This is only safe if the name is exactly the same -- else it might corrupt
0518       // the tree structure of the set!
0519       me->switchData(*target);
0520       debugTrackME("enterLumi (switchdata)", me, *target);
0521     }
0522   }
0523 
0524   void DQMStore::leaveLumi(edm::RunNumber_t run, edm::LuminosityBlockNumber_t lumi, uint64_t moduleID) {
0525     // here, we remove the pointers in the local MEs. No deletion or recycling
0526     // yet -- this has to happen after the output module had a chance to do its
0527     // work. We just leave the global MEs where they are. This is purely an
0528     // accounting step, the cleanup code has to check that nobody is using the
0529     // ME any more, and here we make sure that is the case.
0530 
0531     auto lock = std::scoped_lock(this->booking_mutex_);
0532 
0533     // these are the MEs we need to update.
0534     auto& localset = this->localMEs_[moduleID];
0535 
0536     auto checkScope = [run, lumi](MonitorElementData::Scope scope) {
0537       if (scope == MonitorElementData::Scope::JOB) {
0538         return (run == 0 && lumi == 0);
0539       } else if (scope == MonitorElementData::Scope::RUN) {
0540         return (run != 0 && lumi == 0);
0541       } else if (scope == MonitorElementData::Scope::LUMI) {
0542         return (lumi != 0);
0543       }
0544       assert(!"Impossible Scope.");
0545       return false;
0546     };
0547 
0548     for (MonitorElement* me : localset) {
0549       // we have to be very careful with the ME here, it might not be backed by data at all.
0550       if (me->isValid() && checkScope(me->getScope()) == true) {
0551         // if we left the scope, simply release the data.
0552         debugTrackME("leaveLumi (release)", me, nullptr);
0553         me->release();
0554       }
0555     }
0556   }
0557 
0558   void DQMStore::cleanupLumi(edm::RunNumber_t run, edm::LuminosityBlockNumber_t lumi) {
0559     // now, we are done with the lumi, no modules have any work to do on these
0560     // MEs, and the output modules have saved this lumi/run. Remove/recycle
0561     // the MEs here.
0562 
0563     auto lock = std::scoped_lock(this->booking_mutex_);
0564 
0565     // in case of end-job cleanup we need different logic because of the
0566     // prototype set.
0567     assert(run != 0 || lumi != 0);
0568     auto& prototypes = this->globalMEs_[edm::LuminosityBlockID()];
0569 
0570     // these are the MEs we need to get rid of...
0571     auto meset = std::set<MonitorElement*, MonitorElement::MEComparison>();
0572     // ... we take them out first.
0573     meset.swap(this->globalMEs_[edm::LuminosityBlockID(run, lumi)]);
0574 
0575     // temporary buffer for the MEs to recycle, we must not change the key
0576     // while they are in a set.
0577     auto torecycle = std::vector<MonitorElement*>();
0578 
0579     // here, this is only a sanity check and not functionally needed.
0580     auto checkScope = [run, lumi](MonitorElementData::Scope scope) {
0581       if (scope == MonitorElementData::Scope::JOB) {
0582         assert(run == 0 && lumi == 0);
0583       } else if (scope == MonitorElementData::Scope::RUN) {
0584         assert(run != 0 && lumi == 0);
0585       } else if (scope == MonitorElementData::Scope::LUMI) {
0586         assert(lumi != 0);
0587       } else {
0588         assert(!"Impossible Scope.");
0589       }
0590     };
0591 
0592     for (MonitorElement* me : meset) {
0593       assert(me->isValid());       // global MEs should always be valid.
0594       checkScope(me->getScope());  // we should only see MEs of one scope here.
0595       auto other = this->findME(me);
0596       if (other) {
0597         // we still have a global one, so we can just remove this.
0598         debugTrackME("cleanupLumi (delete)", nullptr, me);
0599         delete me;
0600       } else {
0601         // we will modify the ME, so it needs to be out of the set.
0602         // use a temporary vector to be save.
0603         debugTrackME("cleanupLumi (recycle)", nullptr, me);
0604         torecycle.push_back(me);
0605       }
0606     }
0607 
0608     meset.clear();
0609 
0610     for (MonitorElement* me : torecycle) {
0611       auto medata = me->release();                        // destroy the ME, get its data.
0612       medata->data_.key_.id_ = edm::LuminosityBlockID();  // prototype
0613       // We reuse the ME object here, even if we don't have to. This ensures
0614       // that when running single-threaded without concurrent lumis/runs,
0615       // the global MEs will also live forever and allow legacy usages.
0616       me->switchData(medata);
0617       // reset here (not later) to still catch random legacy fill calls.
0618       me->Reset();
0619       auto result = prototypes.insert(me);
0620       assert(result.second);  // was new insertion, else findME should succeed
0621       debugTrackME("cleanupLumi (reset)", nullptr, me);
0622     }
0623   }
0624 
0625   std::vector<dqm::harvesting::MonitorElement*> IGetter::getContents(std::string const& pathname) const {
0626     auto lock = std::scoped_lock(store_->booking_mutex_);
0627     std::vector<MonitorElement*> out;
0628     MonitorElementData::Path path;
0629     path.set(pathname, MonitorElementData::Path::Type::DIR);
0630     for (auto& [runlumi, meset] : store_->globalMEs_) {
0631       auto it = meset.lower_bound(path);
0632       while (it != meset.end() && (*it)->getPathname() == path.getDirname()) {
0633         store_->debugTrackME("getContents (match)", nullptr, *it);
0634         out.push_back(*it);
0635         ++it;
0636       }
0637     }
0638     return out;
0639   }
0640 
0641   std::vector<dqm::harvesting::MonitorElement*> IGetter::getAllContents(std::string const& pathname) const {
0642     auto lock = std::scoped_lock(store_->booking_mutex_);
0643     std::vector<MonitorElement*> out;
0644     MonitorElementData::Path path;
0645     path.set(pathname, MonitorElementData::Path::Type::DIR);
0646     // make sure this is normalized by getting it from Path object.
0647     auto path_str = path.getFullname();
0648     for (auto& [runlumi, meset] : store_->globalMEs_) {
0649       auto it = meset.lower_bound(path);
0650       // rfind can be used as a prefix match.
0651       while (it != meset.end() && (*it)->getPathname().rfind(path_str, 0) == 0) {
0652         if (runlumi == edm::LuminosityBlockID() && (*it)->getScope() != MonitorElementData::Scope::JOB) {
0653           // skip prototypes
0654         } else {
0655           store_->debugTrackME("getAllContents (match)", nullptr, *it);
0656           out.push_back(*it);
0657         }
0658         ++it;
0659       }
0660     }
0661     return out;
0662   }
0663   std::vector<dqm::harvesting::MonitorElement*> IGetter::getAllContents(std::string const& pathname,
0664                                                                         uint32_t runNumber,
0665                                                                         uint32_t lumi) const {
0666     auto lock = std::scoped_lock(store_->booking_mutex_);
0667     std::vector<MonitorElement*> out;
0668     MonitorElementData::Path path;
0669     path.set(pathname, MonitorElementData::Path::Type::DIR);
0670     // make sure this is normalized by getting it from Path object.
0671     auto path_str = path.getFullname();
0672     auto const& meset = store_->globalMEs_[edm::LuminosityBlockID(runNumber, lumi)];
0673     auto it = meset.lower_bound(path);
0674 
0675     // decide if the ME should be saved in DQMIO and/or nanoDQMIO
0676     // if doSaveByLumi_ is false: store all monitoring elements (needed for harvesting step!)
0677     // if doSaveByLumi_ is true: store only selected monitoring elements (i.e. "nanoDQMIO")
0678     while (it != meset.end() && (*it)->getFullname().rfind(path_str, 0) == 0) {
0679       bool saveIt = true;  // default value if doSaveByLumi_ is false
0680 
0681       if (store_->doSaveByLumi_ && not store_->MEsToSave_.empty()) {
0682         std::string name = (*it)->getFullname();
0683         saveIt = false;  // default value if doSaveByLumi_ is true
0684         for (std::vector<std::string>::const_iterator ipath = store_->MEsToSave_.begin();
0685              ipath != store_->MEsToSave_.end();
0686              ++ipath) {
0687           const std::string& nameToSave = *ipath;
0688           // option 1 (used in the past): inclusive selection
0689           // (store all MEs that contain any of the requested patterns)
0690           // if (name.find(nameToSave) != std::string::npos) {
0691           // option 2 (current criterion): exact selection
0692           // (store only MEs that exactly match a requested pattern)
0693           if (name == nameToSave) {
0694             saveIt = true;
0695             // std::cout<<name<<" compared to"<<ipath->data()<<std::endl;
0696             break;
0697           }
0698         }
0699       }
0700 
0701       store_->debugTrackME("getAllContents (run/lumi match)", nullptr, *it);
0702       if (saveIt) {
0703         out.push_back(*it);
0704         if (store_->doSaveByLumi_)
0705           store_->debugTrackME("getAllContents (run/lumi saved)", nullptr, *it);
0706       }
0707       ++it;
0708     }
0709     return out;
0710   }
0711 
0712   MonitorElement* IGetter::get(std::string const& fullpath) const {
0713     MonitorElementData::Path path;
0714     path.set(fullpath, MonitorElementData::Path::Type::DIR_AND_NAME);
0715     // this only really makes sense if there is only one instance of this ME,
0716     // but the signature of this method also only makes sense in that case.
0717     return store_->findME(path);
0718   }
0719 
0720   MonitorElement* IGetter::get(MonitorElementData::Key const& key) const {
0721     auto const& meset = store_->globalMEs_[key.id_];
0722     auto it = meset.find(key.path_);
0723     if (it != meset.end()) {
0724       assert((*it)->getScope() == key.scope_);
0725       store_->debugTrackME("get (key found)", nullptr, *it);
0726       return *it;
0727     }
0728     return nullptr;
0729   }
0730 
0731   MonitorElement* IGetter::getElement(std::string const& path) const {
0732     auto result = this->get(path);
0733     if (result == nullptr) {
0734       throw cms::Exception("iGetter Error") << "ME " << path << " was requested but not found.";
0735     }
0736     return result;
0737   }
0738 
0739   std::vector<std::string> IGetter::getSubdirs() const {
0740     // This is terribly inefficient, esp. if this method is then used to
0741     // recursively enumerate whatever getAllContents would return anyways.
0742     // But that is fine, any such code should just use getAllContents instead.
0743     std::set<std::string> subdirs;
0744     for (auto me : this->getAllContents(this->cwd_)) {
0745       const auto& name = me->getPathname();
0746       auto subdirname = name.substr(this->cwd_.length(), std::string::npos);
0747       auto dirname = subdirname.substr(0, subdirname.find('/'));
0748       subdirs.insert(dirname);
0749     }
0750     std::vector<std::string> out;
0751     for (const auto& dir : subdirs) {
0752       if (dir.length() == 0)
0753         continue;
0754       out.push_back(this->cwd_ + dir);
0755     }
0756     return out;
0757   }
0758 
0759   std::vector<std::string> IGetter::getMEs() const {
0760     auto mes = this->getContents(this->cwd_);
0761     std::vector<std::string> out;
0762     out.reserve(mes.size());
0763     for (auto me : mes) {
0764       out.push_back(me->getName());
0765     }
0766     return out;
0767   }
0768 
0769   bool IGetter::dirExists(std::string const& path) const {
0770     // we don't claim this is fast.
0771     return !this->getAllContents(path).empty();
0772   }
0773 
0774   IGetter::IGetter(DQMStore* store) { store_ = store; }
0775 
0776   IGetter::~IGetter() {}
0777 
0778   DQMStore::DQMStore(edm::ParameterSet const& pset, edm::ActivityRegistry& ar) : IGetter(this), IBooker(this) {
0779     verbose_ = pset.getUntrackedParameter<int>("verbose", 0);
0780     assertLegacySafe_ = pset.getUntrackedParameter<bool>("assertLegacySafe", false);
0781     doSaveByLumi_ = pset.getUntrackedParameter<bool>("saveByLumi", false);
0782     MEsToSave_ = pset.getUntrackedParameter<std::vector<std::string>>("MEsToSave", std::vector<std::string>());
0783     trackME_ = pset.getUntrackedParameter<std::string>("trackME", "");
0784     onlineMode_ = pset.getUntrackedParameter<bool>("onlineMode", false);
0785 
0786     // Set lumi and run for legacy booking.
0787     // This is no more than a guess with concurrent runs/lumis, but should be
0788     // correct for purely sequential legacy stuff.
0789     // Also reset Scope, such that legacy modules can expect it to be JOB.
0790     // initLumi and leaveLumi are needed for all module types: these handle
0791     // creating and deleting global MEs as needed, which has to happen even if
0792     // a module does not see lumi transitions.
0793     ar.watchPreGlobalBeginRun([this](edm::GlobalContext const& gc) {
0794       this->setRunLumi(gc.luminosityBlockID());
0795       this->initLumi(gc.luminosityBlockID().run(), /* lumi */ 0);
0796       this->enterLumi(gc.luminosityBlockID().run(), /* lumi */ 0, /* moduleID */ 0);
0797       this->setScope(MonitorElementData::Scope::JOB);
0798     });
0799     ar.watchPreGlobalBeginLumi([this](edm::GlobalContext const& gc) {
0800       this->setRunLumi(gc.luminosityBlockID());
0801       this->initLumi(gc.luminosityBlockID().run(), gc.luminosityBlockID().luminosityBlock());
0802       this->enterLumi(gc.luminosityBlockID().run(), gc.luminosityBlockID().luminosityBlock(), /* moduleID */ 0);
0803     });
0804     ar.watchPostGlobalEndRun([this](edm::GlobalContext const& gc) {
0805       this->leaveLumi(gc.luminosityBlockID().run(), /* lumi */ 0, /* moduleID */ 0);
0806     });
0807     ar.watchPostGlobalEndLumi([this](edm::GlobalContext const& gc) {
0808       this->leaveLumi(gc.luminosityBlockID().run(), gc.luminosityBlockID().luminosityBlock(), /* moduleID */ 0);
0809     });
0810 
0811     // Trigger cleanup after writing. This is needed for all modules; we can
0812     // only run the cleanup after all output modules have run.
0813     ar.watchPostGlobalWriteLumi([this](edm::GlobalContext const& gc) {
0814       this->cleanupLumi(gc.luminosityBlockID().run(), gc.luminosityBlockID().luminosityBlock());
0815     });
0816     ar.watchPostGlobalWriteRun(
0817         [this](edm::GlobalContext const& gc) { this->cleanupLumi(gc.luminosityBlockID().run(), 0); });
0818 
0819     // no cleanup at end of job, we don't really need it.
0820   }
0821 
0822   DQMStore::~DQMStore() {}
0823 
0824   void DQMStore::save(std::string const& filename, std::string const& path) {
0825     LegacyIOHelper h(this);
0826     // no run number passed, will save a flat ROOT file (rather than 'Run xxxxxx/.../Run Summary/...')
0827     h.save(filename, path);
0828   }
0829 
0830   bool DQMStore::open(std::string const& filename,
0831                       bool overwrite,
0832                       std::string const& path,
0833                       std::string const& prepend,
0834                       OpenRunDirs stripdirs,
0835                       bool fileMustExist) {
0836     assert(!"NIY");
0837   }
0838 
0839 }  // namespace dqm::implementation