• 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 #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