Clear webapp storage when site data is cleared
[chromium-blink-merge.git] / net / tools / dump_cache / dump_files.cc
blob8c7434e971f0619bd6dcb74b5cfa8593f2710f66
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
7 // files).
9 #include "net/tools/dump_cache/dump_files.h"
11 #include <stdio.h>
13 #include <set>
14 #include <string>
16 #include "base/command_line.h"
17 #include "base/files/file.h"
18 #include "base/files/file_enumerator.h"
19 #include "base/files/file_util.h"
20 #include "base/format_macros.h"
21 #include "base/message_loop/message_loop.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/strings/stringprintf.h"
24 #include "net/disk_cache/blockfile/block_files.h"
25 #include "net/disk_cache/blockfile/disk_format.h"
26 #include "net/disk_cache/blockfile/mapped_file.h"
27 #include "net/disk_cache/blockfile/stats.h"
28 #include "net/disk_cache/blockfile/storage_block-inl.h"
29 #include "net/disk_cache/blockfile/storage_block.h"
30 #include "net/url_request/view_cache_helper.h"
32 namespace {
34 const base::FilePath::CharType kIndexName[] = FILE_PATH_LITERAL("index");
36 // Reads the |header_size| bytes from the beginning of file |name|.
37 bool ReadHeader(const base::FilePath& name, char* header, int header_size) {
38 base::File file(name, base::File::FLAG_OPEN | base::File::FLAG_READ);
39 if (!file.IsValid()) {
40 printf("Unable to open file %s\n", name.MaybeAsASCII().c_str());
41 return false;
44 int read = file.Read(0, header, header_size);
45 if (read != header_size) {
46 printf("Unable to read file %s\n", name.MaybeAsASCII().c_str());
47 return false;
49 return true;
52 int GetMajorVersionFromFile(const base::FilePath& name) {
53 disk_cache::IndexHeader header;
54 if (!ReadHeader(name, reinterpret_cast<char*>(&header), sizeof(header)))
55 return 0;
57 return header.version >> 16;
60 // Dumps the contents of the Stats record.
61 void DumpStats(const base::FilePath& path, disk_cache::CacheAddr addr) {
62 // We need a message loop, although we really don't run any task.
63 base::MessageLoopForIO loop;
65 disk_cache::BlockFiles block_files(path);
66 if (!block_files.Init(false)) {
67 printf("Unable to init block files\n");
68 return;
71 disk_cache::Addr address(addr);
72 disk_cache::MappedFile* file = block_files.GetFile(address);
73 if (!file)
74 return;
76 size_t length = (2 + disk_cache::Stats::kDataSizesLength) * sizeof(int32) +
77 disk_cache::Stats::MAX_COUNTER * sizeof(int64);
79 size_t offset = address.start_block() * address.BlockSize() +
80 disk_cache::kBlockHeaderSize;
82 scoped_ptr<int32[]> buffer(new int32[length]);
83 if (!file->Read(buffer.get(), length, offset))
84 return;
86 printf("Stats:\nSignatrure: 0x%x\n", buffer[0]);
87 printf("Total size: %d\n", buffer[1]);
88 for (int i = 0; i < disk_cache::Stats::kDataSizesLength; i++)
89 printf("Size(%d): %d\n", i, buffer[i + 2]);
91 int64* counters = reinterpret_cast<int64*>(
92 buffer.get() + 2 + disk_cache::Stats::kDataSizesLength);
93 for (int i = 0; i < disk_cache::Stats::MAX_COUNTER; i++)
94 printf("Count(%d): %" PRId64 "\n", i, *counters++);
95 printf("-------------------------\n\n");
98 // Dumps the contents of the Index-file header.
99 void DumpIndexHeader(const base::FilePath& name,
100 disk_cache::CacheAddr* stats_addr) {
101 disk_cache::IndexHeader header;
102 if (!ReadHeader(name, reinterpret_cast<char*>(&header), sizeof(header)))
103 return;
105 printf("Index file:\n");
106 printf("magic: %x\n", header.magic);
107 printf("version: %d.%d\n", header.version >> 16, header.version & 0xffff);
108 printf("entries: %d\n", header.num_entries);
109 printf("total bytes: %d\n", header.num_bytes);
110 printf("last file number: %d\n", header.last_file);
111 printf("current id: %d\n", header.this_id);
112 printf("table length: %d\n", header.table_len);
113 printf("last crash: %d\n", header.crash);
114 printf("experiment: %d\n", header.experiment);
115 printf("stats: %x\n", header.stats);
116 for (int i = 0; i < 5; i++) {
117 printf("head %d: 0x%x\n", i, header.lru.heads[i]);
118 printf("tail %d: 0x%x\n", i, header.lru.tails[i]);
119 printf("size %d: 0x%x\n", i, header.lru.sizes[i]);
121 printf("transaction: 0x%x\n", header.lru.transaction);
122 printf("operation: %d\n", header.lru.operation);
123 printf("operation list: %d\n", header.lru.operation_list);
124 printf("-------------------------\n\n");
126 if (stats_addr)
127 *stats_addr = header.stats;
130 // Dumps the contents of a block-file header.
131 void DumpBlockHeader(const base::FilePath& name) {
132 disk_cache::BlockFileHeader header;
133 if (!ReadHeader(name, reinterpret_cast<char*>(&header), sizeof(header)))
134 return;
136 printf("Block file: %s\n", name.BaseName().MaybeAsASCII().c_str());
137 printf("magic: %x\n", header.magic);
138 printf("version: %d.%d\n", header.version >> 16, header.version & 0xffff);
139 printf("file id: %d\n", header.this_file);
140 printf("next file id: %d\n", header.next_file);
141 printf("entry size: %d\n", header.entry_size);
142 printf("current entries: %d\n", header.num_entries);
143 printf("max entries: %d\n", header.max_entries);
144 printf("updating: %d\n", header.updating);
145 printf("empty sz 1: %d\n", header.empty[0]);
146 printf("empty sz 2: %d\n", header.empty[1]);
147 printf("empty sz 3: %d\n", header.empty[2]);
148 printf("empty sz 4: %d\n", header.empty[3]);
149 printf("user 0: 0x%x\n", header.user[0]);
150 printf("user 1: 0x%x\n", header.user[1]);
151 printf("user 2: 0x%x\n", header.user[2]);
152 printf("user 3: 0x%x\n", header.user[3]);
153 printf("-------------------------\n\n");
156 // Simple class that interacts with the set of cache files.
157 class CacheDumper {
158 public:
159 explicit CacheDumper(const base::FilePath& path)
160 : path_(path),
161 block_files_(path),
162 index_(NULL),
163 current_hash_(0),
164 next_addr_(0) {
167 bool Init();
169 // Reads an entry from disk. Return false when all entries have been already
170 // returned.
171 bool GetEntry(disk_cache::EntryStore* entry, disk_cache::CacheAddr* addr);
173 // Loads a specific block from the block files.
174 bool LoadEntry(disk_cache::CacheAddr addr, disk_cache::EntryStore* entry);
175 bool LoadRankings(disk_cache::CacheAddr addr,
176 disk_cache::RankingsNode* rankings);
178 // Appends the data store at |addr| to |out|.
179 bool HexDump(disk_cache::CacheAddr addr, std::string* out);
181 private:
182 base::FilePath path_;
183 disk_cache::BlockFiles block_files_;
184 scoped_refptr<disk_cache::MappedFile> index_file_;
185 disk_cache::Index* index_;
186 int current_hash_;
187 disk_cache::CacheAddr next_addr_;
188 std::set<disk_cache::CacheAddr> dumped_entries_;
189 DISALLOW_COPY_AND_ASSIGN(CacheDumper);
192 bool CacheDumper::Init() {
193 if (!block_files_.Init(false)) {
194 printf("Unable to init block files\n");
195 return false;
198 base::FilePath index_name(path_.Append(kIndexName));
199 index_file_ = new disk_cache::MappedFile;
200 index_ = reinterpret_cast<disk_cache::Index*>(
201 index_file_->Init(index_name, 0));
202 if (!index_) {
203 printf("Unable to map index\n");
204 return false;
207 return true;
210 bool CacheDumper::GetEntry(disk_cache::EntryStore* entry,
211 disk_cache::CacheAddr* addr) {
212 if (dumped_entries_.find(next_addr_) != dumped_entries_.end()) {
213 printf("Loop detected\n");
214 next_addr_ = 0;
215 current_hash_++;
218 if (next_addr_) {
219 *addr = next_addr_;
220 if (LoadEntry(next_addr_, entry))
221 return true;
223 printf("Unable to load entry at address 0x%x\n", next_addr_);
224 next_addr_ = 0;
225 current_hash_++;
228 for (int i = current_hash_; i < index_->header.table_len; i++) {
229 // Yes, we'll crash if the table is shorter than expected, but only after
230 // dumping every entry that we can find.
231 if (index_->table[i]) {
232 current_hash_ = i;
233 *addr = index_->table[i];
234 if (LoadEntry(index_->table[i], entry))
235 return true;
237 printf("Unable to load entry at address 0x%x\n", index_->table[i]);
240 return false;
243 bool CacheDumper::LoadEntry(disk_cache::CacheAddr addr,
244 disk_cache::EntryStore* entry) {
245 disk_cache::Addr address(addr);
246 disk_cache::MappedFile* file = block_files_.GetFile(address);
247 if (!file)
248 return false;
250 disk_cache::StorageBlock<disk_cache::EntryStore> entry_block(file, address);
251 if (!entry_block.Load())
252 return false;
254 memcpy(entry, entry_block.Data(), sizeof(*entry));
255 if (!entry_block.VerifyHash())
256 printf("Self hash failed at 0x%x\n", addr);
258 // Prepare for the next entry to load.
259 next_addr_ = entry->next;
260 if (next_addr_) {
261 dumped_entries_.insert(addr);
262 } else {
263 current_hash_++;
264 dumped_entries_.clear();
266 return true;
269 bool CacheDumper::LoadRankings(disk_cache::CacheAddr addr,
270 disk_cache::RankingsNode* rankings) {
271 disk_cache::Addr address(addr);
272 if (address.file_type() != disk_cache::RANKINGS)
273 return false;
275 disk_cache::MappedFile* file = block_files_.GetFile(address);
276 if (!file)
277 return false;
279 disk_cache::StorageBlock<disk_cache::RankingsNode> rank_block(file, address);
280 if (!rank_block.Load())
281 return false;
283 if (!rank_block.VerifyHash())
284 printf("Self hash failed at 0x%x\n", addr);
286 memcpy(rankings, rank_block.Data(), sizeof(*rankings));
287 return true;
290 bool CacheDumper::HexDump(disk_cache::CacheAddr addr, std::string* out) {
291 disk_cache::Addr address(addr);
292 disk_cache::MappedFile* file = block_files_.GetFile(address);
293 if (!file)
294 return false;
296 size_t size = address.num_blocks() * address.BlockSize();
297 scoped_ptr<char> buffer(new char[size]);
299 size_t offset = address.start_block() * address.BlockSize() +
300 disk_cache::kBlockHeaderSize;
301 if (!file->Read(buffer.get(), size, offset))
302 return false;
304 base::StringAppendF(out, "0x%x:\n", addr);
305 net::ViewCacheHelper::HexDump(buffer.get(), size, out);
306 return true;
309 std::string ToLocalTime(int64 time_us) {
310 base::Time time = base::Time::FromInternalValue(time_us);
311 base::Time::Exploded e;
312 time.LocalExplode(&e);
313 return base::StringPrintf("%d/%d/%d %d:%d:%d.%d", e.year, e.month,
314 e.day_of_month, e.hour, e.minute, e.second,
315 e.millisecond);
318 void DumpEntry(disk_cache::CacheAddr addr,
319 const disk_cache::EntryStore& entry,
320 bool verbose) {
321 std::string key;
322 static bool full_key =
323 base::CommandLine::ForCurrentProcess()->HasSwitch("full-key");
324 if (!entry.long_key) {
325 key = std::string(entry.key, std::min(static_cast<size_t>(entry.key_len),
326 sizeof(entry.key)));
327 if (entry.key_len > 90 && !full_key)
328 key.resize(90);
331 printf("Entry at 0x%x\n", addr);
332 printf("rankings: 0x%x\n", entry.rankings_node);
333 printf("key length: %d\n", entry.key_len);
334 printf("key: \"%s\"\n", key.c_str());
336 if (verbose) {
337 printf("key addr: 0x%x\n", entry.long_key);
338 printf("hash: 0x%x\n", entry.hash);
339 printf("next entry: 0x%x\n", entry.next);
340 printf("reuse count: %d\n", entry.reuse_count);
341 printf("refetch count: %d\n", entry.refetch_count);
342 printf("state: %d\n", entry.state);
343 printf("creation: %s\n", ToLocalTime(entry.creation_time).c_str());
344 for (int i = 0; i < 4; i++) {
345 printf("data size %d: %d\n", i, entry.data_size[i]);
346 printf("data addr %d: 0x%x\n", i, entry.data_addr[i]);
348 printf("----------\n\n");
352 void DumpRankings(disk_cache::CacheAddr addr,
353 const disk_cache::RankingsNode& rankings,
354 bool verbose) {
355 printf("Rankings at 0x%x\n", addr);
356 printf("next: 0x%x\n", rankings.next);
357 printf("prev: 0x%x\n", rankings.prev);
358 printf("entry: 0x%x\n", rankings.contents);
360 if (verbose) {
361 printf("dirty: %d\n", rankings.dirty);
362 if (rankings.last_used != rankings.last_modified)
363 printf("used: %s\n", ToLocalTime(rankings.last_used).c_str());
364 printf("modified: %s\n", ToLocalTime(rankings.last_modified).c_str());
365 printf("hash: 0x%x\n", rankings.self_hash);
366 printf("----------\n\n");
367 } else {
368 printf("\n");
372 void PrintCSVHeader() {
373 printf(
374 "entry,rankings,next,prev,rank-contents,chain,reuse,key,"
375 "d0,d1,d2,d3\n");
378 void DumpCSV(disk_cache::CacheAddr addr,
379 const disk_cache::EntryStore& entry,
380 const disk_cache::RankingsNode& rankings) {
381 printf("0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x\n", addr,
382 entry.rankings_node, rankings.next, rankings.prev, rankings.contents,
383 entry.next, entry.reuse_count, entry.long_key, entry.data_addr[0],
384 entry.data_addr[1], entry.data_addr[2], entry.data_addr[3]);
386 if (addr != rankings.contents)
387 printf("Broken entry\n");
390 bool CanDump(disk_cache::CacheAddr addr) {
391 disk_cache::Addr address(addr);
392 return address.is_initialized() && address.is_block_file();
395 } // namespace.
397 // -----------------------------------------------------------------------
399 int GetMajorVersion(const base::FilePath& input_path) {
400 base::FilePath index_name(input_path.Append(kIndexName));
402 int version = GetMajorVersionFromFile(index_name);
403 if (!version)
404 return 0;
406 base::FilePath data_name(input_path.Append(FILE_PATH_LITERAL("data_0")));
407 if (version != GetMajorVersionFromFile(data_name))
408 return 0;
410 data_name = input_path.Append(FILE_PATH_LITERAL("data_1"));
411 if (version != GetMajorVersionFromFile(data_name))
412 return 0;
414 data_name = input_path.Append(FILE_PATH_LITERAL("data_2"));
415 if (version != GetMajorVersionFromFile(data_name))
416 return 0;
418 data_name = input_path.Append(FILE_PATH_LITERAL("data_3"));
419 if (version != GetMajorVersionFromFile(data_name))
420 return 0;
422 return version;
425 // Dumps the headers of all files.
426 int DumpHeaders(const base::FilePath& input_path) {
427 base::FilePath index_name(input_path.Append(kIndexName));
428 disk_cache::CacheAddr stats_addr = 0;
429 DumpIndexHeader(index_name, &stats_addr);
431 base::FileEnumerator iter(input_path, false,
432 base::FileEnumerator::FILES,
433 FILE_PATH_LITERAL("data_*"));
434 for (base::FilePath file = iter.Next(); !file.empty(); file = iter.Next())
435 DumpBlockHeader(file);
437 DumpStats(input_path, stats_addr);
438 return 0;
441 // Dumps all entries from the cache.
442 int DumpContents(const base::FilePath& input_path) {
443 bool print_csv = base::CommandLine::ForCurrentProcess()->HasSwitch("csv");
444 if (!print_csv)
445 DumpIndexHeader(input_path.Append(kIndexName), nullptr);
447 // We need a message loop, although we really don't run any task.
448 base::MessageLoopForIO loop;
449 CacheDumper dumper(input_path);
450 if (!dumper.Init())
451 return -1;
453 if (print_csv)
454 PrintCSVHeader();
456 disk_cache::EntryStore entry;
457 disk_cache::CacheAddr addr;
458 bool verbose = base::CommandLine::ForCurrentProcess()->HasSwitch("v");
459 while (dumper.GetEntry(&entry, &addr)) {
460 if (!print_csv)
461 DumpEntry(addr, entry, verbose);
462 disk_cache::RankingsNode rankings;
463 if (!dumper.LoadRankings(entry.rankings_node, &rankings))
464 continue;
466 if (print_csv)
467 DumpCSV(addr, entry, rankings);
468 else
469 DumpRankings(entry.rankings_node, rankings, verbose);
472 printf("Done.\n");
474 return 0;
477 int DumpLists(const base::FilePath& input_path) {
478 base::FilePath index_name(input_path.Append(kIndexName));
479 disk_cache::IndexHeader header;
480 if (!ReadHeader(index_name, reinterpret_cast<char*>(&header), sizeof(header)))
481 return -1;
483 // We need a message loop, although we really don't run any task.
484 base::MessageLoopForIO loop;
485 CacheDumper dumper(input_path);
486 if (!dumper.Init())
487 return -1;
489 printf("list, addr, next, prev, entry\n");
491 const int kMaxLength = 1 * 1000 * 1000;
492 for (int i = 0; i < 5; i++) {
493 int32 size = header.lru.sizes[i];
494 if (size < 0 || size > kMaxLength) {
495 printf("Wrong size %d\n", size);
496 size = kMaxLength;
499 disk_cache::CacheAddr addr = header.lru.tails[i];
500 int count = 0;
501 for (; size && addr; size--) {
502 count++;
503 disk_cache::RankingsNode rankings;
504 if (!dumper.LoadRankings(addr, &rankings)) {
505 printf("Failed to load node at 0x%x\n", addr);
506 break;
508 printf("%d, 0x%x, 0x%x, 0x%x, 0x%x\n", i, addr, rankings.next,
509 rankings.prev, rankings.contents);
511 if (rankings.prev == addr)
512 break;
514 addr = rankings.prev;
516 printf("%d nodes found, %d reported\n", count, header.lru.sizes[i]);
519 printf("Done.\n");
520 return 0;
523 int DumpEntryAt(const base::FilePath& input_path, const std::string& at) {
524 disk_cache::CacheAddr addr;
525 if (!base::HexStringToUInt(at, &addr))
526 return -1;
528 if (!CanDump(addr))
529 return -1;
531 base::FilePath index_name(input_path.Append(kIndexName));
532 disk_cache::IndexHeader header;
533 if (!ReadHeader(index_name, reinterpret_cast<char*>(&header), sizeof(header)))
534 return -1;
536 // We need a message loop, although we really don't run any task.
537 base::MessageLoopForIO loop;
538 CacheDumper dumper(input_path);
539 if (!dumper.Init())
540 return -1;
542 disk_cache::CacheAddr entry_addr = 0;
543 disk_cache::CacheAddr rankings_addr = 0;
544 disk_cache::Addr address(addr);
546 disk_cache::RankingsNode rankings;
547 if (address.file_type() == disk_cache::RANKINGS) {
548 if (dumper.LoadRankings(addr, &rankings)) {
549 rankings_addr = addr;
550 addr = rankings.contents;
551 address = disk_cache::Addr(addr);
555 disk_cache::EntryStore entry = {};
556 if (address.file_type() == disk_cache::BLOCK_256 &&
557 dumper.LoadEntry(addr, &entry)) {
558 entry_addr = addr;
559 DumpEntry(addr, entry, true);
560 if (!rankings_addr && dumper.LoadRankings(entry.rankings_node, &rankings))
561 rankings_addr = entry.rankings_node;
564 bool verbose = base::CommandLine::ForCurrentProcess()->HasSwitch("v");
566 std::string hex_dump;
567 if (!rankings_addr || verbose)
568 dumper.HexDump(addr, &hex_dump);
570 if (rankings_addr)
571 DumpRankings(rankings_addr, rankings, true);
573 if (entry_addr && verbose) {
574 if (entry.long_key && CanDump(entry.long_key))
575 dumper.HexDump(entry.long_key, &hex_dump);
577 for (int i = 0; i < 4; i++) {
578 if (entry.data_addr[i] && CanDump(entry.data_addr[i]))
579 dumper.HexDump(entry.data_addr[i], &hex_dump);
583 printf("%s\n", hex_dump.c_str());
584 printf("Done.\n");
585 return 0;
588 int DumpAllocation(const base::FilePath& file) {
589 disk_cache::BlockFileHeader header;
590 if (!ReadHeader(file, reinterpret_cast<char*>(&header), sizeof(header)))
591 return -1;
593 std::string out;
594 net::ViewCacheHelper::HexDump(reinterpret_cast<char*>(&header.allocation_map),
595 sizeof(header.allocation_map), &out);
596 printf("%s\n", out.c_str());
597 return 0;