OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012 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/v3/block_bitmaps.h" |
| 6 |
| 7 #include "base/metrics/histogram.h" |
| 8 #include "base/string_util.h" |
| 9 #include "base/stringprintf.h" |
| 10 #include "base/threading/thread_checker.h" |
| 11 #include "base/time.h" |
| 12 #include "net/disk_cache/cache_util.h" |
| 13 #include "net/disk_cache/disk_format_base.h" |
| 14 #include "net/disk_cache/file_lock.h" |
| 15 #include "net/disk_cache/trace.h" |
| 16 #include "net/disk_cache/v3/backend_impl_v3.h" |
| 17 |
| 18 using base::TimeTicks; |
| 19 |
| 20 namespace disk_cache { |
| 21 |
| 22 BlockBitmaps::BlockBitmaps(BackendImplV3* backend) : backend_(backend) { |
| 23 } |
| 24 |
| 25 BlockBitmaps::~BlockBitmaps() { |
| 26 } |
| 27 |
| 28 void BlockBitmaps::Init(const BlockFilesBitmaps& bitmaps) { |
| 29 bitmaps_ = bitmaps; |
| 30 for (int i = 0; i < kFirstAdditionalBlockFileV3; i++) |
| 31 empty_counts_[i] = EmptyBlocksForType(i); |
| 32 } |
| 33 |
| 34 int BlockBitmaps::GetHeader(Addr address) { |
| 35 DCHECK(bitmaps_.size() >= kFirstAdditionalBlockFileV3); |
| 36 DCHECK(address.is_block_file() || !address.is_initialized()); |
| 37 if (!address.is_initialized()) |
| 38 return NULL; |
| 39 |
| 40 int file_index = address.FileNumber(); |
| 41 if (static_cast<unsigned int>(file_index) >= bitmaps_.size() || |
| 42 !bitmaps_[file_index].Get()) { |
| 43 return -1; |
| 44 } |
| 45 DCHECK(bitmaps_.size() >= static_cast<unsigned int>(file_index)); |
| 46 return file_index; |
| 47 } |
| 48 |
| 49 bool BlockBitmaps::CreateBlock(FileType block_type, int block_count, |
| 50 Addr* block_address) { |
| 51 if (block_type < BLOCK_256 || block_type > BLOCK_EVICTED || |
| 52 block_count < 1 || block_count > 4) |
| 53 return false; |
| 54 |
| 55 int header_num = HeaderForNewBlock(block_type, block_count); |
| 56 if (header_num < 0) |
| 57 return false; |
| 58 |
| 59 int target_size = 0; |
| 60 for (int i = block_count; i <= 4; i++) { |
| 61 if (bitmaps_[header_num]->empty[i - 1]) { |
| 62 target_size = i; |
| 63 break; |
| 64 } |
| 65 } |
| 66 |
| 67 DCHECK(target_size); |
| 68 int index; |
| 69 if (!bitmaps_[header_num].CreateMapBlock(target_size, block_count, &index)) |
| 70 return false; |
| 71 |
| 72 if (!index && (block_type == BLOCK_ENTRIES || block_type == BLOCK_EVICTED) && |
| 73 !bitmaps_[header_num].CreateMapBlock(target_size, block_count, &index)) { |
| 74 // index 0 for entries is a reserved value. |
| 75 return false; |
| 76 } |
| 77 |
| 78 // Yes, the count may be off by 1 when we start. |
| 79 empty_counts_[target_size] -= block_count; |
| 80 if (empty_counts_[target_size] < kNumExtraBlocks / 2) |
| 81 backend_->GrowBlockFiles(); |
| 82 |
| 83 Addr address(block_type, block_count, bitmaps_[header_num]->this_file, index); |
| 84 block_address->set_value(address.value()); |
| 85 Trace("CreateBlock 0x%x", address.value()); |
| 86 return true; |
| 87 } |
| 88 |
| 89 void BlockBitmaps::DeleteBlock(Addr address) { |
| 90 if (!address.is_initialized() || address.is_separate_file()) |
| 91 return; |
| 92 |
| 93 int header_num = GetHeader(address); |
| 94 if (header_num < 0) |
| 95 return; |
| 96 |
| 97 Trace("DeleteBlock 0x%x", address.value()); |
| 98 bitmaps_[header_num].DeleteMapBlock(address.start_block(), |
| 99 address.num_blocks()); |
| 100 |
| 101 if (!bitmaps_[header_num]->num_entries) { |
| 102 // This file is now empty. Let's try to delete it. |
| 103 //FileType type = Addr::RequiredFileType(header->entry_size); |
| 104 //if (Addr::BlockSizeForFileType(RANKINGS) == header->entry_size) |
| 105 // type = RANKINGS; |
| 106 //RemoveEmptyFile(type); // Ignore failures. |
| 107 } |
| 108 } |
| 109 |
| 110 void BlockBitmaps::Clear() { |
| 111 bitmaps_.clear(); |
| 112 } |
| 113 |
| 114 void BlockBitmaps::ReportStats() { |
| 115 int used_blocks[kFirstAdditionalBlockFile]; |
| 116 int load[kFirstAdditionalBlockFile]; |
| 117 for (int i = 0; i < kFirstAdditionalBlockFile; i++) { |
| 118 GetFileStats(i, &used_blocks[i], &load[i]); |
| 119 } |
| 120 UMA_HISTOGRAM_COUNTS("DiskCache.Blocks_0", used_blocks[0]); |
| 121 UMA_HISTOGRAM_COUNTS("DiskCache.Blocks_1", used_blocks[1]); |
| 122 UMA_HISTOGRAM_COUNTS("DiskCache.Blocks_2", used_blocks[2]); |
| 123 UMA_HISTOGRAM_COUNTS("DiskCache.Blocks_3", used_blocks[3]); |
| 124 |
| 125 UMA_HISTOGRAM_ENUMERATION("DiskCache.BlockLoad_0", load[0], 101); |
| 126 UMA_HISTOGRAM_ENUMERATION("DiskCache.BlockLoad_1", load[1], 101); |
| 127 UMA_HISTOGRAM_ENUMERATION("DiskCache.BlockLoad_2", load[2], 101); |
| 128 UMA_HISTOGRAM_ENUMERATION("DiskCache.BlockLoad_3", load[3], 101); |
| 129 } |
| 130 |
| 131 bool BlockBitmaps::IsValid(Addr address) { |
| 132 #ifdef NDEBUG |
| 133 return true; |
| 134 #else |
| 135 if (!address.is_initialized() || address.is_separate_file()) |
| 136 return false; |
| 137 |
| 138 int header_num = GetHeader(address); |
| 139 if (header_num < 0) |
| 140 return false; |
| 141 |
| 142 bool rv = bitmaps_[header_num].UsedMapBlock(address.start_block(), |
| 143 address.num_blocks()); |
| 144 DCHECK(rv); |
| 145 return rv; |
| 146 #endif |
| 147 } |
| 148 |
| 149 int BlockBitmaps::EmptyBlocksForType(int next_file) { |
| 150 int empty_blocks = 0; |
| 151 do { |
| 152 empty_blocks += bitmaps_[next_file].EmptyBlocks(); |
| 153 next_file = bitmaps_[next_file]->next_file; |
| 154 } while (next_file); |
| 155 return empty_blocks; |
| 156 } |
| 157 |
| 158 int BlockBitmaps::HeaderForNewBlock(FileType block_type, int block_count) { |
| 159 COMPILE_ASSERT(RANKINGS == 1, invalid_file_type); |
| 160 int header_num = block_type - 1; |
| 161 bool found = true; |
| 162 |
| 163 TimeTicks start = TimeTicks::Now(); |
| 164 while (bitmaps_[header_num].NeedToGrowBlockFile(block_count)) { |
| 165 header_num = bitmaps_[header_num]->next_file; |
| 166 if (!header_num) { |
| 167 found = false; |
| 168 break; |
| 169 } |
| 170 } |
| 171 |
| 172 if (!found) { |
| 173 // Restart the search, looking for any file with space. |
| 174 header_num = block_type - 1; |
| 175 do { |
| 176 if (bitmaps_[header_num].CanAllocate(block_count)) |
| 177 found = true; |
| 178 else |
| 179 header_num = bitmaps_[header_num]->next_file; |
| 180 } while (header_num && !found); |
| 181 if (!found) { |
| 182 NOTREACHED(); |
| 183 header_num = -1; |
| 184 } |
| 185 } |
| 186 |
| 187 HISTOGRAM_TIMES("DiskCache.GetFileForNewBlock", TimeTicks::Now() - start); |
| 188 return header_num; |
| 189 } |
| 190 |
| 191 // Note that we expect to be called outside of a FileLock... however, we cannot |
| 192 // DCHECK on header->updating because we may be fixing a crash. |
| 193 bool BlockBitmaps::FixBlockFileHeader(MappedFile* file) { |
| 194 ScopedFlush flush(file); |
| 195 BlockHeader header(file); |
| 196 int file_size = static_cast<int>(file->GetLength()); |
| 197 if (file_size < header.Size()) |
| 198 return false; // file_size > 2GB is also an error. |
| 199 |
| 200 const int kMinBlockSize = 36; |
| 201 const int kMaxBlockSize = 4096; |
| 202 if (header->entry_size < kMinBlockSize || |
| 203 header->entry_size > kMaxBlockSize || header->num_entries < 0) |
| 204 return false; |
| 205 |
| 206 // Make sure that we survive crashes. |
| 207 header->updating = 1; |
| 208 int expected = header->entry_size * header->max_entries + header.Size(); |
| 209 if (file_size != expected) { |
| 210 int max_expected = header->entry_size * kMaxBlocks + header.Size(); |
| 211 if (file_size < expected || header->empty[3] || file_size > max_expected) { |
| 212 NOTREACHED(); |
| 213 LOG(ERROR) << "Unexpected file size"; |
| 214 return false; |
| 215 } |
| 216 // We were in the middle of growing the file. |
| 217 int num_entries = (file_size - header.Size()) / header->entry_size; |
| 218 header->max_entries = num_entries; |
| 219 } |
| 220 |
| 221 header.FixAllocationCounters(); |
| 222 int empty_blocks = header.EmptyBlocks(); |
| 223 if (empty_blocks + header->num_entries > header->max_entries) |
| 224 header->num_entries = header->max_entries - empty_blocks; |
| 225 |
| 226 if (!header.ValidateCounters()) |
| 227 return false; |
| 228 |
| 229 header->updating = 0; |
| 230 return true; |
| 231 } |
| 232 |
| 233 // We are interested in the total number of blocks used by this file type, and |
| 234 // the max number of blocks that we can store (reported as the percentage of |
| 235 // used blocks). In order to find out the number of used blocks, we have to |
| 236 // substract the empty blocks from the total blocks for each file in the chain. |
| 237 void BlockBitmaps::GetFileStats(int index, int* used_count, int* load) { |
| 238 int max_blocks = 0; |
| 239 *used_count = 0; |
| 240 *load = 0; |
| 241 for (;;) { |
| 242 BlockFileHeader* header = bitmaps_[index].Get(); |
| 243 |
| 244 max_blocks += header->max_entries; |
| 245 int used = header->max_entries; |
| 246 for (int i = 0; i < 4; i++) { |
| 247 used -= header->empty[i] * (i + 1); |
| 248 DCHECK_GE(used, 0); |
| 249 } |
| 250 *used_count += used; |
| 251 |
| 252 if (!header->next_file) |
| 253 break; |
| 254 index = header->next_file; |
| 255 } |
| 256 if (max_blocks) |
| 257 *load = *used_count * 100 / max_blocks; |
| 258 } |
| 259 |
| 260 } // namespace disk_cache |
OLD | NEW |