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