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/time/time.h" 9 #include "net/disk_cache/disk_format_base.h" 10 #include "net/disk_cache/trace.h" 11 12 using base::TimeTicks; 13 14 namespace disk_cache { 15 16 BlockBitmaps::BlockBitmaps() { 17 } 18 19 BlockBitmaps::~BlockBitmaps() { 20 } 21 22 void BlockBitmaps::Init(const BlockFilesBitmaps& bitmaps) { 23 bitmaps_ = bitmaps; 24 } 25 26 bool BlockBitmaps::CreateBlock(FileType block_type, 27 int block_count, 28 Addr* block_address) { 29 DCHECK_NE(block_type, EXTERNAL); 30 DCHECK_NE(block_type, RANKINGS); 31 if (block_count < 1 || block_count > kMaxNumBlocks) 32 return false; 33 34 int header_num = HeaderNumberForNewBlock(block_type, block_count); 35 if (header_num < 0) 36 return false; 37 38 int index; 39 if (!bitmaps_[header_num].CreateMapBlock(block_count, &index)) 40 return false; 41 42 if (!index && (block_type == BLOCK_ENTRIES || block_type == BLOCK_EVICTED) && 43 !bitmaps_[header_num].CreateMapBlock(block_count, &index)) { 44 // index 0 for entries is a reserved value. 45 return false; 46 } 47 48 Addr address(block_type, block_count, bitmaps_[header_num].FileId(), index); 49 block_address->set_value(address.value()); 50 Trace("CreateBlock 0x%x", address.value()); 51 return true; 52 } 53 54 void BlockBitmaps::DeleteBlock(Addr address) { 55 if (!address.is_initialized() || address.is_separate_file()) 56 return; 57 58 int header_num = GetHeaderNumber(address); 59 if (header_num < 0) 60 return; 61 62 Trace("DeleteBlock 0x%x", address.value()); 63 bitmaps_[header_num].DeleteMapBlock(address.start_block(), 64 address.num_blocks()); 65 } 66 67 void BlockBitmaps::Clear() { 68 bitmaps_.clear(); 69 } 70 71 void BlockBitmaps::ReportStats() { 72 int used_blocks[kFirstAdditionalBlockFile]; 73 int load[kFirstAdditionalBlockFile]; 74 for (int i = 0; i < kFirstAdditionalBlockFile; i++) { 75 GetFileStats(i, &used_blocks[i], &load[i]); 76 } 77 UMA_HISTOGRAM_COUNTS("DiskCache.Blocks_0", used_blocks[0]); 78 UMA_HISTOGRAM_COUNTS("DiskCache.Blocks_1", used_blocks[1]); 79 UMA_HISTOGRAM_COUNTS("DiskCache.Blocks_2", used_blocks[2]); 80 UMA_HISTOGRAM_COUNTS("DiskCache.Blocks_3", used_blocks[3]); 81 82 UMA_HISTOGRAM_ENUMERATION("DiskCache.BlockLoad_0", load[0], 101); 83 UMA_HISTOGRAM_ENUMERATION("DiskCache.BlockLoad_1", load[1], 101); 84 UMA_HISTOGRAM_ENUMERATION("DiskCache.BlockLoad_2", load[2], 101); 85 UMA_HISTOGRAM_ENUMERATION("DiskCache.BlockLoad_3", load[3], 101); 86 } 87 88 bool BlockBitmaps::IsValid(Addr address) { 89 #ifdef NDEBUG 90 return true; 91 #else 92 if (!address.is_initialized() || address.is_separate_file()) 93 return false; 94 95 int header_num = GetHeaderNumber(address); 96 if (header_num < 0) 97 return false; 98 99 bool rv = bitmaps_[header_num].UsedMapBlock(address.start_block(), 100 address.num_blocks()); 101 DCHECK(rv); 102 return rv; 103 #endif 104 } 105 106 int BlockBitmaps::GetHeaderNumber(Addr address) { 107 DCHECK_GE(bitmaps_.size(), static_cast<size_t>(kFirstAdditionalBlockFileV3)); 108 DCHECK(address.is_block_file() || !address.is_initialized()); 109 if (!address.is_initialized()) 110 return -1; 111 112 int file_index = address.FileNumber(); 113 if (static_cast<unsigned int>(file_index) >= bitmaps_.size()) 114 return -1; 115 116 return file_index; 117 } 118 119 int BlockBitmaps::HeaderNumberForNewBlock(FileType block_type, 120 int block_count) { 121 DCHECK_GT(block_type, 0); 122 int header_num = block_type - 1; 123 bool found = true; 124 125 TimeTicks start = TimeTicks::Now(); 126 while (bitmaps_[header_num].NeedToGrowBlockFile(block_count)) { 127 header_num = bitmaps_[header_num].NextFileId(); 128 if (!header_num) { 129 found = false; 130 break; 131 } 132 } 133 134 if (!found) { 135 // Restart the search, looking for any file with space. We know that all 136 // files of this type are low on free blocks, but we cannot grow any file 137 // at this time. 138 header_num = block_type - 1; 139 do { 140 if (bitmaps_[header_num].CanAllocate(block_count)) { 141 found = true; // Make sure file 0 is not mistaken with a failure. 142 break; 143 } 144 header_num = bitmaps_[header_num].NextFileId(); 145 } while (header_num); 146 147 if (!found) 148 header_num = -1; 149 } 150 151 HISTOGRAM_TIMES("DiskCache.GetFileForNewBlock", TimeTicks::Now() - start); 152 return header_num; 153 } 154 155 // We are interested in the total number of blocks used by this file type, and 156 // the max number of blocks that we can store (reported as the percentage of 157 // used blocks). In order to find out the number of used blocks, we have to 158 // substract the empty blocks from the total blocks for each file in the chain. 159 void BlockBitmaps::GetFileStats(int index, int* used_count, int* load) { 160 int max_blocks = 0; 161 *used_count = 0; 162 *load = 0; 163 do { 164 int capacity = bitmaps_[index].Capacity(); 165 int used = capacity - bitmaps_[index].EmptyBlocks(); 166 DCHECK_GE(used, 0); 167 168 max_blocks += capacity; 169 *used_count += used; 170 171 index = bitmaps_[index].NextFileId(); 172 } while (index); 173 174 if (max_blocks) 175 *load = *used_count * 100 / max_blocks; 176 } 177 178 } // namespace disk_cache 179