CMS 3D CMS Logo

DQMFileSaverPB.cc
Go to the documentation of this file.
1 #include <filesystem>
2 #include <fstream>
3 #include <iostream>
4 #include <string>
5 #include <utility>
6 #include <vector>
7 #include <sys/stat.h>
8 #include <sys/types.h>
9 #include <unistd.h>
10 #include <boost/property_tree/json_parser.hpp>
11 #include <openssl/md5.h>
12 #include <fmt/printf.h>
13 
14 #include <google/protobuf/io/coded_stream.h>
15 #include <google/protobuf/io/gzip_stream.h>
16 #include <google/protobuf/io/zero_copy_stream_impl.h>
17 
18 #include <TString.h>
19 #include <TSystem.h>
20 #include <TBufferFile.h>
21 
22 #include "zlib.h"
28 
29 #include "DQMFileSaverPB.h"
30 
31 using namespace dqm;
32 
34  fakeFilterUnitMode_ = ps.getUntrackedParameter<bool>("fakeFilterUnitMode", false);
35  streamLabel_ = ps.getUntrackedParameter<std::string>("streamLabel", "streamDQMHistograms");
36  tag_ = ps.getUntrackedParameter<std::string>("tag", "UNKNOWN");
37 
39  mergeType_ = "";
40 
41  // If tag is set we're running in a DQM Live mode.
42  // Snapshot files will be saved for every client, then they will be merged and uploaded to the new DQM GUI.
43  if (tag_ != "UNKNOWN") {
44  streamLabel_ = "DQMLive";
45  }
46 
47  if (!fakeFilterUnitMode_) {
49  throw cms::Exception("DQMFileSaverPB") << "EvFDaqDirector is not available";
50  std::string initFileName = edm::Service<evf::EvFDaqDirector>()->getInitFilePath(streamLabel_);
51  std::ofstream file(initFileName);
52  if (!file)
53  throw cms::Exception("DQMFileSaverPB")
54  << "Cannot create INI file: " << initFileName << " error: " << strerror(errno);
55  file.close();
56  }
57 }
58 
60 
62  if (!fakeFilterUnitMode_) {
65  }
66 }
67 
69  // get from DAQ2 services where to store the files according to their format
70  namespace bpt = boost::property_tree;
71 
72  std::string openJsonFilePathName;
73  std::string jsonFilePathName;
74  std::string openHistoFilePathName;
75  std::string histoFilePathName;
76 
77  evf::FastMonitoringService* fms = nullptr;
79 
80  // create the files names
81  if (fakeFilterUnitMode_) {
82  std::string runDir = fmt::sprintf("%s/run%06d", fp.path_, fp.run_);
83  std::string baseName = "";
84  std::filesystem::create_directories(runDir);
85  // If tag is configured, append it to the name of the resulting file.
86  // This differentiates files saved by different clients.
87  // If tag is not configured, we don't add it at all to keep the old behaviour unchanged.
88  if (tag_ == "UNKNOWN") {
89  baseName = fmt::sprintf("%s/run%06d_ls%04d_%s", runDir, fp.run_, fp.lumi_, streamLabel_);
90  } else {
91  baseName = fmt::sprintf("%s/run%06d_%s_%s", runDir, fp.run_, tag_, streamLabel_);
92  }
93 
94  jsonFilePathName = baseName + ".jsn";
95  openJsonFilePathName = jsonFilePathName + ".open";
96 
97  histoFilePathName = baseName + ".pb";
98  openHistoFilePathName = histoFilePathName + ".open";
99  } else {
100  openJsonFilePathName = edm::Service<evf::EvFDaqDirector>()->getOpenOutputJsonFilePath(fp.lumi_, streamLabel_);
101  jsonFilePathName = edm::Service<evf::EvFDaqDirector>()->getOutputJsonFilePath(fp.lumi_, streamLabel_);
102 
103  openHistoFilePathName =
104  edm::Service<evf::EvFDaqDirector>()->getOpenProtocolBufferHistogramFilePath(fp.lumi_, streamLabel_);
105  histoFilePathName = edm::Service<evf::EvFDaqDirector>()->getProtocolBufferHistogramFilePath(fp.lumi_, streamLabel_);
106 
108  }
109 
110  bool abortFlag = false;
111  if (fms ? fms->getEventsProcessedForLumi(fp.lumi_, &abortFlag) : true) {
112  // Save the file in the open directory.
113  this->savePB(&*store, openHistoFilePathName, fp.run_, fp.lumi_);
114 
115  // Now move the the data and json files into the output directory.
116  ::rename(openHistoFilePathName.c_str(), histoFilePathName.c_str());
117  }
118 
119  if (abortFlag)
120  return;
121 
122  // Write the json file in the open directory.
123  bpt::ptree pt = fillJson(fp.run_, fp.lumi_, histoFilePathName, transferDestination_, mergeType_, fms);
124  write_json(openJsonFilePathName, pt);
125  ::rename(openJsonFilePathName.c_str(), jsonFilePathName.c_str());
126 }
127 
129  // no saving for the run
130 }
131 
132 boost::property_tree::ptree DQMFileSaverPB::fillJson(int run,
133  int lumi,
134  const std::string& dataFilePathName,
135  const std::string& transferDestinationStr,
136  const std::string& mergeTypeStr,
138  namespace bpt = boost::property_tree;
139  namespace bfs = std::filesystem;
140 
141  bpt::ptree pt;
142 
143  int hostnameReturn;
144  char host[32];
145  hostnameReturn = gethostname(host, sizeof(host));
146  if (hostnameReturn == -1)
147  throw cms::Exception("fillJson") << "Internal error, cannot get host name";
148 
149  int pid = getpid();
150  std::ostringstream oss_pid;
151  oss_pid << pid;
152 
153  int nProcessed = fms ? (fms->getEventsProcessedForLumi(lumi)) : -1;
154 
155  // Stat the data file: if not there, throw
156  std::string dataFileName;
157  struct stat dataFileStat;
158  dataFileStat.st_size = 0;
159  if (nProcessed) {
160  if (stat(dataFilePathName.c_str(), &dataFileStat) != 0)
161  throw cms::Exception("fillJson") << "Internal error, cannot get data file: " << dataFilePathName;
162  // Extract only the data file name from the full path
163  dataFileName = bfs::path(dataFilePathName).filename().string();
164  }
165  // The availability test of the FastMonitoringService was done in the ctor.
166  bpt::ptree data;
167  bpt::ptree processedEvents, acceptedEvents, errorEvents, bitmask, fileList, fileSize, inputFiles, fileAdler32,
168  transferDestination, mergeType, hltErrorEvents;
169 
170  processedEvents.put("", nProcessed); // Processed events
171  acceptedEvents.put("", nProcessed); // Accepted events, same as processed for our purposes
172 
173  errorEvents.put("", 0); // Error events
174  bitmask.put("", 0); // Bitmask of abs of CMSSW return code
175  fileList.put("", dataFileName); // Data file the information refers to
176  fileSize.put("", dataFileStat.st_size); // Size in bytes of the data file
177  inputFiles.put("", ""); // We do not care about input files!
178  fileAdler32.put("", -1); // placeholder to match output json definition
179  transferDestination.put("", transferDestinationStr); // SM Transfer destination field
180  mergeType.put("", mergeTypeStr); // SM Transfer destination field
181  hltErrorEvents.put("", 0); // Error events
182 
183  data.push_back(std::make_pair("", processedEvents));
184  data.push_back(std::make_pair("", acceptedEvents));
185  data.push_back(std::make_pair("", errorEvents));
186  data.push_back(std::make_pair("", bitmask));
187  data.push_back(std::make_pair("", fileList));
188  data.push_back(std::make_pair("", fileSize));
189  data.push_back(std::make_pair("", inputFiles));
190  data.push_back(std::make_pair("", fileAdler32));
191  data.push_back(std::make_pair("", transferDestination));
192  data.push_back(std::make_pair("", mergeType));
193  data.push_back(std::make_pair("", hltErrorEvents));
194 
195  pt.add_child("data", data);
196 
197  if (fms == nullptr) {
198  pt.put("definition", "/fakeDefinition.jsn");
199  } else {
200  // The availability test of the EvFDaqDirector Service was done in the ctor.
201  bfs::path outJsonDefName{
202  edm::Service<evf::EvFDaqDirector>()->baseRunDir()}; //we assume this file is written bu the EvF Output module
203  outJsonDefName /= (std::string("output_") + oss_pid.str() + std::string(".jsd"));
204  pt.put("definition", outJsonDefName.string());
205  }
206 
207  char sourceInfo[64]; //host and pid information
208  sprintf(sourceInfo, "%s_%d", host, pid);
209  pt.put("source", sourceInfo);
210 
211  return pt;
212 }
213 
216  desc.setComment("Saves histograms from DQM store, HLT->pb workflow.");
217 
218  desc.addUntracked<bool>("fakeFilterUnitMode", false)->setComment("If set, EvFDaqDirector is emulated and not used.");
219 
220  desc.addUntracked<std::string>("streamLabel", "streamDQMHistograms")->setComment("Label of the stream.");
221 
223 
224  // Changed to use addDefault instead of add here because previously
225  // DQMFileSaverOnline and DQMFileSaverPB both used the module label
226  // "saver" which caused conflicting cfi filenames to be generated.
227  // add could be used if unique module labels were given.
228  descriptions.addDefault(desc);
229 }
230 
231 void DQMFileSaverPB::savePB(DQMStore* store, std::string const& filename, int run, int lumi) const {
232  using google::protobuf::io::FileOutputStream;
233  using google::protobuf::io::GzipOutputStream;
234  using google::protobuf::io::StringOutputStream;
235 
236  unsigned int nme = 0;
237 
238  dqmstorepb::ROOTFilePB dqmstore_message;
239 
240  // We save all histograms, indifferent of the lumi flag: even tough we save per lumi, this is a *snapshot*.
241  auto mes = store->getAllContents("");
242  for (auto const me : mes) {
243  TBufferFile buffer(TBufferFile::kWrite);
244  if (me->kind() < MonitorElement::Kind::TH1F) {
245  TObjString object(me->tagString().c_str());
246  buffer.WriteObject(&object);
247  } else {
248  buffer.WriteObject(me->getRootObject());
249  }
250  dqmstorepb::ROOTFilePB::Histo& histo = *dqmstore_message.add_histo();
251  histo.set_full_pathname(me->getFullname());
252  uint32_t flags = 0;
253  flags |= (uint32_t)me->kind();
254  if (me->getLumiFlag())
256  if (me->getEfficiencyFlag())
258  histo.set_flags(flags);
259  histo.set_size(buffer.Length());
260 
261  if (tag_ == "UNKNOWN") {
262  histo.set_streamed_histo((void const*)buffer.Buffer(), buffer.Length());
263  } else {
264  // Compress ME blob with zlib
265  int maxOutputSize = this->getMaxCompressedSize(buffer.Length());
266  std::vector<char> compression_output(maxOutputSize);
267  uLong total_out = this->compressME(buffer, maxOutputSize, compression_output.data());
268  histo.set_streamed_histo(compression_output.data(), total_out);
269  }
270 
271  // Save quality reports
272  for (const auto& qr : me->getQReports()) {
274  // TODO: 64 is likely too short; memory corruption in the old code?
275  char buf[64];
276  std::snprintf(buf, sizeof(buf), "qr=st:%d:%.*g:", qr->getStatus(), DBL_DIG + 2, qr->getQTresult());
277  result = '<' + me->getName() + '.' + qr->getQRName() + '>';
278  result += buf;
279  result += qr->getAlgorithm() + ':' + qr->getMessage();
280  result += "</" + me->getName() + '.' + qr->getQRName() + '>';
281  TObjString str(result.c_str());
282 
283  dqmstorepb::ROOTFilePB::Histo& qr_histo = *dqmstore_message.add_histo();
284  TBufferFile qr_buffer(TBufferFile::kWrite);
285  qr_buffer.WriteObject(&str);
286  qr_histo.set_full_pathname(me->getFullname() + '.' + qr->getQRName());
287  qr_histo.set_flags(static_cast<uint32_t>(MonitorElement::Kind::STRING));
288  qr_histo.set_size(qr_buffer.Length());
289  // qr_histo.set_streamed_histo((void const*)qr_buffer.Buffer(), qr_buffer.Length());
290 
291  if (tag_ == "UNKNOWN") {
292  qr_histo.set_streamed_histo((void const*)qr_buffer.Buffer(), qr_buffer.Length());
293  } else {
294  // Compress ME blob with zlib
295  int maxOutputSize = this->getMaxCompressedSize(qr_buffer.Length());
296  char compression_output[maxOutputSize];
297  uLong total_out = this->compressME(qr_buffer, maxOutputSize, compression_output);
298  qr_histo.set_streamed_histo(compression_output, total_out);
299  }
300  }
301 
302  // Save efficiency tag, if any.
303  // XXX not supported by protobuf files.
304 
305  // Save tag if any.
306  // XXX not supported by protobuf files.
307 
308  // Count saved histograms
309  ++nme;
310  }
311 
312  int filedescriptor =
313  ::open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
314  FileOutputStream file_stream(filedescriptor);
315  if (tag_ == "UNKNOWN") {
317  options.format = GzipOutputStream::GZIP;
318  options.compression_level = 1;
319  GzipOutputStream gzip_stream(&file_stream, options);
320  dqmstore_message.SerializeToZeroCopyStream(&gzip_stream);
321 
322  // Flush the internal streams & Close the file descriptor
323  gzip_stream.Close();
324  file_stream.Close();
325  } else {
326  // We zlib compressed individual MEs so no need to compress the entire file again.
327  dqmstore_message.SerializeToZeroCopyStream(&file_stream);
328 
329  // Flush the internal stream & Close the file descriptor
330  file_stream.Close();
331  }
332 
333  // Maybe make some noise.
334  edm::LogInfo("DQMFileSaverPB") << "savePB: successfully wrote " << nme << " objects "
335  << "into DQM file '" << filename << "'\n";
336 }
337 
338 int DQMFileSaverPB::getMaxCompressedSize(int bufferSize) const {
339  // When input data is very badly compressable, zlib will add overhead instead of reducing the size.
340  // There is a minor amount of overhead (6 bytes overall and 5 bytes per 16K block) that is taken
341  // into consideration here to find out potential absolute maximum size of the output.
342  int n16kBlocks = (bufferSize + 16383) / 16384; // round up any fraction of a block
343  int maxOutputSize = bufferSize + 6 + (n16kBlocks * 5);
344  return maxOutputSize;
345 }
346 
347 ulong DQMFileSaverPB::compressME(const TBufferFile& buffer, int maxOutputSize, char* compression_output) const {
348  z_stream deflateStream;
349  deflateStream.zalloc = Z_NULL;
350  deflateStream.zfree = Z_NULL;
351  deflateStream.opaque = Z_NULL;
352  deflateStream.avail_in = (uInt)buffer.Length() + 1; // size of input, string + terminator
353  deflateStream.next_in = (Bytef*)buffer.Buffer(); // input array
354  deflateStream.avail_out = (uInt)maxOutputSize; // size of output
355  deflateStream.next_out = (Bytef*)compression_output; // output array, result will be placed here
356 
357  // The actual compression
358  deflateInit(&deflateStream, Z_BEST_COMPRESSION);
359  deflate(&deflateStream, Z_FINISH);
360  deflateEnd(&deflateStream);
361 
362  return deflateStream.total_out;
363 }
364 
std::string streamLabel_
std::string transferDestination_
string host
Definition: query.py:115
DQMFileSaverPB(const edm::ParameterSet &ps)
static const uint32_t DQM_PROP_EFFICIENCY_PLOT
Definition: DQMNet.h:66
static const std::string filename(const FileParameters &fp, bool useLumi=false)
T getUntrackedParameter(std::string const &, T const &) const
~DQMFileSaverPB() override
void initRun() const override
void addDefault(ParameterSetDescription const &psetDescription)
void savePB(DQMStore *store, std::string const &filename, int run, int lumi) const
virtual std::vector< dqm::harvesting::MonitorElement * > getAllContents(std::string const &path) const
Definition: DQMStore.cc:641
static void fillDescriptions(edm::ConfigurationDescriptions &descriptions)
std::vector< std::shared_ptr< fireworks::OptionNode > > Options
#define DEFINE_FWK_MODULE(type)
Definition: MakerMacros.h:16
ulong compressME(const TBufferFile &buffer, int maxOutputSize, char *compression_output) const
int getMaxCompressedSize(int bufferSize) const
Log< level::Info, false > LogInfo
void saveLumi(const FileParameters &fp) const override
char data[epos_bytes_allocation]
Definition: EPOS_Wrapper.h:80
void saveRun(const FileParameters &fp) const override
static void fillDescription(edm::ParameterSetDescription &d)
bool isAvailable() const
Definition: Service.h:40
unsigned int getEventsProcessedForLumi(unsigned int lumi, bool *abortFlag=nullptr)
#define str(s)
Definition: DQMStore.h:18
static boost::property_tree::ptree fillJson(int run, int lumi, const std::string &dataFilePathName, const std::string &transferDestinationStr, const std::string &mergeTypeStr, evf::FastMonitoringService *fms)
static const uint32_t DQM_PROP_LUMI
Definition: DQMNet.h:63
std::string mergeType_