/
githubmirror
/
cmssw
Обзор
Документация
Войти
/
githubmirror
/
cmssw
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
DQMServices/Core/src/DQMStore.cc
839 строк
34 KB
Christopher Jones
DQMStore tracing now gives full path to booked ME
18 июл 2025, 18:55
18 июл 2025, 18:55
6d26a21
Код
Авторство
О чём код?
// silence deprecation warnings for the DQMStore itself. #define DQM_DEPRECATED #include "DQMServices/Core/interface/DQMStore.h" #include "DQMServices/Core/interface/LegacyIOHelper.h" #include "FWCore/MessageLogger/interface/MessageLogger.h" #include "FWCore/ServiceRegistry/interface/GlobalContext.h" #include <string> #include <regex> #include <csignal> #include <execinfo.h> #include <cxxabi.h> namespace dqm::implementation { // list of acceptable characters for ME path names, in order to be able to upload to the CMS DQM GUI // See https://github.com/cms-DQM/dqmgui_prod/blob/af0a388e8f57c60e51111585d298aeeea943367f/src/cpp/DQM/DQMStore.cc#L56 static const std::string s_safe = "/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+=_()# "; std::string NavigatorBase::pwd() { if (cwd_.empty()) { return ""; } else { // strip trailing slash. // This is inefficient and error prone (callers need to do the same // branching to re-add the "/"!) but some legacy code expects it like // that and is to complicated to change. assert(cwd_[cwd_.size() - 1] == '/'); auto pwd = cwd_.substr(0, cwd_.size() - 1); return pwd; } } void NavigatorBase::cd() { setCurrentFolder(""); } void NavigatorBase::cd(std::string const& dir) { setCurrentFolder(dir); } void NavigatorBase::goUp() { cd(cwd_ + ".."); } void NavigatorBase::setCurrentFolder(std::string const& fullpath) { MonitorElementData::Path path; path.set(fullpath, MonitorElementData::Path::Type::DIR); assert(this); cwd_ = path.getDirname(); } IBooker::IBooker(DQMStore* store) { store_ = store; scope_ = MonitorElementData::Scope::JOB; } IBooker::~IBooker() {} MonitorElementData::Scope IBooker::setScope(MonitorElementData::Scope newscope) { auto oldscope = scope_; scope_ = newscope; return oldscope; } uint64_t IBooker::setModuleID(uint64_t moduleID) { auto oldid = moduleID_; moduleID_ = moduleID; return oldid; } edm::LuminosityBlockID IBooker::setRunLumi(edm::LuminosityBlockID runlumi) { auto oldrunlumi = runlumi_; runlumi_ = runlumi; return oldrunlumi; } MonitorElement* IBooker::bookME(TString const& name, MonitorElementData::Kind kind, std::function<TH1*()> makeobject, bool forceReplace /* = false */) { MonitorElementData::Path path; std::string fullpath = cwd_ + std::string(name.View()); auto pathToCheck{fullpath}; std::string limiter{".root:/"}; // this indicates that all the substring before is a file name size_t pos = pathToCheck.find(limiter); //find location of limiter //delete everything prior to location found as it might contain illegal chars pathToCheck.erase(0, pos + limiter.size()); if (pathToCheck.find_first_not_of(s_safe) != std::string::npos) { throw cms::Exception("BadMonitorElementPathName") << " Monitor element path name: '" << pathToCheck.c_str() << "' uses unacceptable characters." << "\n Acceptable characters are: " << s_safe.c_str(); } path.set(fullpath, MonitorElementData::Path::Type::DIR_AND_NAME); // We should check if there is a local ME for this module and name already. // However, it is easier to do that in putME(). MonitorElement* me = store_->findME(path); store_->printTrace("Booking " + fullpath + (me ? " (existing)" : " (new)")); if (me == nullptr) { // no existing global ME found. We need to instantiate one, and put it // into the DQMStore. This will typically be a prototype, unless run and // lumi are set and we proces a legacy booking call. TH1* th1 = makeobject(); MonitorElementData medata; medata.key_.path_ = path; medata.key_.kind_ = kind; const auto& MEs = store_->getMEsToSave(); if (not MEs.empty() && not store_->getMode()) { bool pathInList = false; for (const auto& thepath : MEs) { if (fullpath == thepath) { medata.key_.scope_ = MonitorElementData::Scope::LUMI; pathInList = true; break; } } if (not pathInList) medata.key_.scope_ = this->scope_; } else medata.key_.scope_ = this->scope_; // will be (0,0) ( = prototype) in the common case. // This branching is for harvesting, where we have run/lumi in the booker. if (medata.key_.scope_ == MonitorElementData::Scope::JOB) { medata.key_.id_ = edm::LuminosityBlockID(); } else if (medata.key_.scope_ == MonitorElementData::Scope::RUN) { medata.key_.id_ = edm::LuminosityBlockID(this->runlumi_.run(), 0); } else if (medata.key_.scope_ == MonitorElementData::Scope::LUMI) { // In the messy case of legacy-booking a LUMI ME in beginRun (or // similar), where we don't have a valid lumi number yet, make sure to // book a prototype instead. if (this->runlumi_.run() != 0 && this->runlumi_.luminosityBlock() != 0) { medata.key_.id_ = this->runlumi_; } else { medata.key_.id_ = edm::LuminosityBlockID(); } } else { assert(!"Illegal scope"); } medata.value_.object_ = std::unique_ptr<TH1>(th1); MonitorElement* me_ptr = new MonitorElement(std::move(medata)); me = store_->putME(me_ptr); } else { if (forceReplace) { TH1* th1 = makeobject(); assert(th1); store_->debugTrackME("bookME (forceReplace)", nullptr, me); // surgically replace Histogram me->switchObject(std::unique_ptr<TH1>(th1)); } } // me now points to a global ME owned by the DQMStore. assert(me); // each booking call returns a unique "local" ME, which the DQMStore keeps // in a container associated with the module (and potentially run, for // DQMGlobalEDAnalyzer). This will later be update to point to different // MEData (kept in a global ME) as needed. // putME creates the local ME object as needed. auto localme = store_->putME(me, this->moduleID_); // me now points to a local ME owned by the DQMStore. assert(localme); if (this->moduleID_ == 0) { // this is a legacy/global/harvesting booking. In this case, we return // the global directly. It is not advisable to hold this pointer, as we // may delete the global ME later, but we promise to keep it valid for // the entire job if there are no concurrent runs/lumis. (see // assertLegacySafe option). // We still created a local ME, so we can drive the lumi-changing for // legacy modules in watchPreGlobalBeginLumi. store_->debugTrackME("bookME (legacy)", localme, me); return me; } else { // the normal case. store_->debugTrackME("bookME (normal)", localme, me); return localme; } } MonitorElement* DQMStore::putME(MonitorElement* me) { auto lock = std::scoped_lock(this->booking_mutex_); assert(me); auto existing_new = globalMEs_[me->getRunLumi()].insert(me); if (existing_new.second == true) { // successfully inserted, return new object debugTrackME("putME (global)", nullptr, me); return me; } else { // already present, return old object delete me; assert(!"Currently, this should never happen."); return *(existing_new.first); } } MonitorElement* DQMStore::putME(MonitorElement* me, uint64_t moduleID) { auto lock = std::scoped_lock(this->booking_mutex_); assert(me); auto& localmes = localMEs_[moduleID]; auto existing = localmes.find(me); if (existing == localmes.end()) { // insert new local ME MonitorElement* local_me = new MonitorElement(me); auto existing_new = localmes.insert(local_me); // successfully inserted, return new object assert(existing_new.second == true); // insert successful debugTrackME("putME (local, new)", local_me, me); return local_me; } else { // already present, return old object auto local_me = *existing; edm::LogInfo("DQMStore") << "ME " << me->getFullname() << " booked twice in the same module."; // the existing local ME might not have data attached (e.g. in 2nd run) // in that case, we attach the global ME provided by booking above. // This may be a prototype or of a random run/lumi, but it ensures that // even LUMI histos are always valid after booking (as we promise for // legacy modules -- for sequential runs/lumis, there is only ever one // global ME, and the local one points to it). if (!local_me->isValid()) { local_me->switchData(me); } debugTrackME("putME (local, existing)", local_me, me); return local_me; } } template <typename MELIKE> MonitorElement* DQMStore::findME(MELIKE const& path) { auto lock = std::scoped_lock(this->booking_mutex_); for (auto& [runlumi, meset] : this->globalMEs_) { auto it = meset.find(path); if (it != meset.end()) { debugTrackME("findME (found)", nullptr, *it); // no guarantee on which ME we return here -- only that clone'ing this // would give a valid ME for that path. return *it; } } return nullptr; } void DQMStore::printTrace(std::string const& message) { if (verbose_ < 3) return; edm::LogWarning("DQMStoreBooking").log([&](auto& logger) { std::regex s_rxtrace{"(.*)\\((.*)\\+0x.*\\).*(\\[.*\\])"}; std::regex s_rxself{"^[^()]*dqm::implementation::.*|^[^()]*edm::.*|.*edm::convertException::wrap.*"}; void* array[10]; size_t size; char** strings; int demangle_status = 0; std::vector<std::string> clean_trace; // glibc/libgcc backtrace functionality, declared in execinfo.h. size = backtrace(array, 10); strings = backtrace_symbols(array, size); size_t level = 1; char* demangled = nullptr; for (; level < size; ++level) { std::cmatch match; bool ok = std::regex_match(strings[level], match, s_rxtrace); if (!ok) { edm::LogWarning("DQMStoreBacktrace") << "failed match" << level << strings[level]; continue; } if (match[2].length() == 0) { // no symbol, ignore. continue; } // demangle name to human readable form demangled = abi::__cxa_demangle(std::string(match[2]).c_str(), nullptr, nullptr, &demangle_status); if (!demangled || demangle_status != 0) { edm::LogWarning("DQMStoreBacktrace") << "failed demangle! status " << demangle_status << " on " << match[2]; continue; } if (std::regex_match(demangled, s_rxself)) { // ignore framework/internal methods free(demangled); demangled = nullptr; continue; } else { // keep the demangled name and the address. // The address can be resolved to a line number in gdb attached to // the process, using `list *0x<addr>`, but it can only be done in // the running process and we can"t easily do it in this code. clean_trace.push_back(std::string(demangled) + std::string(match[3])); free(demangled); demangled = nullptr; } } if (!clean_trace.empty()) { logger << message << " at "; for (auto const& s : clean_trace) { logger << s << "; "; } } else { logger << message << " : failed to collect stack trace."; } free(strings); }); } void DQMStore::debugTrackME(const char* message, MonitorElement* me_local, MonitorElement* me_global) const { const char* scopename[] = {"INVALID", "JOB", "RUN", "LUMI"}; if (!this->trackME_.empty() && (me_local || me_global)) { std::string name = me_global ? me_global->getFullname() : me_local->getFullname(); if (name.find(this->trackME_) != std::string::npos) { edm::LogWarning("DQMStoreTrackME").log([&](auto& logger) { logger << message << " for " << name << "(" << me_local << "," << me_global << ")"; auto writeme = [&](MonitorElement* me) { if (me->isValid()) { logger << " " << me->getRunLumi() << " scope " << scopename[me->getScope()]; if (me->kind() >= MonitorElement::Kind::TH1F) { logger << " entries " << me->getEntries(); } else if (me->kind() == MonitorElement::Kind::STRING) { logger << " value " << me->getStringValue(); } else if (me->kind() == MonitorElement::Kind::REAL) { logger << " value " << me->getFloatValue(); } else if (me->kind() == MonitorElement::Kind::INT) { logger << " value " << me->getIntValue(); } } else { logger << " (invalid)"; } }; if (me_local) { logger << " local:"; writeme(me_local); } if (me_global) { logger << " global:"; writeme(me_global); } }); // A breakpoint can be useful here. //std::raise(SIGINT); } } } MonitorElement* DQMStore::findOrRecycle(MonitorElementData::Key const& key) { // This is specifically for DQMRootSource, or other input modules. These // are special in that they use the legacy interface (no moduleID, no local // MEs) but need to be able to handle concurrent lumisections correctly. // The logic is very similar to that in enterLumi; this is enterLumi for // Input Modules. auto lock = std::scoped_lock(this->booking_mutex_); auto existing = this->get(key); if (existing) { // exactly matching ME found, needs merging with the new data. debugTrackME("findOrRecycle (found)", nullptr, existing); return existing; } // else // this is where we'd expect the ME. auto& targetset = this->globalMEs_[key.id_]; // this is where we can get MEs to reuse. auto& prototypes = this->globalMEs_[edm::LuminosityBlockID()]; auto proto = prototypes.find(key.path_); if (proto != prototypes.end()) { MonitorElement* oldme = *proto; assert(oldme->getScope() == key.scope_); prototypes.erase(proto); auto medata = oldme->release(); // destroy the ME, get its data. // in this situation, nobody should be filling the ME concurrently. medata->data_.key_.id_ = key.id_; // We reuse the ME object here, even if we don't have to. This ensures // that when running single-threaded without concurrent lumis/runs, // the global MEs will also live forever and allow legacy usages. oldme->switchData(medata); auto result = targetset.insert(oldme); assert(result.second); // was new insertion auto newme = *result.first; // iterator to new ME assert(oldme == newme); // recycling! // newme is reset and ready to accept data. debugTrackME("findOrRecycle (recycled)", nullptr, newme); return newme; } // else return nullptr; } void DQMStore::initLumi(edm::RunNumber_t run, edm::LuminosityBlockNumber_t lumi) { // Call initLumi for all modules, as a global operation. auto lock = std::scoped_lock(this->booking_mutex_); for (auto& kv : this->localMEs_) { initLumi(run, lumi, kv.first); } } void DQMStore::initLumi(edm::RunNumber_t run, edm::LuminosityBlockNumber_t lumi, uint64_t moduleID) { // Make sure global MEs for the run/lumi exist (depending on scope) auto lock = std::scoped_lock(this->booking_mutex_); // these are the MEs we need to update. auto& localset = this->localMEs_[moduleID]; // this is where they need to point to. // This could be a per-run or per-lumi set (depending on lumi == 0) auto& targetset = this->globalMEs_[edm::LuminosityBlockID(run, lumi)]; // this is where we can get MEs to reuse. auto& prototypes = this->globalMEs_[edm::LuminosityBlockID()]; auto checkScope = [run, lumi](MonitorElementData::Scope scope) { if (scope == MonitorElementData::Scope::JOB) { return (run == 0 && lumi == 0); } else if (scope == MonitorElementData::Scope::RUN) { return (run != 0 && lumi == 0); } else if (scope == MonitorElementData::Scope::LUMI) { return (lumi != 0); } assert(!"Impossible Scope."); return false; }; for (MonitorElement* me : localset) { auto target = targetset.find(me); // lookup by path, thanks to MEComparison if (target != targetset.end()) { // we already have a ME, just use it! debugTrackME("initLumi (existing)", nullptr, *target); } else { // look for a prototype to reuse. auto proto = prototypes.find(me); if (proto != prototypes.end()) { // first, check if this ME needs updating at all. We can only check // the scope once we have an actual global ME instance, the local ME // might not have any data attached! if (checkScope((*proto)->getScope()) == false) { continue; } // else // reuse that. MonitorElement* oldme = *proto; prototypes.erase(proto); auto medata = oldme->release(); // destroy the ME, get its data. // in this situation, nobody should be filling the ME concurrently. medata->data_.key_.id_ = edm::LuminosityBlockID(run, lumi); // We reuse the ME object here, even if we don't have to. This ensures // that when running single-threaded without concurrent lumis/runs, // the global MEs will also live forever and allow legacy usages. oldme->switchData(medata); auto result = targetset.insert(oldme); assert(result.second); // was new insertion target = result.first; // iterator to new ME debugTrackME("initLumi (reused)", nullptr, *target); } else { // no prototype available. That means we have concurrent Lumis/Runs, // and need to make a clone now. auto anyme = this->findME(me); assert(anyme || !"local ME without any global ME!"); if (checkScope(anyme->getScope()) == false) { continue; } // else // whenever we clone global MEs, it is no longer safe to hold // pointers to them. assert(!assertLegacySafe_); MonitorElementData newdata = anyme->cloneMEData(); newdata.key_.id_ = edm::LuminosityBlockID(run, lumi); auto newme = new MonitorElement(std::move(newdata)); newme->Reset(); // we cloned a ME in use, not an empty prototype auto result = targetset.insert(newme); assert(result.second); // was new insertion target = result.first; // iterator to new ME debugTrackME("initLumi (allocated)", nullptr, *target); } } } } void DQMStore::enterLumi(edm::RunNumber_t run, edm::LuminosityBlockNumber_t lumi, uint64_t moduleID) { // point the local MEs for this module to these global MEs. // This needs to happen before we can use the global MEs for this run/lumi here. // We could do it lazyly here, or eagerly globally in global begin lumi. //initLumi(run, lumi, moduleID); auto lock = std::scoped_lock(this->booking_mutex_); // these are the MEs we need to update. auto& localset = this->localMEs_[moduleID]; // this is where they need to point to. auto& targetset = this->globalMEs_[edm::LuminosityBlockID(run, lumi)]; // only for a sanity check auto checkScope = [run, lumi](MonitorElementData::Scope scope) { if (scope == MonitorElementData::Scope::JOB) { return (run == 0 && lumi == 0); } else if (scope == MonitorElementData::Scope::RUN) { return (run != 0 && lumi == 0); } else if (scope == MonitorElementData::Scope::LUMI) { return (lumi != 0); } assert(!"Impossible Scope."); return false; }; for (MonitorElement* me : localset) { auto target = targetset.find(me); // lookup by path, thanks to MEComparison if (target == targetset.end()) { auto anyme = this->findME(me); debugTrackME("enterLumi (nothingtodo)", me, nullptr); assert(anyme && checkScope(anyme->getScope()) == false); continue; } assert(target != targetset.end()); // initLumi should have taken care of this. // now we have the proper global ME in the right place, point the local there. // This is only safe if the name is exactly the same -- else it might corrupt // the tree structure of the set! me->switchData(*target); debugTrackME("enterLumi (switchdata)", me, *target); } } void DQMStore::leaveLumi(edm::RunNumber_t run, edm::LuminosityBlockNumber_t lumi, uint64_t moduleID) { // here, we remove the pointers in the local MEs. No deletion or recycling // yet -- this has to happen after the output module had a chance to do its // work. We just leave the global MEs where they are. This is purely an // accounting step, the cleanup code has to check that nobody is using the // ME any more, and here we make sure that is the case. auto lock = std::scoped_lock(this->booking_mutex_); // these are the MEs we need to update. auto& localset = this->localMEs_[moduleID]; auto checkScope = [run, lumi](MonitorElementData::Scope scope) { if (scope == MonitorElementData::Scope::JOB) { return (run == 0 && lumi == 0); } else if (scope == MonitorElementData::Scope::RUN) { return (run != 0 && lumi == 0); } else if (scope == MonitorElementData::Scope::LUMI) { return (lumi != 0); } assert(!"Impossible Scope."); return false; }; for (MonitorElement* me : localset) { // we have to be very careful with the ME here, it might not be backed by data at all. if (me->isValid() && checkScope(me->getScope()) == true) { // if we left the scope, simply release the data. debugTrackME("leaveLumi (release)", me, nullptr); me->release(); } } } void DQMStore::cleanupLumi(edm::RunNumber_t run, edm::LuminosityBlockNumber_t lumi) { // now, we are done with the lumi, no modules have any work to do on these // MEs, and the output modules have saved this lumi/run. Remove/recycle // the MEs here. auto lock = std::scoped_lock(this->booking_mutex_); // in case of end-job cleanup we need different logic because of the // prototype set. assert(run != 0 || lumi != 0); auto& prototypes = this->globalMEs_[edm::LuminosityBlockID()]; // these are the MEs we need to get rid of... auto meset = std::set<MonitorElement*, MonitorElement::MEComparison>(); // ... we take them out first. meset.swap(this->globalMEs_[edm::LuminosityBlockID(run, lumi)]); // temporary buffer for the MEs to recycle, we must not change the key // while they are in a set. auto torecycle = std::vector<MonitorElement*>(); // here, this is only a sanity check and not functionally needed. auto checkScope = [run, lumi](MonitorElementData::Scope scope) { if (scope == MonitorElementData::Scope::JOB) { assert(run == 0 && lumi == 0); } else if (scope == MonitorElementData::Scope::RUN) { assert(run != 0 && lumi == 0); } else if (scope == MonitorElementData::Scope::LUMI) { assert(lumi != 0); } else { assert(!"Impossible Scope."); } }; for (MonitorElement* me : meset) { assert(me->isValid()); // global MEs should always be valid. checkScope(me->getScope()); // we should only see MEs of one scope here. auto other = this->findME(me); if (other) { // we still have a global one, so we can just remove this. debugTrackME("cleanupLumi (delete)", nullptr, me); delete me; } else { // we will modify the ME, so it needs to be out of the set. // use a temporary vector to be save. debugTrackME("cleanupLumi (recycle)", nullptr, me); torecycle.push_back(me); } } meset.clear(); for (MonitorElement* me : torecycle) { auto medata = me->release(); // destroy the ME, get its data. medata->data_.key_.id_ = edm::LuminosityBlockID(); // prototype // We reuse the ME object here, even if we don't have to. This ensures // that when running single-threaded without concurrent lumis/runs, // the global MEs will also live forever and allow legacy usages. me->switchData(medata); // reset here (not later) to still catch random legacy fill calls. me->Reset(); auto result = prototypes.insert(me); assert(result.second); // was new insertion, else findME should succeed debugTrackME("cleanupLumi (reset)", nullptr, me); } } std::vector<dqm::harvesting::MonitorElement*> IGetter::getContents(std::string const& pathname) const { auto lock = std::scoped_lock(store_->booking_mutex_); std::vector<MonitorElement*> out; MonitorElementData::Path path; path.set(pathname, MonitorElementData::Path::Type::DIR); for (auto& [runlumi, meset] : store_->globalMEs_) { auto it = meset.lower_bound(path); while (it != meset.end() && (*it)->getPathname() == path.getDirname()) { store_->debugTrackME("getContents (match)", nullptr, *it); out.push_back(*it); ++it; } } return out; } std::vector<dqm::harvesting::MonitorElement*> IGetter::getAllContents(std::string const& pathname) const { auto lock = std::scoped_lock(store_->booking_mutex_); std::vector<MonitorElement*> out; MonitorElementData::Path path; path.set(pathname, MonitorElementData::Path::Type::DIR); // make sure this is normalized by getting it from Path object. auto path_str = path.getFullname(); for (auto& [runlumi, meset] : store_->globalMEs_) { auto it = meset.lower_bound(path); // rfind can be used as a prefix match. while (it != meset.end() && (*it)->getPathname().rfind(path_str, 0) == 0) { if (runlumi == edm::LuminosityBlockID() && (*it)->getScope() != MonitorElementData::Scope::JOB) { // skip prototypes } else { store_->debugTrackME("getAllContents (match)", nullptr, *it); out.push_back(*it); } ++it; } } return out; } std::vector<dqm::harvesting::MonitorElement*> IGetter::getAllContents(std::string const& pathname, uint32_t runNumber, uint32_t lumi) const { auto lock = std::scoped_lock(store_->booking_mutex_); std::vector<MonitorElement*> out; MonitorElementData::Path path; path.set(pathname, MonitorElementData::Path::Type::DIR); // make sure this is normalized by getting it from Path object. auto path_str = path.getFullname(); auto const& meset = store_->globalMEs_[edm::LuminosityBlockID(runNumber, lumi)]; auto it = meset.lower_bound(path); // decide if the ME should be saved in DQMIO and/or nanoDQMIO // if doSaveByLumi_ is false: store all monitoring elements (needed for harvesting step!) // if doSaveByLumi_ is true: store only selected monitoring elements (i.e. "nanoDQMIO") while (it != meset.end() && (*it)->getFullname().rfind(path_str, 0) == 0) { bool saveIt = true; // default value if doSaveByLumi_ is false if (store_->doSaveByLumi_ && not store_->MEsToSave_.empty()) { std::string name = (*it)->getFullname(); saveIt = false; // default value if doSaveByLumi_ is true for (std::vector<std::string>::const_iterator ipath = store_->MEsToSave_.begin(); ipath != store_->MEsToSave_.end(); ++ipath) { const std::string& nameToSave = *ipath; // option 1 (used in the past): inclusive selection // (store all MEs that contain any of the requested patterns) // if (name.find(nameToSave) != std::string::npos) { // option 2 (current criterion): exact selection // (store only MEs that exactly match a requested pattern) if (name == nameToSave) { saveIt = true; // std::cout<<name<<" compared to"<<ipath->data()<<std::endl; break; } } } store_->debugTrackME("getAllContents (run/lumi match)", nullptr, *it); if (saveIt) { out.push_back(*it); if (store_->doSaveByLumi_) store_->debugTrackME("getAllContents (run/lumi saved)", nullptr, *it); } ++it; } return out; } MonitorElement* IGetter::get(std::string const& fullpath) const { MonitorElementData::Path path; path.set(fullpath, MonitorElementData::Path::Type::DIR_AND_NAME); // this only really makes sense if there is only one instance of this ME, // but the signature of this method also only makes sense in that case. return store_->findME(path); } MonitorElement* IGetter::get(MonitorElementData::Key const& key) const { auto const& meset = store_->globalMEs_[key.id_]; auto it = meset.find(key.path_); if (it != meset.end()) { assert((*it)->getScope() == key.scope_); store_->debugTrackME("get (key found)", nullptr, *it); return *it; } return nullptr; } MonitorElement* IGetter::getElement(std::string const& path) const { auto result = this->get(path); if (result == nullptr) { throw cms::Exception("iGetter Error") << "ME " << path << " was requested but not found."; } return result; } std::vector<std::string> IGetter::getSubdirs() const { // This is terribly inefficient, esp. if this method is then used to // recursively enumerate whatever getAllContents would return anyways. // But that is fine, any such code should just use getAllContents instead. std::set<std::string> subdirs; for (auto me : this->getAllContents(this->cwd_)) { const auto& name = me->getPathname(); auto subdirname = name.substr(this->cwd_.length(), std::string::npos); auto dirname = subdirname.substr(0, subdirname.find('/')); subdirs.insert(dirname); } std::vector<std::string> out; for (const auto& dir : subdirs) { if (dir.empty()) continue; out.push_back(this->cwd_ + dir); } return out; } std::vector<std::string> IGetter::getMEs() const { auto mes = this->getContents(this->cwd_); std::vector<std::string> out; out.reserve(mes.size()); for (auto me : mes) { out.push_back(me->getName()); } return out; } bool IGetter::dirExists(std::string const& path) const { // we don't claim this is fast. return !this->getAllContents(path).empty(); } IGetter::IGetter(DQMStore* store) { store_ = store; } IGetter::~IGetter() {} DQMStore::DQMStore(edm::ParameterSet const& pset, edm::ActivityRegistry& ar) : IGetter(this), IBooker(this) { verbose_ = pset.getUntrackedParameter<int>("verbose", 0); assertLegacySafe_ = pset.getUntrackedParameter<bool>("assertLegacySafe", false); doSaveByLumi_ = pset.getUntrackedParameter<bool>("saveByLumi", false); MEsToSave_ = pset.getUntrackedParameter<std::vector<std::string>>("MEsToSave", std::vector<std::string>()); trackME_ = pset.getUntrackedParameter<std::string>("trackME", ""); onlineMode_ = pset.getUntrackedParameter<bool>("onlineMode", false); // Set lumi and run for legacy booking. // This is no more than a guess with concurrent runs/lumis, but should be // correct for purely sequential legacy stuff. // Also reset Scope, such that legacy modules can expect it to be JOB. // initLumi and leaveLumi are needed for all module types: these handle // creating and deleting global MEs as needed, which has to happen even if // a module does not see lumi transitions. ar.watchPreGlobalBeginRun([this](edm::GlobalContext const& gc) { this->setRunLumi(gc.luminosityBlockID()); this->initLumi(gc.luminosityBlockID().run(), /* lumi */ 0); this->enterLumi(gc.luminosityBlockID().run(), /* lumi */ 0, /* moduleID */ 0); this->setScope(MonitorElementData::Scope::JOB); }); ar.watchPreGlobalBeginLumi([this](edm::GlobalContext const& gc) { this->setRunLumi(gc.luminosityBlockID()); this->initLumi(gc.luminosityBlockID().run(), gc.luminosityBlockID().luminosityBlock()); this->enterLumi(gc.luminosityBlockID().run(), gc.luminosityBlockID().luminosityBlock(), /* moduleID */ 0); }); ar.watchPostGlobalEndRun([this](edm::GlobalContext const& gc) { this->leaveLumi(gc.luminosityBlockID().run(), /* lumi */ 0, /* moduleID */ 0); }); ar.watchPostGlobalEndLumi([this](edm::GlobalContext const& gc) { this->leaveLumi(gc.luminosityBlockID().run(), gc.luminosityBlockID().luminosityBlock(), /* moduleID */ 0); }); // Trigger cleanup after writing. This is needed for all modules; we can // only run the cleanup after all output modules have run. ar.watchPostGlobalWriteLumi([this](edm::GlobalContext const& gc) { this->cleanupLumi(gc.luminosityBlockID().run(), gc.luminosityBlockID().luminosityBlock()); }); ar.watchPostGlobalWriteRun( [this](edm::GlobalContext const& gc) { this->cleanupLumi(gc.luminosityBlockID().run(), 0); }); // no cleanup at end of job, we don't really need it. } DQMStore::~DQMStore() {} void DQMStore::save(std::string const& filename, std::string const& path) { LegacyIOHelper h(this); // no run number passed, will save a flat ROOT file (rather than 'Run xxxxxx/.../Run Summary/...') h.save(filename, path); } bool DQMStore::open(std::string const& filename, bool overwrite, std::string const& path, std::string const& prepend, OpenRunDirs stripdirs, bool fileMustExist) { assert(!"NIY"); } } // namespace dqm::implementation