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.
5 // Performs basic inspection of the disk cache files with minimal disruption
6 // to the actual files (they still may change if an error is detected on the
9 #include "net/tools/dump_cache/dump_files.h"
16 #include "base/file_util.h"
17 #include "base/files/file.h"
18 #include "base/files/file_enumerator.h"
19 #include "base/format_macros.h"
20 #include "base/message_loop/message_loop.h"
21 #include "net/disk_cache/blockfile/block_files.h"
22 #include "net/disk_cache/blockfile/disk_format.h"
23 #include "net/disk_cache/blockfile/mapped_file.h"
24 #include "net/disk_cache/blockfile/stats.h"
25 #include "net/disk_cache/blockfile/storage_block-inl.h"
26 #include "net/disk_cache/blockfile/storage_block.h"
30 const base::FilePath::CharType kIndexName
[] = FILE_PATH_LITERAL("index");
32 // Reads the |header_size| bytes from the beginning of file |name|.
33 bool ReadHeader(const base::FilePath
& name
, char* header
, int header_size
) {
34 base::File
file(name
, base::File::FLAG_OPEN
| base::File::FLAG_READ
);
35 if (!file
.IsValid()) {
36 printf("Unable to open file %s\n", name
.MaybeAsASCII().c_str());
40 int read
= file
.Read(0, header
, header_size
);
41 if (read
!= header_size
) {
42 printf("Unable to read file %s\n", name
.MaybeAsASCII().c_str());
48 int GetMajorVersionFromFile(const base::FilePath
& name
) {
49 disk_cache::IndexHeader header
;
50 if (!ReadHeader(name
, reinterpret_cast<char*>(&header
), sizeof(header
)))
53 return header
.version
>> 16;
56 // Dumps the contents of the Stats record.
57 void DumpStats(const base::FilePath
& path
, disk_cache::CacheAddr addr
) {
58 // We need a message loop, although we really don't run any task.
59 base::MessageLoopForIO loop
;
61 disk_cache::BlockFiles
block_files(path
);
62 if (!block_files
.Init(false)) {
63 printf("Unable to init block files\n");
67 disk_cache::Addr
address(addr
);
68 disk_cache::MappedFile
* file
= block_files
.GetFile(address
);
72 size_t length
= (2 + disk_cache::Stats::kDataSizesLength
) * sizeof(int32
) +
73 disk_cache::Stats::MAX_COUNTER
* sizeof(int64
);
75 size_t offset
= address
.start_block() * address
.BlockSize() +
76 disk_cache::kBlockHeaderSize
;
78 scoped_ptr
<int32
[]> buffer(new int32
[length
]);
79 if (!file
->Read(buffer
.get(), length
, offset
))
82 printf("Stats:\nSignatrure: 0x%x\n", buffer
[0]);
83 printf("Total size: %d\n", buffer
[1]);
84 for (int i
= 0; i
< disk_cache::Stats::kDataSizesLength
; i
++)
85 printf("Size(%d): %d\n", i
, buffer
[i
+ 2]);
87 int64
* counters
= reinterpret_cast<int64
*>(
88 buffer
.get() + 2 + disk_cache::Stats::kDataSizesLength
);
89 for (int i
= 0; i
< disk_cache::Stats::MAX_COUNTER
; i
++)
90 printf("Count(%d): %" PRId64
"\n", i
, *counters
++);
91 printf("-------------------------\n\n");
94 // Dumps the contents of the Index-file header.
95 void DumpIndexHeader(const base::FilePath
& name
,
96 disk_cache::CacheAddr
* stats_addr
) {
97 disk_cache::IndexHeader header
;
98 if (!ReadHeader(name
, reinterpret_cast<char*>(&header
), sizeof(header
)))
101 printf("Index file:\n");
102 printf("magic: %x\n", header
.magic
);
103 printf("version: %d.%d\n", header
.version
>> 16, header
.version
& 0xffff);
104 printf("entries: %d\n", header
.num_entries
);
105 printf("total bytes: %d\n", header
.num_bytes
);
106 printf("last file number: %d\n", header
.last_file
);
107 printf("current id: %d\n", header
.this_id
);
108 printf("table length: %d\n", header
.table_len
);
109 printf("last crash: %d\n", header
.crash
);
110 printf("experiment: %d\n", header
.experiment
);
111 printf("stats: %x\n", header
.stats
);
112 for (int i
= 0; i
< 5; i
++) {
113 printf("head %d: 0x%x\n", i
, header
.lru
.heads
[i
]);
114 printf("tail %d: 0x%x\n", i
, header
.lru
.tails
[i
]);
115 printf("size %d: 0x%x\n", i
, header
.lru
.sizes
[i
]);
117 printf("transaction: 0x%x\n", header
.lru
.transaction
);
118 printf("operation: %d\n", header
.lru
.operation
);
119 printf("operation list: %d\n", header
.lru
.operation_list
);
120 printf("-------------------------\n\n");
122 *stats_addr
= header
.stats
;
125 // Dumps the contents of a block-file header.
126 void DumpBlockHeader(const base::FilePath
& name
) {
127 disk_cache::BlockFileHeader header
;
128 if (!ReadHeader(name
, reinterpret_cast<char*>(&header
), sizeof(header
)))
131 printf("Block file: %s\n", name
.BaseName().MaybeAsASCII().c_str());
132 printf("magic: %x\n", header
.magic
);
133 printf("version: %d.%d\n", header
.version
>> 16, header
.version
& 0xffff);
134 printf("file id: %d\n", header
.this_file
);
135 printf("next file id: %d\n", header
.next_file
);
136 printf("entry size: %d\n", header
.entry_size
);
137 printf("current entries: %d\n", header
.num_entries
);
138 printf("max entries: %d\n", header
.max_entries
);
139 printf("updating: %d\n", header
.updating
);
140 printf("empty sz 1: %d\n", header
.empty
[0]);
141 printf("empty sz 2: %d\n", header
.empty
[1]);
142 printf("empty sz 3: %d\n", header
.empty
[2]);
143 printf("empty sz 4: %d\n", header
.empty
[3]);
144 printf("user 0: 0x%x\n", header
.user
[0]);
145 printf("user 1: 0x%x\n", header
.user
[1]);
146 printf("user 2: 0x%x\n", header
.user
[2]);
147 printf("user 3: 0x%x\n", header
.user
[3]);
148 printf("-------------------------\n\n");
151 // Simple class that interacts with the set of cache files.
154 explicit CacheDumper(const base::FilePath
& path
)
164 // Reads an entry from disk. Return false when all entries have been already
166 bool GetEntry(disk_cache::EntryStore
* entry
);
168 // Loads a specific block from the block files.
169 bool LoadEntry(disk_cache::CacheAddr addr
, disk_cache::EntryStore
* entry
);
170 bool LoadRankings(disk_cache::CacheAddr addr
,
171 disk_cache::RankingsNode
* rankings
);
174 base::FilePath path_
;
175 disk_cache::BlockFiles block_files_
;
176 scoped_refptr
<disk_cache::MappedFile
> index_file_
;
177 disk_cache::Index
* index_
;
179 disk_cache::CacheAddr next_addr_
;
180 std::set
<disk_cache::CacheAddr
> dumped_entries_
;
181 DISALLOW_COPY_AND_ASSIGN(CacheDumper
);
184 bool CacheDumper::Init() {
185 if (!block_files_
.Init(false)) {
186 printf("Unable to init block files\n");
190 base::FilePath
index_name(path_
.Append(kIndexName
));
191 index_file_
= new disk_cache::MappedFile
;
192 index_
= reinterpret_cast<disk_cache::Index
*>(
193 index_file_
->Init(index_name
, 0));
195 printf("Unable to map index\n");
202 bool CacheDumper::GetEntry(disk_cache::EntryStore
* entry
) {
203 if (dumped_entries_
.find(next_addr_
) != dumped_entries_
.end()) {
204 printf("Loop detected\n");
210 if (LoadEntry(next_addr_
, entry
))
213 printf("Unable to load entry at address 0x%x\n", next_addr_
);
218 for (int i
= current_hash_
; i
< index_
->header
.table_len
; i
++) {
219 // Yes, we'll crash if the table is shorter than expected, but only after
220 // dumping every entry that we can find.
221 if (index_
->table
[i
]) {
223 if (LoadEntry(index_
->table
[i
], entry
))
226 printf("Unable to load entry at address 0x%x\n", index_
->table
[i
]);
232 bool CacheDumper::LoadEntry(disk_cache::CacheAddr addr
,
233 disk_cache::EntryStore
* entry
) {
234 disk_cache::Addr
address(addr
);
235 disk_cache::MappedFile
* file
= block_files_
.GetFile(address
);
239 disk_cache::StorageBlock
<disk_cache::EntryStore
> entry_block(file
, address
);
240 if (!entry_block
.Load())
243 memcpy(entry
, entry_block
.Data(), sizeof(*entry
));
244 printf("Entry at 0x%x\n", addr
);
246 // Prepare for the next entry to load.
247 next_addr_
= entry
->next
;
249 dumped_entries_
.insert(addr
);
252 dumped_entries_
.clear();
257 bool CacheDumper::LoadRankings(disk_cache::CacheAddr addr
,
258 disk_cache::RankingsNode
* rankings
) {
259 disk_cache::Addr
address(addr
);
260 disk_cache::MappedFile
* file
= block_files_
.GetFile(address
);
264 disk_cache::StorageBlock
<disk_cache::RankingsNode
> rank_block(file
, address
);
265 if (!rank_block
.Load())
268 memcpy(rankings
, rank_block
.Data(), sizeof(*rankings
));
269 printf("Rankings at 0x%x\n", addr
);
273 void DumpEntry(const disk_cache::EntryStore
& entry
) {
275 if (!entry
.long_key
) {
281 printf("hash: 0x%x\n", entry
.hash
);
282 printf("next entry: 0x%x\n", entry
.next
);
283 printf("rankings: 0x%x\n", entry
.rankings_node
);
284 printf("key length: %d\n", entry
.key_len
);
285 printf("key: \"%s\"\n", key
.c_str());
286 printf("key addr: 0x%x\n", entry
.long_key
);
287 printf("reuse count: %d\n", entry
.reuse_count
);
288 printf("refetch count: %d\n", entry
.refetch_count
);
289 printf("state: %d\n", entry
.state
);
290 for (int i
= 0; i
< 4; i
++) {
291 printf("data size %d: %d\n", i
, entry
.data_size
[i
]);
292 printf("data addr %d: 0x%x\n", i
, entry
.data_addr
[i
]);
294 printf("----------\n\n");
297 void DumpRankings(const disk_cache::RankingsNode
& rankings
) {
298 printf("next: 0x%x\n", rankings
.next
);
299 printf("prev: 0x%x\n", rankings
.prev
);
300 printf("entry: 0x%x\n", rankings
.contents
);
301 printf("dirty: %d\n", rankings
.dirty
);
302 printf("hash: 0x%x\n", rankings
.self_hash
);
303 printf("----------\n\n");
308 // -----------------------------------------------------------------------
310 int GetMajorVersion(const base::FilePath
& input_path
) {
311 base::FilePath
index_name(input_path
.Append(kIndexName
));
313 int version
= GetMajorVersionFromFile(index_name
);
317 base::FilePath
data_name(input_path
.Append(FILE_PATH_LITERAL("data_0")));
318 if (version
!= GetMajorVersionFromFile(data_name
))
321 data_name
= input_path
.Append(FILE_PATH_LITERAL("data_1"));
322 if (version
!= GetMajorVersionFromFile(data_name
))
325 data_name
= input_path
.Append(FILE_PATH_LITERAL("data_2"));
326 if (version
!= GetMajorVersionFromFile(data_name
))
329 data_name
= input_path
.Append(FILE_PATH_LITERAL("data_3"));
330 if (version
!= GetMajorVersionFromFile(data_name
))
336 // Dumps the headers of all files.
337 int DumpHeaders(const base::FilePath
& input_path
) {
338 base::FilePath
index_name(input_path
.Append(kIndexName
));
339 disk_cache::CacheAddr stats_addr
= 0;
340 DumpIndexHeader(index_name
, &stats_addr
);
342 base::FileEnumerator
iter(input_path
, false,
343 base::FileEnumerator::FILES
,
344 FILE_PATH_LITERAL("data_*"));
345 for (base::FilePath file
= iter
.Next(); !file
.empty(); file
= iter
.Next())
346 DumpBlockHeader(file
);
348 DumpStats(input_path
, stats_addr
);
352 // Dumps all entries from the cache.
353 int DumpContents(const base::FilePath
& input_path
) {
354 DumpHeaders(input_path
);
356 // We need a message loop, although we really don't run any task.
357 base::MessageLoopForIO loop
;
358 CacheDumper
dumper(input_path
);
362 disk_cache::EntryStore entry
;
363 while (dumper
.GetEntry(&entry
)) {
365 disk_cache::RankingsNode rankings
;
366 if (dumper
.LoadRankings(entry
.rankings_node
, &rankings
))
367 DumpRankings(rankings
);