1 |
|
|
/** |
2 |
|
|
* This file is part of the CernVM File System. |
3 |
|
|
*/ |
4 |
|
|
|
5 |
|
|
#include "statistics.h" |
6 |
|
|
|
7 |
|
|
#include <algorithm> |
8 |
|
|
#include <cassert> |
9 |
|
|
|
10 |
|
|
#include "platform.h" |
11 |
|
|
#include "smalloc.h" |
12 |
|
|
#include "util/string.h" |
13 |
|
|
#include "util_concurrency.h" |
14 |
|
|
|
15 |
|
|
using namespace std; // NOLINT |
16 |
|
|
|
17 |
|
|
#ifdef CVMFS_NAMESPACE_GUARD |
18 |
|
|
namespace CVMFS_NAMESPACE_GUARD { |
19 |
|
|
#endif |
20 |
|
|
|
21 |
|
|
namespace perf { |
22 |
|
|
|
23 |
|
1 |
std::string Counter::ToString() { return StringifyInt(Get()); } |
24 |
|
1 |
std::string Counter::Print() { return StringifyInt(Get()); } |
25 |
|
1 |
std::string Counter::PrintK() { return StringifyInt(Get() / 1000); } |
26 |
|
1 |
std::string Counter::PrintKi() { return StringifyInt(Get() / 1024); } |
27 |
|
1 |
std::string Counter::PrintM() { return StringifyInt(Get() / (1000 * 1000)); } |
28 |
|
1 |
std::string Counter::PrintMi() { return StringifyInt(Get() / (1024 * 1024)); } |
29 |
|
2 |
std::string Counter::PrintRatio(Counter divider) { |
30 |
|
2 |
double enumerator_value = Get(); |
31 |
|
2 |
double divider_value = divider.Get(); |
32 |
|
2 |
return StringifyDouble(enumerator_value / divider_value); |
33 |
|
|
} |
34 |
|
|
|
35 |
|
|
|
36 |
|
|
//----------------------------------------------------------------------------- |
37 |
|
|
|
38 |
|
|
|
39 |
|
|
/** |
40 |
|
|
* Creates a new Statistics binder which maintains the same Counters as the |
41 |
|
|
* existing one. Changes to those counters are visible in both Statistics |
42 |
|
|
* objects. The child can then independently add more counters. CounterInfo |
43 |
|
|
* objects are reference counted and deleted when all the statistics objects |
44 |
|
|
* dealing with it are destroyed. |
45 |
|
|
*/ |
46 |
|
62 |
Statistics *Statistics::Fork() { |
47 |
|
62 |
Statistics *child = new Statistics(); |
48 |
|
|
|
49 |
|
62 |
MutexLockGuard lock_guard(lock_); |
50 |
✓✓ |
1711 |
for (map<string, CounterInfo *>::iterator i = counters_.begin(), |
51 |
|
62 |
iEnd = counters_.end(); i != iEnd; ++i) |
52 |
|
|
{ |
53 |
|
1587 |
atomic_inc32(&i->second->refcnt); |
54 |
|
|
} |
55 |
|
62 |
child->counters_ = counters_; |
56 |
|
|
|
57 |
|
62 |
return child; |
58 |
|
|
} |
59 |
|
|
|
60 |
|
|
|
61 |
|
31 |
Counter *Statistics::Lookup(const std::string &name) const { |
62 |
|
31 |
MutexLockGuard lock_guard(lock_); |
63 |
|
31 |
map<string, CounterInfo *>::const_iterator i = counters_.find(name); |
64 |
✓✓ |
31 |
if (i != counters_.end()) |
65 |
|
30 |
return &i->second->counter; |
66 |
|
1 |
return NULL; |
67 |
|
|
} |
68 |
|
|
|
69 |
|
|
|
70 |
|
1 |
string Statistics::LookupDesc(const std::string &name) { |
71 |
|
1 |
MutexLockGuard lock_guard(lock_); |
72 |
|
1 |
map<string, CounterInfo *>::const_iterator i = counters_.find(name); |
73 |
✓✗ |
1 |
if (i != counters_.end()) |
74 |
|
1 |
return i->second->desc; |
75 |
|
|
return ""; |
76 |
|
|
} |
77 |
|
|
|
78 |
|
|
|
79 |
|
1 |
string Statistics::PrintList(const PrintOptions print_options) { |
80 |
|
1 |
string result; |
81 |
✗✓ |
1 |
if (print_options == kPrintHeader) |
82 |
|
|
result += "Name|Value|Description\n"; |
83 |
|
|
|
84 |
|
1 |
MutexLockGuard lock_guard(lock_); |
85 |
✓✓ |
3 |
for (map<string, CounterInfo *>::const_iterator i = counters_.begin(), |
86 |
|
1 |
iEnd = counters_.end(); i != iEnd; ++i) |
87 |
|
|
{ |
88 |
|
|
result += i->first + "|" + i->second->counter.ToString() + |
89 |
|
1 |
"|" + i->second->desc + "\n"; |
90 |
|
|
} |
91 |
|
|
return result; |
92 |
|
|
} |
93 |
|
|
|
94 |
|
|
|
95 |
|
10518 |
Counter *Statistics::Register(const string &name, const string &desc) { |
96 |
|
10518 |
MutexLockGuard lock_guard(lock_); |
97 |
✗✓ |
10518 |
assert(counters_.find(name) == counters_.end()); |
98 |
|
10518 |
CounterInfo *counter_info = new CounterInfo(desc); |
99 |
|
10518 |
counters_[name] = counter_info; |
100 |
|
10518 |
return &counter_info->counter; |
101 |
|
|
} |
102 |
|
|
|
103 |
|
|
|
104 |
|
408 |
Statistics::Statistics() { |
105 |
|
|
lock_ = |
106 |
|
408 |
reinterpret_cast<pthread_mutex_t *>(smalloc(sizeof(pthread_mutex_t))); |
107 |
|
408 |
int retval = pthread_mutex_init(lock_, NULL); |
108 |
✗✓ |
408 |
assert(retval == 0); |
109 |
|
408 |
} |
110 |
|
|
|
111 |
|
|
|
112 |
|
405 |
Statistics::~Statistics() { |
113 |
✓✓ |
12837 |
for (map<string, CounterInfo *>::iterator i = counters_.begin(), |
114 |
|
405 |
iEnd = counters_.end(); i != iEnd; ++i) |
115 |
|
|
{ |
116 |
|
12027 |
int32_t old_value = atomic_xadd32(&i->second->refcnt, -1); |
117 |
✓✓ |
12027 |
if (old_value == 1) |
118 |
✓✗ |
10440 |
delete i->second; |
119 |
|
|
} |
120 |
|
405 |
pthread_mutex_destroy(lock_); |
121 |
|
405 |
free(lock_); |
122 |
|
405 |
} |
123 |
|
|
|
124 |
|
|
|
125 |
|
|
//------------------------------------------------------------------------------ |
126 |
|
|
|
127 |
|
|
|
128 |
|
|
/** |
129 |
|
|
* If necessary, capacity_s is extended to be a multiple of resolution_s |
130 |
|
|
*/ |
131 |
|
455 |
Recorder::Recorder(uint32_t resolution_s, uint32_t capacity_s) |
132 |
|
|
: last_timestamp_(0) |
133 |
|
|
, capacity_s_(capacity_s) |
134 |
|
455 |
, resolution_s_(resolution_s) |
135 |
|
|
{ |
136 |
✓✗✗✓
|
455 |
assert((resolution_s > 0) && (capacity_s > resolution_s)); |
137 |
|
455 |
bool has_remainder = (capacity_s_ % resolution_s_) != 0; |
138 |
✓✓ |
455 |
if (has_remainder) { |
139 |
|
2 |
capacity_s_ += resolution_s_ - (capacity_s_ % resolution_s_); |
140 |
|
|
} |
141 |
|
455 |
no_bins_ = capacity_s_ / resolution_s_; |
142 |
|
455 |
bins_.reserve(no_bins_); |
143 |
✓✓ |
37166 |
for (unsigned i = 0; i < no_bins_; ++i) |
144 |
|
36711 |
bins_.push_back(0); |
145 |
|
|
} |
146 |
|
|
|
147 |
|
|
|
148 |
|
2 |
void Recorder::Tick() { |
149 |
|
2 |
TickAt(platform_monotonic_time()); |
150 |
|
2 |
} |
151 |
|
|
|
152 |
|
|
|
153 |
|
129 |
void Recorder::TickAt(uint64_t timestamp) { |
154 |
|
129 |
uint64_t bin_abs = timestamp / resolution_s_; |
155 |
|
129 |
uint64_t last_bin_abs = last_timestamp_ / resolution_s_; |
156 |
|
|
|
157 |
|
|
// timestamp in the past: don't update last_timestamp_ |
158 |
✓✓ |
129 |
if (bin_abs < last_bin_abs) { |
159 |
|
|
// Do we still remember this event? |
160 |
✓✓ |
2 |
if ((last_bin_abs - bin_abs) < no_bins_) |
161 |
|
1 |
bins_[bin_abs % no_bins_]++; |
162 |
|
2 |
return; |
163 |
|
|
} |
164 |
|
|
|
165 |
✓✓ |
127 |
if (last_bin_abs == bin_abs) { |
166 |
|
29 |
bins_[bin_abs % no_bins_]++; |
167 |
|
|
} else { |
168 |
|
|
// When clearing bins between last_timestamp_ and now, avoid cycling the |
169 |
|
|
// ring buffer multiple times. |
170 |
|
98 |
unsigned max_bins_clear = std::min(bin_abs, last_bin_abs + no_bins_ + 1); |
171 |
✓✓ |
2123 |
for (uint64_t i = last_bin_abs + 1; i < max_bins_clear; ++i) |
172 |
|
2025 |
bins_[i % no_bins_] = 0; |
173 |
|
98 |
bins_[bin_abs % no_bins_] = 1; |
174 |
|
|
} |
175 |
|
|
|
176 |
|
127 |
last_timestamp_ = timestamp; |
177 |
|
|
} |
178 |
|
|
|
179 |
|
|
|
180 |
|
21 |
uint64_t Recorder::GetNoTicks(uint32_t retrospect_s) const { |
181 |
|
21 |
uint64_t now = platform_monotonic_time(); |
182 |
✓✓ |
21 |
if (retrospect_s > now) |
183 |
|
10 |
retrospect_s = now; |
184 |
|
|
|
185 |
|
21 |
uint64_t last_bin_abs = last_timestamp_ / resolution_s_; |
186 |
|
21 |
uint64_t past_bin_abs = (now - retrospect_s) / resolution_s_; |
187 |
|
|
int64_t min_bin_abs = |
188 |
|
|
std::max(past_bin_abs, |
189 |
✓✓ |
21 |
(last_bin_abs < no_bins_) ? 0 : (last_bin_abs - (no_bins_ - 1))); |
190 |
|
21 |
uint64_t result = 0; |
191 |
✓✓ |
172 |
for (int64_t i = last_bin_abs; i >= min_bin_abs; --i) { |
192 |
|
151 |
result += bins_[i % no_bins_]; |
193 |
|
|
} |
194 |
|
|
|
195 |
|
21 |
return result; |
196 |
|
|
} |
197 |
|
|
|
198 |
|
|
|
199 |
|
|
//------------------------------------------------------------------------------ |
200 |
|
|
|
201 |
|
|
|
202 |
|
446 |
void MultiRecorder::AddRecorder(uint32_t resolution_s, uint32_t capacity_s) { |
203 |
|
446 |
recorders_.push_back(Recorder(resolution_s, capacity_s)); |
204 |
|
446 |
} |
205 |
|
|
|
206 |
|
|
|
207 |
|
8 |
uint64_t MultiRecorder::GetNoTicks(uint32_t retrospect_s) const { |
208 |
|
8 |
unsigned N = recorders_.size(); |
209 |
✓✓ |
10 |
for (unsigned i = 0; i < N; ++i) { |
210 |
✓✓✓✓ ✓✓ |
9 |
if ( (recorders_[i].capacity_s() >= retrospect_s) || |
211 |
|
|
(i == (N - 1)) ) |
212 |
|
|
{ |
213 |
|
7 |
return recorders_[i].GetNoTicks(retrospect_s); |
214 |
|
|
} |
215 |
|
|
} |
216 |
|
1 |
return 0; |
217 |
|
|
} |
218 |
|
|
|
219 |
|
|
|
220 |
|
10 |
void MultiRecorder::Tick() { |
221 |
|
10 |
uint64_t now = platform_monotonic_time(); |
222 |
✓✓ |
44 |
for (unsigned i = 0; i < recorders_.size(); ++i) |
223 |
|
34 |
recorders_[i].TickAt(now); |
224 |
|
10 |
} |
225 |
|
|
|
226 |
|
|
|
227 |
|
20 |
void MultiRecorder::TickAt(uint64_t timestamp) { |
228 |
✓✓ |
60 |
for (unsigned i = 0; i < recorders_.size(); ++i) |
229 |
|
40 |
recorders_[i].TickAt(timestamp); |
230 |
|
20 |
} |
231 |
|
|
|
232 |
|
|
} // namespace perf |
233 |
|
|
|
234 |
|
|
|
235 |
|
|
#ifdef CVMFS_NAMESPACE_GUARD |
236 |
|
|
} // namespace CVMFS_NAMESPACE_GUARD |
237 |
|
|
#endif |