GCC Code Coverage Report
Directory: cvmfs/ Exec Total Coverage
File: cvmfs/statistics.cc Lines: 112 115 97.4 %
Date: 2019-02-03 02:48:13 Branches: 47 54 87.0 %

Line Branch Exec Source
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