1 // Copyright 2011 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/disk_cache/blockfile/stats.h"
6
7 #include "base/check.h"
8 #include "base/format_macros.h"
9 #include "base/metrics/bucket_ranges.h"
10 #include "base/metrics/histogram.h"
11 #include "base/metrics/histogram_samples.h"
12 #include "base/metrics/sample_vector.h"
13 #include "base/metrics/statistics_recorder.h"
14 #include "base/notreached.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/stringprintf.h"
17
18 namespace {
19
20 const int32_t kDiskSignature = 0xF01427E0;
21
22 struct OnDiskStats {
23 int32_t signature;
24 int size;
25 int data_sizes[disk_cache::Stats::kDataSizesLength];
26 int64_t counters[disk_cache::Stats::MAX_COUNTER];
27 };
28 static_assert(sizeof(OnDiskStats) < 512, "needs more than 2 blocks");
29
30 // Returns the "floor" (as opposed to "ceiling") of log base 2 of number.
LogBase2(int32_t number)31 int LogBase2(int32_t number) {
32 unsigned int value = static_cast<unsigned int>(number);
33 const unsigned int mask[] = {0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000};
34 const unsigned int s[] = {1, 2, 4, 8, 16};
35
36 unsigned int result = 0;
37 for (int i = 4; i >= 0; i--) {
38 if (value & mask[i]) {
39 value >>= s[i];
40 result |= s[i];
41 }
42 }
43 return static_cast<int>(result);
44 }
45
46 // WARNING: Add new stats only at the end, or change LoadStats().
47 const char* const kCounterNames[] = {
48 "Open miss",
49 "Open hit",
50 "Create miss",
51 "Create hit",
52 "Resurrect hit",
53 "Create error",
54 "Trim entry",
55 "Doom entry",
56 "Doom cache",
57 "Invalid entry",
58 "Open entries",
59 "Max entries",
60 "Timer",
61 "Read data",
62 "Write data",
63 "Open rankings",
64 "Get rankings",
65 "Fatal error",
66 "Last report",
67 "Last report timer",
68 "Doom recent entries",
69 "unused"
70 };
71 static_assert(std::size(kCounterNames) == disk_cache::Stats::MAX_COUNTER,
72 "update the names");
73
74 } // namespace
75
76 namespace disk_cache {
77
VerifyStats(OnDiskStats * stats)78 bool VerifyStats(OnDiskStats* stats) {
79 if (stats->signature != kDiskSignature)
80 return false;
81
82 // We don't want to discard the whole cache every time we have one extra
83 // counter; we keep old data if we can.
84 if (static_cast<unsigned int>(stats->size) > sizeof(*stats)) {
85 memset(stats, 0, sizeof(*stats));
86 stats->signature = kDiskSignature;
87 } else if (static_cast<unsigned int>(stats->size) != sizeof(*stats)) {
88 size_t delta = sizeof(*stats) - static_cast<unsigned int>(stats->size);
89 memset(reinterpret_cast<char*>(stats) + stats->size, 0, delta);
90 stats->size = sizeof(*stats);
91 }
92
93 return true;
94 }
95
96 Stats::Stats() = default;
97
98 Stats::~Stats() = default;
99
Init(void * data,int num_bytes,Addr address)100 bool Stats::Init(void* data, int num_bytes, Addr address) {
101 OnDiskStats local_stats;
102 OnDiskStats* stats = &local_stats;
103 if (!num_bytes) {
104 memset(stats, 0, sizeof(local_stats));
105 local_stats.signature = kDiskSignature;
106 local_stats.size = sizeof(local_stats);
107 } else if (num_bytes >= static_cast<int>(sizeof(*stats))) {
108 stats = reinterpret_cast<OnDiskStats*>(data);
109 if (!VerifyStats(stats)) {
110 memset(&local_stats, 0, sizeof(local_stats));
111 if (memcmp(stats, &local_stats, sizeof(local_stats))) {
112 return false;
113 } else {
114 // The storage is empty which means that SerializeStats() was never
115 // called on the last run. Just re-initialize everything.
116 local_stats.signature = kDiskSignature;
117 local_stats.size = sizeof(local_stats);
118 stats = &local_stats;
119 }
120 }
121 } else {
122 return false;
123 }
124
125 storage_addr_ = address;
126
127 memcpy(data_sizes_, stats->data_sizes, sizeof(data_sizes_));
128 memcpy(counters_, stats->counters, sizeof(counters_));
129
130 // Clean up old value.
131 SetCounter(UNUSED, 0);
132 return true;
133 }
134
InitSizeHistogram()135 void Stats::InitSizeHistogram() {
136 // Only generate this histogram for the main cache.
137 static bool first_time = true;
138 if (!first_time)
139 return;
140
141 first_time = false;
142 for (int& data_size : data_sizes_) {
143 // This is a good time to fix any inconsistent data. The count should be
144 // always positive, but if it's not, reset the value now.
145 if (data_size < 0)
146 data_size = 0;
147 }
148 }
149
StorageSize()150 int Stats::StorageSize() {
151 // If we have more than 512 bytes of counters, change kDiskSignature so we
152 // don't overwrite something else (LoadStats must fail).
153 static_assert(sizeof(OnDiskStats) <= 256 * 2, "use more blocks");
154 return 256 * 2;
155 }
156
ModifyStorageStats(int32_t old_size,int32_t new_size)157 void Stats::ModifyStorageStats(int32_t old_size, int32_t new_size) {
158 // We keep a counter of the data block size on an array where each entry is
159 // the adjusted log base 2 of the size. The first entry counts blocks of 256
160 // bytes, the second blocks up to 512 bytes, etc. With 20 entries, the last
161 // one stores entries of more than 64 MB
162 int new_index = GetStatsBucket(new_size);
163 int old_index = GetStatsBucket(old_size);
164
165 if (new_size)
166 data_sizes_[new_index]++;
167
168 if (old_size)
169 data_sizes_[old_index]--;
170 }
171
OnEvent(Counters an_event)172 void Stats::OnEvent(Counters an_event) {
173 DCHECK(an_event >= MIN_COUNTER && an_event < MAX_COUNTER);
174 counters_[an_event]++;
175 }
176
SetCounter(Counters counter,int64_t value)177 void Stats::SetCounter(Counters counter, int64_t value) {
178 DCHECK(counter >= MIN_COUNTER && counter < MAX_COUNTER);
179 counters_[counter] = value;
180 }
181
GetCounter(Counters counter) const182 int64_t Stats::GetCounter(Counters counter) const {
183 DCHECK(counter >= MIN_COUNTER && counter < MAX_COUNTER);
184 return counters_[counter];
185 }
186
GetItems(StatsItems * items)187 void Stats::GetItems(StatsItems* items) {
188 std::pair<std::string, std::string> item;
189 for (int i = 0; i < kDataSizesLength; i++) {
190 item.first = base::StringPrintf("Size%02d", i);
191 item.second = base::StringPrintf("0x%08x", data_sizes_[i]);
192 items->push_back(item);
193 }
194
195 for (int i = MIN_COUNTER; i < MAX_COUNTER; i++) {
196 item.first = kCounterNames[i];
197 item.second = base::StringPrintf("0x%" PRIx64, counters_[i]);
198 items->push_back(item);
199 }
200 }
201
GetHitRatio() const202 int Stats::GetHitRatio() const {
203 return GetRatio(OPEN_HIT, OPEN_MISS);
204 }
205
GetResurrectRatio() const206 int Stats::GetResurrectRatio() const {
207 return GetRatio(RESURRECT_HIT, CREATE_HIT);
208 }
209
ResetRatios()210 void Stats::ResetRatios() {
211 SetCounter(OPEN_HIT, 0);
212 SetCounter(OPEN_MISS, 0);
213 SetCounter(RESURRECT_HIT, 0);
214 SetCounter(CREATE_HIT, 0);
215 }
216
GetLargeEntriesSize()217 int Stats::GetLargeEntriesSize() {
218 int total = 0;
219 // data_sizes_[20] stores values between 512 KB and 1 MB (see comment before
220 // GetStatsBucket()).
221 for (int bucket = 20; bucket < kDataSizesLength; bucket++)
222 total += data_sizes_[bucket] * GetBucketRange(bucket);
223
224 return total;
225 }
226
SerializeStats(void * data,int num_bytes,Addr * address)227 int Stats::SerializeStats(void* data, int num_bytes, Addr* address) {
228 OnDiskStats* stats = reinterpret_cast<OnDiskStats*>(data);
229 if (num_bytes < static_cast<int>(sizeof(*stats)))
230 return 0;
231
232 stats->signature = kDiskSignature;
233 stats->size = sizeof(*stats);
234 memcpy(stats->data_sizes, data_sizes_, sizeof(data_sizes_));
235 memcpy(stats->counters, counters_, sizeof(counters_));
236
237 *address = storage_addr_;
238 return sizeof(*stats);
239 }
240
GetBucketRange(size_t i) const241 int Stats::GetBucketRange(size_t i) const {
242 if (i < 2)
243 return static_cast<int>(1024 * i);
244
245 if (i < 12)
246 return static_cast<int>(2048 * (i - 1));
247
248 if (i < 17)
249 return static_cast<int>(4096 * (i - 11)) + 20 * 1024;
250
251 int n = 64 * 1024;
252 if (i > static_cast<size_t>(kDataSizesLength)) {
253 NOTREACHED();
254 i = kDataSizesLength;
255 }
256
257 i -= 17;
258 n <<= i;
259 return n;
260 }
261
262 // The array will be filled this way:
263 // index size
264 // 0 [0, 1024)
265 // 1 [1024, 2048)
266 // 2 [2048, 4096)
267 // 3 [4K, 6K)
268 // ...
269 // 10 [18K, 20K)
270 // 11 [20K, 24K)
271 // 12 [24k, 28K)
272 // ...
273 // 15 [36k, 40K)
274 // 16 [40k, 64K)
275 // 17 [64K, 128K)
276 // 18 [128K, 256K)
277 // ...
278 // 23 [4M, 8M)
279 // 24 [8M, 16M)
280 // 25 [16M, 32M)
281 // 26 [32M, 64M)
282 // 27 [64M, ...)
GetStatsBucket(int32_t size)283 int Stats::GetStatsBucket(int32_t size) {
284 if (size < 1024)
285 return 0;
286
287 // 10 slots more, until 20K.
288 if (size < 20 * 1024)
289 return size / 2048 + 1;
290
291 // 5 slots more, from 20K to 40K.
292 if (size < 40 * 1024)
293 return (size - 20 * 1024) / 4096 + 11;
294
295 // From this point on, use a logarithmic scale.
296 int result = LogBase2(size) + 1;
297
298 static_assert(kDataSizesLength > 16, "update the scale");
299 if (result >= kDataSizesLength)
300 result = kDataSizesLength - 1;
301
302 return result;
303 }
304
GetRatio(Counters hit,Counters miss) const305 int Stats::GetRatio(Counters hit, Counters miss) const {
306 int64_t ratio = GetCounter(hit) * 100;
307 if (!ratio)
308 return 0;
309
310 ratio /= (GetCounter(hit) + GetCounter(miss));
311 return static_cast<int>(ratio);
312 }
313
314 } // namespace disk_cache
315