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