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 // 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 | |
7 // files). | |
8 | |
9 #include "net/tools/dump_cache/dump_files.h" | |
10 | |
11 #include <stdio.h> | |
12 | |
13 #include <set> | |
14 #include <string> | |
15 | |
16 #include "base/files/file.h" | |
17 #include "base/files/file_enumerator.h" | |
18 #include "base/files/file_util.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" | |
27 | |
28 namespace { | |
29 | |
30 const base::FilePath::CharType kIndexName[] = FILE_PATH_LITERAL("index"); | |
31 | |
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()); | |
37 return false; | |
38 } | |
39 | |
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()); | |
43 return false; | |
44 } | |
45 return true; | |
46 } | |
47 | |
48 int GetMajorVersionFromFile(const base::FilePath& name) { | |
49 disk_cache::IndexHeader header; | |
50 if (!ReadHeader(name, reinterpret_cast<char*>(&header), sizeof(header))) | |
51 return 0; | |
52 | |
53 return header.version >> 16; | |
54 } | |
55 | |
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; | |
60 | |
61 disk_cache::BlockFiles block_files(path); | |
62 if (!block_files.Init(false)) { | |
63 printf("Unable to init block files\n"); | |
64 return; | |
65 } | |
66 | |
67 disk_cache::Addr address(addr); | |
68 disk_cache::MappedFile* file = block_files.GetFile(address); | |
69 if (!file) | |
70 return; | |
71 | |
72 size_t length = (2 + disk_cache::Stats::kDataSizesLength) * sizeof(int32) + | |
73 disk_cache::Stats::MAX_COUNTER * sizeof(int64); | |
74 | |
75 size_t offset = address.start_block() * address.BlockSize() + | |
76 disk_cache::kBlockHeaderSize; | |
77 | |
78 scoped_ptr<int32[]> buffer(new int32[length]); | |
79 if (!file->Read(buffer.get(), length, offset)) | |
80 return; | |
81 | |
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]); | |
86 | |
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"); | |
92 } | |
93 | |
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))) | |
99 return; | |
100 | |
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]); | |
116 } | |
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"); | |
121 | |
122 *stats_addr = header.stats; | |
123 } | |
124 | |
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))) | |
129 return; | |
130 | |
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"); | |
149 } | |
150 | |
151 // Simple class that interacts with the set of cache files. | |
152 class CacheDumper { | |
153 public: | |
154 explicit CacheDumper(const base::FilePath& path) | |
155 : path_(path), | |
156 block_files_(path), | |
157 index_(NULL), | |
158 current_hash_(0), | |
159 next_addr_(0) { | |
160 } | |
161 | |
162 bool Init(); | |
163 | |
164 // Reads an entry from disk. Return false when all entries have been already | |
165 // returned. | |
166 bool GetEntry(disk_cache::EntryStore* entry); | |
167 | |
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); | |
172 | |
173 private: | |
174 base::FilePath path_; | |
175 disk_cache::BlockFiles block_files_; | |
176 scoped_refptr<disk_cache::MappedFile> index_file_; | |
177 disk_cache::Index* index_; | |
178 int current_hash_; | |
179 disk_cache::CacheAddr next_addr_; | |
180 std::set<disk_cache::CacheAddr> dumped_entries_; | |
181 DISALLOW_COPY_AND_ASSIGN(CacheDumper); | |
182 }; | |
183 | |
184 bool CacheDumper::Init() { | |
185 if (!block_files_.Init(false)) { | |
186 printf("Unable to init block files\n"); | |
187 return false; | |
188 } | |
189 | |
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)); | |
194 if (!index_) { | |
195 printf("Unable to map index\n"); | |
196 return false; | |
197 } | |
198 | |
199 return true; | |
200 } | |
201 | |
202 bool CacheDumper::GetEntry(disk_cache::EntryStore* entry) { | |
203 if (dumped_entries_.find(next_addr_) != dumped_entries_.end()) { | |
204 printf("Loop detected\n"); | |
205 next_addr_ = 0; | |
206 current_hash_++; | |
207 } | |
208 | |
209 if (next_addr_) { | |
210 if (LoadEntry(next_addr_, entry)) | |
211 return true; | |
212 | |
213 printf("Unable to load entry at address 0x%x\n", next_addr_); | |
214 next_addr_ = 0; | |
215 current_hash_++; | |
216 } | |
217 | |
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]) { | |
222 current_hash_ = i; | |
223 if (LoadEntry(index_->table[i], entry)) | |
224 return true; | |
225 | |
226 printf("Unable to load entry at address 0x%x\n", index_->table[i]); | |
227 } | |
228 } | |
229 return false; | |
230 } | |
231 | |
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); | |
236 if (!file) | |
237 return false; | |
238 | |
239 disk_cache::StorageBlock<disk_cache::EntryStore> entry_block(file, address); | |
240 if (!entry_block.Load()) | |
241 return false; | |
242 | |
243 memcpy(entry, entry_block.Data(), sizeof(*entry)); | |
244 printf("Entry at 0x%x\n", addr); | |
245 | |
246 // Prepare for the next entry to load. | |
247 next_addr_ = entry->next; | |
248 if (next_addr_) { | |
249 dumped_entries_.insert(addr); | |
250 } else { | |
251 current_hash_++; | |
252 dumped_entries_.clear(); | |
253 } | |
254 return true; | |
255 } | |
256 | |
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); | |
261 if (!file) | |
262 return false; | |
263 | |
264 disk_cache::StorageBlock<disk_cache::RankingsNode> rank_block(file, address); | |
265 if (!rank_block.Load()) | |
266 return false; | |
267 | |
268 memcpy(rankings, rank_block.Data(), sizeof(*rankings)); | |
269 printf("Rankings at 0x%x\n", addr); | |
270 return true; | |
271 } | |
272 | |
273 void DumpEntry(const disk_cache::EntryStore& entry) { | |
274 std::string key; | |
275 if (!entry.long_key) { | |
276 key = entry.key; | |
277 if (key.size() > 50) | |
278 key.resize(50); | |
279 } | |
280 | |
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]); | |
293 } | |
294 printf("----------\n\n"); | |
295 } | |
296 | |
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"); | |
304 } | |
305 | |
306 } // namespace. | |
307 | |
308 // ----------------------------------------------------------------------- | |
309 | |
310 int GetMajorVersion(const base::FilePath& input_path) { | |
311 base::FilePath index_name(input_path.Append(kIndexName)); | |
312 | |
313 int version = GetMajorVersionFromFile(index_name); | |
314 if (!version) | |
315 return 0; | |
316 | |
317 base::FilePath data_name(input_path.Append(FILE_PATH_LITERAL("data_0"))); | |
318 if (version != GetMajorVersionFromFile(data_name)) | |
319 return 0; | |
320 | |
321 data_name = input_path.Append(FILE_PATH_LITERAL("data_1")); | |
322 if (version != GetMajorVersionFromFile(data_name)) | |
323 return 0; | |
324 | |
325 data_name = input_path.Append(FILE_PATH_LITERAL("data_2")); | |
326 if (version != GetMajorVersionFromFile(data_name)) | |
327 return 0; | |
328 | |
329 data_name = input_path.Append(FILE_PATH_LITERAL("data_3")); | |
330 if (version != GetMajorVersionFromFile(data_name)) | |
331 return 0; | |
332 | |
333 return version; | |
334 } | |
335 | |
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); | |
341 | |
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); | |
347 | |
348 DumpStats(input_path, stats_addr); | |
349 return 0; | |
350 } | |
351 | |
352 // Dumps all entries from the cache. | |
353 int DumpContents(const base::FilePath& input_path) { | |
354 DumpHeaders(input_path); | |
355 | |
356 // We need a message loop, although we really don't run any task. | |
357 base::MessageLoopForIO loop; | |
358 CacheDumper dumper(input_path); | |
359 if (!dumper.Init()) | |
360 return -1; | |
361 | |
362 disk_cache::EntryStore entry; | |
363 while (dumper.GetEntry(&entry)) { | |
364 DumpEntry(entry); | |
365 disk_cache::RankingsNode rankings; | |
366 if (dumper.LoadRankings(entry.rankings_node, &rankings)) | |
367 DumpRankings(rankings); | |
368 } | |
369 | |
370 printf("Done.\n"); | |
371 | |
372 return 0; | |
373 } | |
OLD | NEW |