artdaq_pcp_mmv_plugin  1.03.04
pcpmmv_metric.cc
1 // PCPMMV_metric.cc: PCPMMV Metric Plugin
2 // Author: Eric Flumerfelt
3 // Last Modified: 3/22/2019
4 //
5 // An implementation of the MetricPlugin interface for PCPMMV
6 
7 #ifndef __PCPMMV_METRIC__
8 #define __PCPMMV_METRIC__ 1
9 
10 #include "TRACE/tracemf.h" // order matters -- trace.h (no "mf") is nested from MetricMacros.hh
11 #define TRACE_NAME (app_name_ + "_pcpmmv_metric").c_str()
12 
13 #include "artdaq-utilities/Plugins/MetricMacros.hh"
14 #include "fhiclcpp/fwd.h"
15 
16 // pmapi.h must be included before mmv_stats.h
17 #include <pcp/pmapi.h>
18 
19 #include <pcp/mmv_stats.h>
20 
21 #include <utility>
22 
26 namespace artdaq {
34 class PCPMMVMetric final : public MetricPlugin
35 {
36 private:
37  std::unordered_map<std::string, int> registered_metric_types_;
38  std::vector<mmv_metric_t> registered_metrics_;
39 
40  void* mmvAddr_;
41  int domain_;
42 
43  size_t initial_metric_collection_time_;
44  std::chrono::steady_clock::time_point metric_start_time_;
45 
46  void copy_string(char* dest, size_t dstsize, const std::string& src)
47  {
48  if (src.size() <= dstsize - 1)
49  {
50  dstsize = src.size() + 1;
51  }
52  memcpy(dest, src.c_str(), dstsize);
53  }
54 
55  void init_mmv()
56  {
57  if (!registered_metrics_.empty())
58  {
59  mmv_stats_flags_t flags{};
60  METLOG(TLVL_INFO) << "Going to initialize mmv metric with name " << normalize_name_(app_name_) << ", metric count " << registered_metrics_.size();
61  METLOG(TLVL_INFO) << "First metric name: " << registered_metrics_[0].name << ", type " << registered_metrics_[0].type << ", item " << registered_metrics_[0].item;
62  mmvAddr_ = mmv_stats_init(normalize_name_(app_name_).c_str(), domain_, flags, &registered_metrics_[0], registered_metrics_.size(), nullptr, 0);
63  }
64  }
65 
66  void stop_mmv()
67  {
68  if (mmvAddr_ != nullptr)
69  {
70  mmv_stats_stop(normalize_name_(app_name_).c_str(), mmvAddr_);
71  mmvAddr_ = nullptr;
72  }
73  }
74 
75  std::string normalize_name_(std::string name)
76  {
77  auto nameTemp(std::move(name));
78 
79  auto pos = nameTemp.find('%');
80  while (pos != std::string::npos)
81  {
82  nameTemp = nameTemp.replace(pos, 1, "Percent");
83  pos = nameTemp.find('%');
84  }
85  std::replace(nameTemp.begin(), nameTemp.end(), ' ', '_');
86 
87  if (nameTemp.size() > MMV_NAMEMAX - 1)
88  {
89  nameTemp = nameTemp.substr(0, MMV_NAMEMAX - 1);
90  }
91  return nameTemp;
92  }
93 
94  bool check_time_()
95  {
96  auto dur = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - metric_start_time_).count();
97  METLOG(TLVL_INFO) << "Duration since start: " << dur << " seconds. (initial = " << initial_metric_collection_time_ << " seconds)";
98  if (dur < 0)
99  {
100  return false;
101  }
102  return static_cast<size_t>(dur) > initial_metric_collection_time_;
103  }
104 
105  pmUnits infer_units_(std::string unitString)
106  {
107  pmUnits output;
108  output.scaleCount = PM_COUNT_ONE;
109  output.scaleSpace = PM_SPACE_BYTE;
110  output.scaleTime = PM_TIME_SEC;
111  output.dimTime = 0;
112  output.dimSpace = 0;
113  output.dimCount = 0;
114 
115  std::transform(unitString.begin(), unitString.end(), unitString.begin(), ::tolower);
116  std::string before = unitString;
117  std::string after;
118 
119  if (auto pos = unitString.find('/') != std::string::npos)
120  {
121  before = unitString.substr(0, static_cast<int>(pos) - 1);
122  after = unitString.substr(pos);
123  }
124  std::istringstream iss(before);
125  std::vector<std::string> before_tokens{std::istream_iterator<std::string>{iss},
126  std::istream_iterator<std::string>{}};
127 
128  iss.str(after);
129  std::vector<std::string> after_tokens{std::istream_iterator<std::string>{iss},
130  std::istream_iterator<std::string>{}};
131 
132  for (const auto& token : before_tokens)
133  {
134  if (token.empty())
135  {
136  continue;
137  }
138  if (token == "s" || token.find("sec") == 0)
139  {
140  output.dimTime++;
141  }
142  else if (token == "b" || token.find("byte") == 0)
143  {
144  output.dimSpace++;
145  }
146  else
147  {
148  output.dimCount++;
149  }
150  }
151 
152  for (const auto& token : after_tokens)
153  {
154  if (token.empty())
155  {
156  continue;
157  }
158  if (token == "s" || token.find("sec") == 0)
159  {
160  output.dimTime--;
161  }
162  else if (token == "b" || token.find("byte") == 0)
163  {
164  output.dimSpace--;
165  }
166  else
167  {
168  output.dimCount--;
169  }
170  }
171 
172  return output;
173  }
174 
175  PCPMMVMetric(PCPMMVMetric const&) = delete;
176  PCPMMVMetric(PCPMMVMetric&&) = delete;
177  PCPMMVMetric& operator=(PCPMMVMetric const&) = delete;
178  PCPMMVMetric& operator=(PCPMMVMetric&&) = delete;
179 
180 public:
190  explicit PCPMMVMetric(fhicl::ParameterSet const& pset, std::string const& app_name, std::string const& metric_name)
191  : MetricPlugin(pset, app_name, metric_name)
192  , registered_metric_types_()
193  , registered_metrics_()
194  , mmvAddr_(nullptr)
195  , domain_(pset.get<int>("pcp_domain_number", 0))
196  , initial_metric_collection_time_(pset.get<size_t>("seconds_before_init", 30))
197  {}
198 
199  ~PCPMMVMetric() override { MetricPlugin::stopMetrics(); }
200 
205  std::string getLibName() const override { return "pcpmmv"; }
206 
210  void stopMetrics_() override {}
211 
215  void startMetrics_() override { metric_start_time_ = std::chrono::steady_clock::now(); }
216 
223  void sendMetric_(const std::string& name, const std::string& value, const std::string& unit, const std::chrono::system_clock::time_point&) override
224  {
225  auto nname = normalize_name_(name);
226  if (registered_metric_types_.count(nname) == 0u)
227  {
228  METLOG(TLVL_INFO) << "Adding string metric named " << nname;
229  mmv_metric_t newMetric;
230  copy_string(newMetric.name, MMV_NAMEMAX, nname);
231  newMetric.item = registered_metrics_.size();
232  newMetric.type = MMV_TYPE_STRING;
233  newMetric.semantics = MMV_SEM_INSTANT;
234  newMetric.dimension = infer_units_(unit);
235  newMetric.indom = 0;
236  newMetric.helptext = nullptr;
237  newMetric.shorttext = nullptr;
238 
239  registered_metrics_.push_back(newMetric);
240  registered_metric_types_[nname] = MMV_TYPE_STRING;
241  stop_mmv();
242  }
243 
244  if (registered_metric_types_[nname] != MMV_TYPE_STRING)
245  {
246  METLOG(TLVL_ERROR) << "PCP-MMV Metric: Metric instance has wrong type! Expected " << registered_metric_types_[nname]
247  << ", got std::string";
248  return;
249  }
250 
251  if ((mmvAddr_ == nullptr) && check_time_())
252  {
253  init_mmv();
254  }
255 
256  if (mmvAddr_ != nullptr)
257  {
258  auto base = mmv_lookup_value_desc(mmvAddr_, nname.c_str(), nullptr);
259  auto val = value;
260  if (val.size() > MMV_STRINGMAX - 1)
261  {
262  val = val.substr(0, MMV_STRINGMAX - 1);
263  }
264 
265  mmv_set_string(mmvAddr_, base, value.c_str(), value.size());
266  }
267  }
268 
275  void sendMetric_(const std::string& name, const int& value, const std::string& unit, const std::chrono::system_clock::time_point&) override
276  {
277  auto nname = normalize_name_(name);
278  if (registered_metric_types_.count(nname) == 0u)
279  {
280  METLOG(TLVL_INFO) << "Adding int metric named " << nname;
281  mmv_metric_t newMetric;
282  copy_string(newMetric.name, MMV_NAMEMAX, nname);
283  newMetric.item = registered_metrics_.size();
284  newMetric.type = MMV_TYPE_I64;
285  newMetric.semantics = MMV_SEM_INSTANT;
286  newMetric.dimension = infer_units_(unit);
287  newMetric.indom = 0;
288  newMetric.helptext = nullptr;
289  newMetric.shorttext = nullptr;
290 
291  registered_metrics_.push_back(newMetric);
292  registered_metric_types_[nname] = MMV_TYPE_I64;
293  stop_mmv();
294  }
295 
296  if (registered_metric_types_[nname] != MMV_TYPE_I64)
297  {
298  METLOG(TLVL_ERROR) << "PCP-MMV Metric: Metric instance has wrong type! Expected " << registered_metric_types_[nname]
299  << ", got int";
300  return;
301  }
302 
303  if ((mmvAddr_ == nullptr) && check_time_())
304  {
305  init_mmv();
306  }
307 
308  if (mmvAddr_ != nullptr)
309  {
310  auto base = mmv_lookup_value_desc(mmvAddr_, nname.c_str(), nullptr);
311  mmv_set_value(mmvAddr_, base, value);
312  }
313  }
314 
321  void sendMetric_(const std::string& name, const double& value, const std::string& unit, const std::chrono::system_clock::time_point&) override
322  {
323  auto nname = normalize_name_(name);
324  if (registered_metric_types_.count(nname) == 0u)
325  {
326  METLOG(TLVL_INFO) << "Adding double metric named " << nname;
327  mmv_metric_t newMetric;
328  copy_string(newMetric.name, MMV_NAMEMAX, nname);
329  newMetric.item = registered_metrics_.size();
330  newMetric.type = MMV_TYPE_DOUBLE;
331  newMetric.semantics = MMV_SEM_INSTANT;
332  newMetric.dimension = infer_units_(unit);
333  newMetric.indom = 0;
334  newMetric.helptext = nullptr;
335  newMetric.shorttext = nullptr;
336 
337  registered_metrics_.push_back(newMetric);
338  registered_metric_types_[nname] = MMV_TYPE_DOUBLE;
339  stop_mmv();
340  }
341 
342  if (registered_metric_types_[nname] != MMV_TYPE_DOUBLE)
343  {
344  METLOG(TLVL_ERROR) << "PCP-MMV Metric: Metric instance has wrong type! Expected " << registered_metric_types_[nname]
345  << ", got double";
346  return;
347  }
348 
349  if ((mmvAddr_ == nullptr) && check_time_())
350  {
351  init_mmv();
352  }
353 
354  if (mmvAddr_ != nullptr)
355  {
356  auto base = mmv_lookup_value_desc(mmvAddr_, nname.c_str(), nullptr);
357  mmv_set_value(mmvAddr_, base, value);
358  }
359  }
360 
367  void sendMetric_(const std::string& name, const float& value, const std::string& unit, const std::chrono::system_clock::time_point&) override
368  {
369  auto nname = normalize_name_(name);
370  if (registered_metric_types_.count(nname) == 0u)
371  {
372  METLOG(TLVL_INFO) << "Adding float metric named " << nname;
373  mmv_metric_t newMetric;
374  copy_string(newMetric.name, MMV_NAMEMAX, nname);
375  newMetric.item = registered_metrics_.size();
376  newMetric.type = MMV_TYPE_FLOAT;
377  newMetric.semantics = MMV_SEM_INSTANT;
378  newMetric.dimension = infer_units_(unit);
379  newMetric.indom = 0;
380  newMetric.helptext = nullptr;
381  newMetric.shorttext = nullptr;
382 
383  registered_metrics_.push_back(newMetric);
384  registered_metric_types_[nname] = MMV_TYPE_FLOAT;
385  stop_mmv();
386  }
387 
388  if (registered_metric_types_[nname] != MMV_TYPE_FLOAT)
389  {
390  METLOG(TLVL_ERROR) << "PCP-MMV Metric: Metric instance has wrong type! Expected " << registered_metric_types_[nname]
391  << ", got float";
392  return;
393  }
394 
395  if ((mmvAddr_ == nullptr) && check_time_())
396  {
397  init_mmv();
398  }
399 
400  if (mmvAddr_ != nullptr)
401  {
402  auto base = mmv_lookup_value_desc(mmvAddr_, nname.c_str(), nullptr);
403  mmv_set_value(mmvAddr_, base, value);
404  }
405  }
406 
413  void sendMetric_(const std::string& name, const uint64_t& value, const std::string& unit, const std::chrono::system_clock::time_point&) override
414  {
415  auto nname = normalize_name_(name);
416  if (registered_metric_types_.count(nname) == 0u)
417  {
418  METLOG(TLVL_INFO) << "Adding unsigned metric named " << nname;
419  mmv_metric_t newMetric;
420  copy_string(newMetric.name, MMV_NAMEMAX, nname);
421  newMetric.item = registered_metrics_.size();
422  newMetric.type = MMV_TYPE_U64;
423  newMetric.semantics = MMV_SEM_INSTANT;
424  newMetric.dimension = infer_units_(unit);
425  newMetric.indom = 0;
426  newMetric.helptext = nullptr;
427  newMetric.shorttext = nullptr;
428 
429  registered_metrics_.push_back(newMetric);
430  registered_metric_types_[nname] = MMV_TYPE_U64;
431  stop_mmv();
432  }
433 
434  if (registered_metric_types_[nname] != MMV_TYPE_U64)
435  {
436  METLOG(TLVL_ERROR) << "PCP-MMV Metric: Metric instance has wrong type! Expected " << registered_metric_types_[nname]
437  << ", got unsigned int";
438  return;
439  }
440 
441  if ((mmvAddr_ == nullptr) && check_time_())
442  {
443  init_mmv();
444  }
445 
446  if (mmvAddr_ != nullptr)
447  {
448  auto base = mmv_lookup_value_desc(mmvAddr_, nname.c_str(), nullptr);
449  mmv_set_value(mmvAddr_, base, value);
450  }
451  }
452 };
453 } // End namespace artdaq
454 
455 DEFINE_ARTDAQ_METRIC(artdaq::PCPMMVMetric)
456 
457 #endif // End ifndef __PCPMMV_METRIC__
void sendMetric_(const std::string &name, const float &value, const std::string &unit, const std::chrono::system_clock::time_point &) override
Send a float metric to PCPMMV.
PCPMMVMetric(fhicl::ParameterSet const &pset, std::string const &app_name, std::string const &metric_name)
Construct an instance of the PCPMMV metric.
void sendMetric_(const std::string &name, const int &value, const std::string &unit, const std::chrono::system_clock::time_point &) override
Send a integer metric to PCPMMV.
std::string getLibName() const override
Gets the unique library name of this plugin.
void sendMetric_(const std::string &name, const std::string &value, const std::string &unit, const std::chrono::system_clock::time_point &) override
Send a string metric to PCPMMV.
void stopMetrics_() override
PCPMMV does not need any specific action on stop.
void startMetrics_() override
PCPMMV does not need any specific action on start.
void sendMetric_(const std::string &name, const uint64_t &value, const std::string &unit, const std::chrono::system_clock::time_point &) override
Send an unsigned long metric to PCPMMV.
An instance of the MetricPlugin class that sends metric data to PCPMMV.
void sendMetric_(const std::string &name, const double &value, const std::string &unit, const std::chrono::system_clock::time_point &) override
Send a double metric to PCPMMV.