]>
Commit | Line | Data |
---|---|---|
a71665e5 O |
1 | /* |
2 | * This file is part of PowerDNS or dnsdist. | |
3 | * Copyright -- PowerDNS.COM B.V. and its contributors | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of version 2 of the GNU General Public License as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * In addition, for the avoidance of any doubt, permission is granted to | |
10 | * link this program with OpenSSL and to (re)distribute the binaries | |
11 | * produced as the result of such linking. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
21 | */ | |
2bd76c77 O |
22 | #pragma once |
23 | ||
7d3d2f4f | 24 | #include <cassert> |
2bd76c77 | 25 | #include <algorithm> |
27136325 | 26 | #include <limits> |
b521df99 | 27 | #include <stdexcept> |
097b1e68 | 28 | #include <string> |
2bd76c77 | 29 | #include <vector> |
2bd76c77 | 30 | |
7db617e1 O |
31 | #include "stat_t.hh" |
32 | ||
342708c0 O |
33 | namespace pdns |
34 | { | |
2bd76c77 O |
35 | |
36 | // By convention, we are using microsecond units | |
37 | struct Bucket | |
38 | { | |
7d3d2f4f OM |
39 | Bucket(std::string name, uint64_t boundary, uint64_t val) : |
40 | d_name(std::move(name)), d_boundary(boundary), d_count(val) {} | |
cc1f5390 OM |
41 | const std::string d_name; |
42 | const uint64_t d_boundary; | |
dad777b6 | 43 | mutable uint64_t d_count{0}; |
cc1f5390 OM |
44 | |
45 | Bucket(const Bucket&) = default; | |
46 | Bucket& operator=(const Bucket& rhs) | |
47 | { | |
48 | assert(d_name == rhs.d_name); | |
49 | assert(d_boundary == rhs.d_boundary); | |
50 | d_count = rhs.d_count; | |
51 | return *this; | |
52 | } | |
2bd76c77 O |
53 | }; |
54 | ||
2bd76c77 O |
55 | struct AtomicBucket |
56 | { | |
a71665e5 | 57 | // We need the constructors in this case, since atomics have a disabled copy constructor. |
abb11ca4 | 58 | AtomicBucket() = default; |
342708c0 O |
59 | AtomicBucket(std::string name, uint64_t boundary, uint64_t val) : |
60 | d_name(std::move(name)), d_boundary(boundary), d_count(val) {} | |
61 | AtomicBucket(const AtomicBucket& rhs) : | |
62 | d_name(rhs.d_name), d_boundary(rhs.d_boundary), d_count(rhs.d_count.load()) {} | |
2bd76c77 | 63 | |
dad777b6 RG |
64 | const std::string d_name; |
65 | const uint64_t d_boundary{0}; | |
7db617e1 | 66 | mutable stat_t d_count{0}; |
2bd76c77 O |
67 | }; |
68 | ||
8a885264 | 69 | template <class B, class SumType> |
2bd76c77 O |
70 | class BaseHistogram |
71 | { | |
72 | public: | |
3a63bf69 O |
73 | BaseHistogram(const std::string& prefix, const std::vector<uint64_t>& boundaries) : |
74 | d_name(prefix) | |
2bd76c77 O |
75 | { |
76 | if (!std::is_sorted(boundaries.cbegin(), boundaries.cend())) { | |
77 | throw std::invalid_argument("boundary array must be sorted"); | |
78 | } | |
79 | if (boundaries.size() == 0) { | |
80 | throw std::invalid_argument("boundary array must not be empty"); | |
81 | } | |
82 | if (boundaries[0] == 0) { | |
83 | throw std::invalid_argument("boundary array's first element should not be zero"); | |
84 | } | |
85 | d_buckets.reserve(boundaries.size() + 1); | |
02d23ff9 | 86 | uint64_t prev = 0; |
342708c0 | 87 | for (auto b : boundaries) { |
02d23ff9 O |
88 | if (prev == b) { |
89 | throw std::invalid_argument("boundary array's elements should be distinct"); | |
90 | } | |
a71665e5 | 91 | std::string str = prefix + "le-" + std::to_string(b); |
0c2e35c2 | 92 | d_buckets.emplace_back(str, b, 0); |
02d23ff9 | 93 | prev = b; |
2bd76c77 | 94 | } |
e616af3c | 95 | |
a71665e5 | 96 | // everything above last boundary |
0c2e35c2 | 97 | d_buckets.emplace_back(prefix + "le-max", std::numeric_limits<uint64_t>::max(), 0); |
2bd76c77 O |
98 | } |
99 | ||
e616af3c O |
100 | BaseHistogram(const std::string& prefix, uint64_t start, int num) : |
101 | BaseHistogram(prefix, to125(start, num)) | |
102 | { | |
103 | } | |
104 | ||
105 | std::string getName() const | |
106 | { | |
107 | return d_name; | |
108 | } | |
109 | ||
8a885264 O |
110 | uint64_t getSum() const |
111 | { | |
112 | return d_sum; | |
113 | } | |
114 | ||
2bd76c77 O |
115 | const std::vector<B>& getRawData() const |
116 | { | |
117 | return d_buckets; | |
118 | } | |
119 | ||
120 | uint64_t getCount(size_t i) const | |
121 | { | |
122 | return d_buckets[i].d_count; | |
123 | } | |
124 | ||
125 | std::vector<B> getCumulativeBuckets() const | |
126 | { | |
127 | std::vector<B> ret; | |
128 | ret.reserve(d_buckets.size()); | |
129 | uint64_t c{0}; | |
130 | for (const auto& b : d_buckets) { | |
131 | c += b.d_count; | |
0c2e35c2 | 132 | ret.emplace_back(b.d_name, b.d_boundary, c); |
2bd76c77 O |
133 | } |
134 | return ret; | |
135 | } | |
136 | ||
137 | std::vector<uint64_t> getCumulativeCounts() const | |
138 | { | |
139 | std::vector<uint64_t> ret; | |
140 | ret.reserve(d_buckets.size()); | |
141 | uint64_t c = 0; | |
142 | for (const auto& b : d_buckets) { | |
143 | c += b.d_count; | |
02d23ff9 | 144 | ret.emplace_back(c); |
2bd76c77 O |
145 | } |
146 | return ret; | |
147 | } | |
148 | ||
02d23ff9 O |
149 | static bool lessOrEqual(uint64_t b, const B& bu) |
150 | { | |
151 | return b <= bu.d_boundary; | |
152 | } | |
153 | ||
dad777b6 | 154 | inline void operator()(uint64_t d) const |
2bd76c77 | 155 | { |
02d23ff9 | 156 | auto index = std::upper_bound(d_buckets.begin(), d_buckets.end(), d, lessOrEqual); |
342708c0 | 157 | // our index is always valid |
2bd76c77 | 158 | ++index->d_count; |
8a885264 | 159 | d_sum += d; |
2bd76c77 O |
160 | } |
161 | ||
7d3d2f4f OM |
162 | BaseHistogram& operator+=(const BaseHistogram& rhs) |
163 | { | |
164 | assert(d_name == rhs.d_name); | |
165 | assert(d_buckets.size() == rhs.d_buckets.size()); | |
166 | for (size_t bucket = 0; bucket < d_buckets.size(); ++bucket) { | |
167 | assert(d_buckets[bucket].d_name == rhs.d_buckets[bucket].d_name); | |
168 | assert(d_buckets[bucket].d_boundary == rhs.d_buckets[bucket].d_boundary); | |
169 | d_buckets[bucket].d_count += rhs.d_buckets[bucket].d_count; | |
170 | } | |
171 | d_sum += rhs.d_sum; | |
172 | return *this; | |
173 | } | |
174 | ||
2bd76c77 O |
175 | private: |
176 | std::vector<B> d_buckets; | |
7d3d2f4f | 177 | std::string d_name; |
8a885264 | 178 | mutable SumType d_sum{0}; |
e616af3c | 179 | |
e616af3c O |
180 | std::vector<uint64_t> to125(uint64_t start, int num) |
181 | { | |
182 | std::vector<uint64_t> boundaries; | |
183 | boundaries.reserve(num); | |
184 | boundaries.emplace_back(start); | |
185 | int i = 0; | |
186 | while (true) { | |
187 | if (++i >= num) { | |
188 | break; | |
189 | } | |
190 | boundaries.push_back(2 * start); | |
191 | if (++i >= num) { | |
192 | break; | |
193 | } | |
194 | boundaries.push_back(5 * start); | |
195 | if (++i >= num) { | |
196 | break; | |
197 | } | |
198 | boundaries.push_back(10 * start); | |
199 | start *= 10; | |
200 | } | |
201 | return boundaries; | |
202 | } | |
2bd76c77 O |
203 | }; |
204 | ||
8a885264 | 205 | using Histogram = BaseHistogram<Bucket, uint64_t>; |
2bd76c77 | 206 | |
7db617e1 | 207 | using AtomicHistogram = BaseHistogram<AtomicBucket, pdns::stat_t>; |
2bd76c77 O |
208 | |
209 | } // namespace pdns |