• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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