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