Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2021-10-22 02:31:13

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