block_bitmaps_v3.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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/blockfile/block_bitmaps_v3.h"
6
7#include "base/metrics/histogram.h"
8#include "base/time/time.h"
9#include "net/disk_cache/blockfile/disk_format_base.h"
10#include "net/disk_cache/blockfile/trace.h"
11
12using base::TimeTicks;
13
14namespace disk_cache {
15
16BlockBitmaps::BlockBitmaps() {
17}
18
19BlockBitmaps::~BlockBitmaps() {
20}
21
22void BlockBitmaps::Init(const BlockFilesBitmaps& bitmaps) {
23  bitmaps_ = bitmaps;
24}
25
26bool 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
54void 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
67void BlockBitmaps::Clear() {
68  bitmaps_.clear();
69}
70
71void 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
88bool 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
106int 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
119int 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.
159void 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