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