Home | History | Annotate | Download | only in dump_cache
      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 }
    374