CMS 3D CMS Logo

 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
CondorStatusUpdater.cc
Go to the documentation of this file.
1 
18 
19 #include <fcntl.h>
20 #include <unistd.h>
21 #include <sys/wait.h>
22 #include <spawn.h>
23 #include <iostream>
24 #include <fstream>
25 #include <sstream>
26 #include <cmath>
27 #include <chrono>
28 #include <sstream>
29 #include <atomic>
30 #include <string>
31 #include <set>
32 
33 namespace edm {
34 
35  namespace service {
36 
38  public:
41  CondorStatusService(const CondorStatusService &) = delete;
43 
44  static void fillDescriptions(ConfigurationDescriptions &descriptions);
45 
46  private:
47  bool isChirpSupported();
48  template <typename T>
49  bool updateChirp(const std::string &key_suffix, const T &value);
50  bool updateChirpQuoted(const std::string &key_suffix, const std::string &value);
51  bool updateChirpImpl(std::string const &key, std::string const &value);
52  inline void update();
53  void firstUpdate();
54  void lastUpdate();
55  void updateImpl(time_t secsSinceLastUpdate);
56 
57  void preSourceConstruction(ModuleDescription const &md, int maxEvents, int maxLumis, int maxSecondsUntilRampdown);
58  void eventPost(StreamContext const &iContext);
59  void lumiPost(GlobalContext const &);
60  void runPost(GlobalContext const &);
61  void beginPre(PathsAndConsumesOfModulesBase const &, ProcessContext const &processContext);
62  void beginPost();
63  void endPost();
64  void filePost(std::string const &);
65 
66  bool m_debug;
67  std::atomic_flag m_shouldUpdate;
68  time_t m_beginJob = 0;
71  float m_rate = 0;
72  static constexpr float m_defaultEmaInterval = 15 * 60; // Time in seconds to average EMA over for event rate.
73  static constexpr unsigned int m_defaultUpdateInterval = 3 * 60;
74  std::atomic<time_t> m_lastUpdate;
75  std::atomic<std::uint_least64_t> m_events;
76  std::atomic<std::uint_least64_t> m_lumis;
77  std::atomic<std::uint_least64_t> m_runs;
78  std::atomic<std::uint_least64_t> m_files;
81 
82  std::uint_least64_t m_lastEventCount = 0;
83  };
84 
85  } // namespace service
86 
87 } // namespace edm
88 
89 using namespace edm::service;
90 
93 
95  : m_debug(false), m_lastUpdate(0), m_events(0), m_lumis(0), m_runs(0), m_files(0) {
96  m_shouldUpdate.clear();
97  if (pset.exists("debug")) {
98  m_debug = true;
99  }
100  if (!isChirpSupported()) {
101  return;
102  }
103 
104  firstUpdate();
105 
113 
114  if (pset.exists("updateIntervalSeconds")) {
115  m_updateInterval = pset.getUntrackedParameter<unsigned int>("updateIntervalSeconds");
116  }
117  if (pset.exists("EMAInterval")) {
118  m_emaInterval = pset.getUntrackedParameter<double>("EMAInterval");
119  }
120  if (pset.exists("tag")) {
121  m_tag = pset.getUntrackedParameter<std::string>("tag");
122  }
123 }
124 
126  m_events++;
127  update();
128 }
129 
131  m_lumis++;
132  update();
133 }
134 
136  m_runs++;
137  update();
138 }
139 
141  m_files++;
142  update();
143 }
144 
147  m_processParameterSetID = processContext.parameterSetID();
148  }
149 }
150 
152  ParameterSet const &processParameterSet = edm::getParameterSet(m_processParameterSetID);
153  const edm::ParameterSet &pset = processParameterSet.getParameterSet("@main_input");
154  // PSet info from edm::ScheduleItems
155  int maxEvents =
156  processParameterSet.getUntrackedParameterSet("maxEvents", ParameterSet()).getUntrackedParameter<int>("input", -1);
157  int maxLumis = processParameterSet.getUntrackedParameterSet("maxLuminosityBlocks", ParameterSet())
158  .getUntrackedParameter<int>("input", -1);
159 
160  // lumisToProcess from EventSkipperByID (PoolSource and similar)
161  std::vector<edm::LuminosityBlockRange> toProcess = pset.getUntrackedParameter<std::vector<LuminosityBlockRange>>(
162  "lumisToProcess", std::vector<LuminosityBlockRange>());
163  edm::sortAndRemoveOverlaps(toProcess);
164  uint64_t lumiCount = 0;
165  for (auto const &range : toProcess) {
166  if (range.startRun() != range.endRun()) {
167  break;
168  }
170  break;
171  }
172  lumiCount += (range.endLumi() - range.startLumi());
173  }
174  // Handle sources deriving from ProducerSourceBase
175  unsigned int eventsPerLumi = pset.getUntrackedParameter<unsigned int>("numberEventsInLuminosityBlock", 0);
176  if ((lumiCount == 0) && (maxEvents > 0) && (eventsPerLumi > 0)) {
177  lumiCount = static_cast<unsigned int>(std::ceil(static_cast<float>(maxEvents) / static_cast<float>(eventsPerLumi)));
178  }
179 
180  std::vector<std::string> fileNames =
181  pset.getUntrackedParameter<std::vector<std::string>>("fileNames", std::vector<std::string>());
182  std::stringstream ss_max_files;
183  ss_max_files << fileNames.size();
184  updateChirp("MaxFiles", ss_max_files.str());
185 
186  if (lumiCount > 0) {
187  if (maxLumis < 0) {
188  maxLumis = lumiCount;
189  }
190  if (maxLumis > static_cast<int>(lumiCount)) {
191  maxLumis = lumiCount;
192  }
193  }
194  if (maxEvents > 0) {
195  std::stringstream ss_max_events;
196  ss_max_events << maxEvents;
197  updateChirp("MaxEvents", ss_max_events.str());
198  }
199  if (maxLumis > 0) {
200  std::stringstream ss_max_lumis;
201  ss_max_lumis << maxLumis;
202  updateChirp("MaxLumis", ss_max_lumis.str());
203  }
204 
205  m_beginJob = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
206  update();
207 }
208 
210 
212  if (m_debug) {
213  return true;
214  }
215 
216  return std::getenv("_CONDOR_CHIRP_CONFIG") && updateChirp("Elapsed", "0");
217 }
218 
220  // Note we always update all our statistics to 0 / false / -1
221  // This allows us to overwrite the activities of a previous cmsRun process
222  // within this HTCondor job.
223  updateImpl(0);
224  updateChirp("MaxFiles", "-1");
225  updateChirp("MaxEvents", "-1");
226  updateChirp("MaxLumis", "-1");
227  updateChirp("Done", "false");
229 
231  std::string models;
232  double avgSpeed;
233  if (cpusvc.isAvailable() && cpusvc->cpuInfo(models, avgSpeed)) {
234  updateChirpQuoted("CPUModels", models);
235  updateChirp("CPUSpeed", avgSpeed);
236  }
237 }
238 
240  time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
241  updateImpl(now - m_lastUpdate);
242  updateChirp("Done", "true");
244  if (!cpusvc.isAvailable()) {
245  std::cout << "At post, CPU service is NOT available.\n";
246  }
247 }
248 
250  time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
251  if ((now - m_lastUpdate.load(std::memory_order_relaxed)) > m_updateInterval) {
252  if (!m_shouldUpdate.test_and_set(std::memory_order_acquire)) {
253  // Caught exception is rethrown
254  CMS_SA_ALLOW try {
255  time_t sinceLastUpdate = now - m_lastUpdate;
256  m_lastUpdate = now;
257  updateImpl(sinceLastUpdate);
258  m_shouldUpdate.clear(std::memory_order_release);
259  } catch (...) {
260  m_shouldUpdate.clear(std::memory_order_release);
261  throw;
262  }
263  }
264  }
265 }
266 
267 void CondorStatusService::updateImpl(time_t sinceLastUpdate) {
268  time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
269  time_t jobTime = now - m_beginJob;
270 
272  if (timingsvc.isAvailable()) {
273  updateChirp("TotalCPU", timingsvc->getTotalCPU());
274  }
275 
276  updateChirp("LastUpdate", now);
277 
278  if (!m_events || (m_events > m_lastEventCount)) {
279  updateChirp("Events", m_events);
280  }
281 
282  updateChirp("Lumis", m_lumis);
283 
284  updateChirp("Runs", m_runs);
285 
286  updateChirp("Files", m_files);
287 
288  float ema_coeff = 1 - std::exp(-static_cast<float>(sinceLastUpdate) /
289  std::max(std::min(m_emaInterval, static_cast<float>(jobTime)), 1.0f));
290  if (sinceLastUpdate > 0) {
291  updateChirp("Elapsed", jobTime);
292  m_rate = ema_coeff * static_cast<float>(m_events - m_lastEventCount) / static_cast<float>(sinceLastUpdate) +
293  (1.0 - ema_coeff) * m_rate;
295  updateChirp("EventRate", m_rate);
296  }
297 
298  // If Xrootd was used, pull the statistics from there.
300  if (xrdsvc.isAvailable()) {
301  for (auto const &iter : xrdsvc->condorUpdate()) {
302  std::string site = iter.first;
303  site.erase(std::remove_if(site.begin(), site.end(), [](char x) { return !isalnum(x) && (x != '_'); }),
304  site.end());
305  auto &iostats = iter.second;
306  updateChirp("IOSite_" + site + "_ReadBytes", iostats.bytesRead);
307  updateChirp("IOSite_" + site + "_ReadTimeMS",
308  std::chrono::duration_cast<std::chrono::milliseconds>(iostats.transferTime).count());
309  }
310  }
311 
312  using namespace edm::storage;
313  // Update storage account information
314  auto const &stats = StorageAccount::summary();
315  uint64_t readOps = 0;
316  uint64_t readVOps = 0;
317  uint64_t readSegs = 0;
318  uint64_t readBytes = 0;
319  uint64_t readTimeTotal = 0;
320  uint64_t writeBytes = 0;
321  uint64_t writeTimeTotal = 0;
322  const auto token = StorageAccount::tokenForStorageClassName("tstoragefile");
323  for (const auto &storage : stats) {
324  // StorageAccount records statistics for both the TFile layer and the
325  // StorageFactory layer. However, the StorageFactory statistics tend to
326  // be more accurate as various backends may alter the incoming read requests
327  // (such as when lazy-download is used).
328  if (storage.first == token.value()) {
329  continue;
330  }
331  for (const auto &counter : storage.second) {
332  if (counter.first == static_cast<int>(StorageAccount::Operation::read)) {
333  readOps += counter.second.successes;
334  readSegs++;
335  readBytes += counter.second.amount;
336  readTimeTotal += counter.second.timeTotal;
337  } else if (counter.first == static_cast<int>(StorageAccount::Operation::readv)) {
338  readVOps += counter.second.successes;
339  readSegs += counter.second.vector_count;
340  readBytes += counter.second.amount;
341  readTimeTotal += counter.second.timeTotal;
342  } else if ((counter.first == static_cast<int>(StorageAccount::Operation::write)) ||
343  (counter.first == static_cast<int>(StorageAccount::Operation::writev))) {
344  writeBytes += counter.second.amount;
345  writeTimeTotal += counter.second.timeTotal;
346  }
347  }
348  }
349  updateChirp("ReadOps", readOps);
350  updateChirp("ReadVOps", readVOps);
351  updateChirp("ReadSegments", readSegs);
352  updateChirp("ReadBytes", readBytes);
353  updateChirp("ReadTimeMsecs", readTimeTotal / (1000 * 1000));
354  updateChirp("WriteBytes", writeBytes);
355  updateChirp("WriteTimeMsecs", writeTimeTotal / (1000 * 1000));
356 }
357 
358 template <typename T>
359 bool CondorStatusService::updateChirp(const std::string &key_suffix, const T &value) {
360  std::stringstream ss;
361  ss << value;
362  return updateChirpImpl(key_suffix, ss.str());
363 }
364 
366  std::string value_copy = value;
367  // Remove double-quotes or the \ character (as it has special escaping semantics in ClassAds).
368  // Make sure we have ASCII characters.
369  // Otherwise, remainder is allowed (including tabs, newlines, single-quotes).
370  value_copy.erase(
371  remove_if(
372  value_copy.begin(), value_copy.end(), [](const char &c) { return !isascii(c) || (c == '"') || (c == '\\'); }),
373  value_copy.end());
374  return updateChirpImpl(key_suffix, "\"" + value_copy + "\"");
375 }
376 
378  std::stringstream ss;
379  ss << "ChirpCMSSW" << m_tag << key_suffix;
380  std::string key = ss.str();
381  if (m_debug) {
382  std::cout << "condor_chirp set_job_attr_delayed " << key << " " << value << std::endl;
383  }
384  int pid = 0;
385  posix_spawn_file_actions_t file_actions;
386  int devnull_fd = open("/dev/null", O_RDWR);
387  if (devnull_fd == -1) {
388  return false;
389  }
390  posix_spawn_file_actions_init(&file_actions);
391  posix_spawn_file_actions_adddup2(&file_actions, devnull_fd, 1);
392  posix_spawn_file_actions_adddup2(&file_actions, devnull_fd, 2);
393  const std::string chirp_name = "condor_chirp";
394  const std::string set_job_attr = "set_job_attr_delayed";
395  std::vector<const char *> argv;
396  argv.push_back(chirp_name.c_str());
397  argv.push_back(set_job_attr.c_str());
398  argv.push_back(key.c_str());
399  argv.push_back(value.c_str());
400  argv.push_back(nullptr);
401  int status = posix_spawnp(&pid, "condor_chirp", &file_actions, nullptr, const_cast<char *const *>(&argv[0]), environ);
402  close(devnull_fd);
403  posix_spawn_file_actions_destroy(&file_actions);
404  if (status) {
405  return false;
406  }
407  while ((waitpid(pid, &status, 0) == -1) && errno == -EINTR) {
408  }
409  return status == 0;
410 }
411 
414  desc.setComment("Service to update HTCondor with the current CMSSW status.");
415  desc.addOptionalUntracked<unsigned int>("updateIntervalSeconds", m_defaultUpdateInterval)
416  ->setComment("Interval, in seconds, for HTCondor updates");
417  desc.addOptionalUntracked<bool>("debug", false)->setComment("Enable debugging of this service");
418  desc.addOptionalUntracked<double>("EMAInterval", m_defaultEmaInterval)
419  ->setComment("Interval, in seconds, to calculate event rate over (using EMA)");
420  desc.addOptionalUntracked<std::string>("tag")->setComment(
421  "Identifier tag for this process (a value of 'Foo' results in ClassAd attributes of the form 'ChirpCMSSWFoo*')");
422  descriptions.add("CondorStatusService", desc);
423 }
424 
bool updateChirpImpl(std::string const &key, std::string const &value)
constexpr int32_t ceil(float num)
T getUntrackedParameter(std::string const &, T const &) const
CondorStatusService(ParameterSet const &pset, edm::ActivityRegistry &ar)
const edm::EventSetup & c
#define CMS_SA_ALLOW
void preSourceConstruction(ModuleDescription const &md, int maxEvents, int maxLumis, int maxSecondsUntilRampdown)
#define DEFINE_FWK_SERVICE_MAKER(concrete, maker)
Definition: ServiceMaker.h:100
void runPost(GlobalContext const &)
void watchPostEndJob(PostEndJob::slot_type const &iSlot)
void beginPre(PathsAndConsumesOfModulesBase const &, ProcessContext const &processContext)
virtual bool cpuInfo(std::string &models, double &avgSpeed)=0
CPU information - the models present and average speed.
Guid const & processGUID()
Definition: processGUID.cc:4
void updateImpl(time_t secsSinceLastUpdate)
std::atomic< std::uint_least64_t > m_events
edm::serviceregistry::AllArgsMaker< edm::service::CondorStatusService > CondorStatusServiceMaker
ParameterSet const & getParameterSet(ParameterSetID const &id)
void watchPostEvent(PostEvent::slot_type const &iSlot)
static LuminosityBlockNumber_t maxLuminosityBlockNumber()
list status
Definition: mps_update.py:107
void lumiPost(GlobalContext const &)
bool exists(std::string const &parameterName) const
checks if a parameter exists
ParameterSet getUntrackedParameterSet(std::string const &name, ParameterSet const &defaultValue) const
Exp< T >::type exp(const T &t)
Definition: Exp.h:22
std::atomic< std::uint_least64_t > m_lumis
ParameterSetID const & parameterSetID() const
const uint16_t range(const Frame &aFrame)
void setComment(std::string const &value)
void watchPostCloseFile(PostCloseFile::slot_type const &iSlot)
bool updateChirpQuoted(const std::string &key_suffix, const std::string &value)
void filePost(std::string const &)
CondorStatusService & operator=(const CondorStatusService &)=delete
static constexpr float m_defaultEmaInterval
std::string toString(const char *format,...)
Definition: xdaq_compat.cc:4
std::atomic< std::uint_least64_t > m_runs
bool isAvailable() const
Definition: Service.h:40
tuple key
prepare the HTCondor submission files and eventually submit them
void watchPostGlobalEndLumi(PostGlobalEndLumi::slot_type const &iSlot)
virtual std::vector< std::pair< std::string, CondorIOStats > > condorUpdate()=0
void eventPost(StreamContext const &iContext)
unsigned long long uint64_t
Definition: Time.h:13
ParameterSet const & getParameterSet(std::string const &) const
void watchPostGlobalEndRun(PostGlobalEndRun::slot_type const &iSlot)
void add(std::string const &label, ParameterSetDescription const &psetDescription)
void watchPreBeginJob(PreBeginJob::slot_type const &iSlot)
convenience function for attaching to signal
static constexpr unsigned int m_defaultUpdateInterval
virtual double getTotalCPU() const =0
bool updateChirp(const std::string &key_suffix, const T &value)
bool isValid() const
Definition: Hash.h:141
static std::atomic< unsigned int > counter
std::vector< EventRange > & sortAndRemoveOverlaps(std::vector< EventRange > &eventRange)
Definition: EventRange.cc:98
float x
tuple cout
Definition: gather_cfg.py:144
ParameterDescriptionBase * addOptionalUntracked(U const &iLabel, T const &value)
tuple fileNames
Definition: LaserDQM_cfg.py:34
long double T
static void fillDescriptions(ConfigurationDescriptions &descriptions)
void watchPostBeginJob(PostBeginJob::slot_type const &iSlot)
convenience function for attaching to signal
std::atomic< std::uint_least64_t > m_files