stats.cpp raw

   1  // Copyright (c) 2016 The Limenka developers
   2  // Distributed under the MIT software license, see the accompanying
   3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   4  
   5  #include <stats/stats.h>
   6  
   7  #include <memusage.h>
   8  #include <util/time.h>
   9  
  10  #include <cmath>
  11  
  12  static const uint32_t SAMPLE_MIN_DELTA_IN_SEC = 2;
  13  static const int CLEANUP_SAMPLES_THRESHOLD = 100;
  14  size_t CStats::maxStatsMemory = 0;
  15  const size_t CStats::DEFAULT_MAX_STATS_MEMORY = 10 * 1024 * 1024; //10 MB
  16  
  17  // NOTE: stats/init.cpp help for -statsenable needs to be manually updated
  18  const bool CStats::DEFAULT_STATISTICS_ENABLED = false;
  19  
  20  std::atomic<bool> CStats::m_stats_enabled(false); //disable stats by default
  21  
  22  CStats* CStats::m_shared_instance{nullptr};
  23  
  24  CStats* CStats::DefaultStats()
  25  {
  26      if (!m_shared_instance)
  27          m_shared_instance = new CStats();
  28  
  29      return m_shared_instance;
  30  }
  31  
  32  void CStats::addMempoolSample(int64_t txcount, int64_t dynUsage, int64_t currentMinRelayFee)
  33  {
  34      if (!m_stats_enabled)
  35          return;
  36  
  37      uint64_t now = GetTime();
  38      {
  39          LOCK(cs_stats);
  40  
  41          // set the mempool stats start time if this is the first sample
  42          if (m_mempool_stats.m_start_time == 0)
  43              m_mempool_stats.m_start_time = now;
  44  
  45          // ensure the minimum time delta between samples
  46          if (m_mempool_stats.m_samples.size() && m_mempool_stats.m_start_time + m_mempool_stats.m_samples.back().m_time_delta + SAMPLE_MIN_DELTA_IN_SEC >= now) {
  47              return;
  48          }
  49  
  50          // calculate the current time delta and add a sample
  51          uint32_t timeDelta = now - m_mempool_stats.m_start_time; //truncate to uint32_t should be sufficient
  52          m_mempool_stats.m_samples.push_back({timeDelta, txcount, dynUsage, currentMinRelayFee});
  53          m_mempool_stats.m_cleanup_counter++;
  54  
  55          // check if we should cleanup the container
  56          if (m_mempool_stats.m_cleanup_counter >= CLEANUP_SAMPLES_THRESHOLD) {
  57              //check memory usage
  58              if (memusage::DynamicUsage(m_mempool_stats.m_samples) > maxStatsMemory && m_mempool_stats.m_samples.size() > 1) {
  59                  // only shrink if the vector.capacity() is > the target for performance reasons
  60                  m_mempool_stats.m_samples.shrink_to_fit();
  61                  const size_t memUsage = memusage::DynamicUsage(m_mempool_stats.m_samples);
  62                  // calculate the amount of samples we need to remove
  63                  size_t itemsToRemove = (memUsage - maxStatsMemory + sizeof(m_mempool_stats.m_samples[0]) - 1) / sizeof(m_mempool_stats.m_samples[0]);
  64  
  65                  // sanity check; always keep the most recent sample we just added
  66                  if (m_mempool_stats.m_samples.size() <= itemsToRemove) {
  67                      itemsToRemove = m_mempool_stats.m_samples.size() - 1;
  68                  }
  69                  m_mempool_stats.m_samples.erase(m_mempool_stats.m_samples.begin(), m_mempool_stats.m_samples.begin() + itemsToRemove);
  70              }
  71              // shrink vector
  72              m_mempool_stats.m_samples.shrink_to_fit();
  73              m_mempool_stats.m_cleanup_counter = 0;
  74          }
  75  
  76          // fire signal
  77          MempoolStatsDidChange();
  78      }
  79  }
  80  
  81  mempoolSamples_t CStats::mempoolGetValuesInRange(uint64_t& fromTime, uint64_t& toTime)
  82  {
  83      if (!m_stats_enabled)
  84          return mempoolSamples_t();
  85  
  86      LOCK(cs_stats);
  87  
  88      // if empty, return directly
  89      if (!m_mempool_stats.m_samples.size())
  90          return m_mempool_stats.m_samples;
  91  
  92  
  93      if (!(fromTime == 0 && toTime == 0) && (fromTime > m_mempool_stats.m_start_time + m_mempool_stats.m_samples.front().m_time_delta || toTime < m_mempool_stats.m_start_time + m_mempool_stats.m_samples.back().m_time_delta)) {
  94          mempoolSamples_t::iterator fromSample = m_mempool_stats.m_samples.begin();
  95          mempoolSamples_t::iterator toSample = std::prev(m_mempool_stats.m_samples.end());
  96  
  97          // create subset of samples
  98          bool fromSet = false;
  99          for (mempoolSamples_t::iterator it = m_mempool_stats.m_samples.begin(); it != m_mempool_stats.m_samples.end(); ++it) {
 100              if (m_mempool_stats.m_start_time + (*it).m_time_delta >= fromTime && !fromSet) {
 101                  fromSample = it;
 102                  fromSet = true;
 103              }
 104              else if (m_mempool_stats.m_start_time + (*it).m_time_delta > toTime) {
 105                  toSample = std::prev(it);
 106                  break;
 107              }
 108          }
 109  
 110          mempoolSamples_t subset(fromSample, toSample + 1);
 111  
 112          // set the fromTime and toTime pass-by-ref parameters
 113          fromTime = m_mempool_stats.m_start_time + (*fromSample).m_time_delta;
 114          toTime = m_mempool_stats.m_start_time + (*toSample).m_time_delta;
 115  
 116          // return subset
 117          return subset;
 118      }
 119  
 120      // return all available samples
 121      fromTime = m_mempool_stats.m_start_time + m_mempool_stats.m_samples.front().m_time_delta;
 122      toTime = m_mempool_stats.m_start_time + m_mempool_stats.m_samples.back().m_time_delta;
 123      return m_mempool_stats.m_samples;
 124  }
 125  
 126  void CStats::setMaxMemoryUsageTarget(size_t maxMem)
 127  {
 128      m_stats_enabled = (maxMem > 0);
 129  
 130      LOCK(cs_stats);
 131      maxStatsMemory = maxMem;
 132  }
 133