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