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