xref: /aosp_15_r20/external/cronet/net/disk_cache/blockfile/stats.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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