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