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