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