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/file_util.h"
     17 #include "base/files/file_enumerator.h"
     18 #include "base/format_macros.h"
     19 #include "base/message_loop/message_loop.h"
     20 #include "net/base/file_stream.h"
     21 #include "net/disk_cache/block_files.h"
     22 #include "net/disk_cache/disk_format.h"
     23 #include "net/disk_cache/mapped_file.h"
     24 #include "net/disk_cache/stats.h"
     25 #include "net/disk_cache/storage_block-inl.h"
     26 #include "net/disk_cache/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   net::FileStream file(NULL);
     35   file.OpenSync(name, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ);
     36   if (!file.IsOpen()) {
     37     printf("Unable to open file %s\n", name.MaybeAsASCII().c_str());
     38     return false;
     39   }
     40 
     41   int read = file.ReadSync(header, header_size);
     42   if (read != header_size) {
     43     printf("Unable to read file %s\n", name.MaybeAsASCII().c_str());
     44     return false;
     45   }
     46   return true;
     47 }
     48 
     49 int GetMajorVersionFromFile(const base::FilePath& name) {
     50   disk_cache::IndexHeader header;
     51   if (!ReadHeader(name, reinterpret_cast<char*>(&header), sizeof(header)))
     52     return 0;
     53 
     54   return header.version >> 16;
     55 }
     56 
     57 // Dumps the contents of the Stats record.
     58 void DumpStats(const base::FilePath& path, disk_cache::CacheAddr addr) {
     59   // We need a message loop, although we really don't run any task.
     60   base::MessageLoop loop(base::MessageLoop::TYPE_IO);
     61 
     62   disk_cache::BlockFiles block_files(path);
     63   if (!block_files.Init(false)) {
     64     printf("Unable to init block files\n");
     65     return;
     66   }
     67 
     68   disk_cache::Addr address(addr);
     69   disk_cache::MappedFile* file = block_files.GetFile(address);
     70   if (!file)
     71     return;
     72 
     73   size_t length = (2 + disk_cache::Stats::kDataSizesLength) * sizeof(int32) +
     74                   disk_cache::Stats::MAX_COUNTER * sizeof(int64);
     75 
     76   size_t offset = address.start_block() * address.BlockSize() +
     77                   disk_cache::kBlockHeaderSize;
     78 
     79   scoped_ptr<int32[]> buffer(new int32[length]);
     80   if (!file->Read(buffer.get(), length, offset))
     81     return;
     82 
     83   printf("Stats:\nSignatrure: 0x%x\n", buffer[0]);
     84   printf("Total size: %d\n", buffer[1]);
     85   for (int i = 0; i < disk_cache::Stats::kDataSizesLength; i++)
     86     printf("Size(%d): %d\n", i, buffer[i + 2]);
     87 
     88   int64* counters = reinterpret_cast<int64*>(
     89                         buffer.get() + 2 + disk_cache::Stats::kDataSizesLength);
     90   for (int i = 0; i < disk_cache::Stats::MAX_COUNTER; i++)
     91     printf("Count(%d): %" PRId64 "\n", i, *counters++);
     92   printf("-------------------------\n\n");
     93 }
     94 
     95 // Dumps the contents of the Index-file header.
     96 void DumpIndexHeader(const base::FilePath& name,
     97                      disk_cache::CacheAddr* stats_addr) {
     98   disk_cache::IndexHeader header;
     99   if (!ReadHeader(name, reinterpret_cast<char*>(&header), sizeof(header)))
    100     return;
    101 
    102   printf("Index file:\n");
    103   printf("magic: %x\n", header.magic);
    104   printf("version: %d.%d\n", header.version >> 16, header.version & 0xffff);
    105   printf("entries: %d\n", header.num_entries);
    106   printf("total bytes: %d\n", header.num_bytes);
    107   printf("last file number: %d\n", header.last_file);
    108   printf("current id: %d\n", header.this_id);
    109   printf("table length: %d\n", header.table_len);
    110   printf("last crash: %d\n", header.crash);
    111   printf("experiment: %d\n", header.experiment);
    112   printf("stats: %x\n", header.stats);
    113   for (int i = 0; i < 5; i++) {
    114     printf("head %d: 0x%x\n", i, header.lru.heads[i]);
    115     printf("tail %d: 0x%x\n", i, header.lru.tails[i]);
    116     printf("size %d: 0x%x\n", i, header.lru.sizes[i]);
    117   }
    118   printf("transaction: 0x%x\n", header.lru.transaction);
    119   printf("operation: %d\n", header.lru.operation);
    120   printf("operation list: %d\n", header.lru.operation_list);
    121   printf("-------------------------\n\n");
    122 
    123   *stats_addr = header.stats;
    124 }
    125 
    126 // Dumps the contents of a block-file header.
    127 void DumpBlockHeader(const base::FilePath& name) {
    128   disk_cache::BlockFileHeader header;
    129   if (!ReadHeader(name, reinterpret_cast<char*>(&header), sizeof(header)))
    130     return;
    131 
    132   printf("Block file: %s\n", name.BaseName().MaybeAsASCII().c_str());
    133   printf("magic: %x\n", header.magic);
    134   printf("version: %d.%d\n", header.version >> 16, header.version & 0xffff);
    135   printf("file id: %d\n", header.this_file);
    136   printf("next file id: %d\n", header.next_file);
    137   printf("entry size: %d\n", header.entry_size);
    138   printf("current entries: %d\n", header.num_entries);
    139   printf("max entries: %d\n", header.max_entries);
    140   printf("updating: %d\n", header.updating);
    141   printf("empty sz 1: %d\n", header.empty[0]);
    142   printf("empty sz 2: %d\n", header.empty[1]);
    143   printf("empty sz 3: %d\n", header.empty[2]);
    144   printf("empty sz 4: %d\n", header.empty[3]);
    145   printf("user 0: 0x%x\n", header.user[0]);
    146   printf("user 1: 0x%x\n", header.user[1]);
    147   printf("user 2: 0x%x\n", header.user[2]);
    148   printf("user 3: 0x%x\n", header.user[3]);
    149   printf("-------------------------\n\n");
    150 }
    151 
    152 // Simple class that interacts with the set of cache files.
    153 class CacheDumper {
    154  public:
    155   explicit CacheDumper(const base::FilePath& path)
    156       : path_(path),
    157         block_files_(path),
    158         index_(NULL),
    159         current_hash_(0),
    160         next_addr_(0) {
    161   }
    162 
    163   bool Init();
    164 
    165   // Reads an entry from disk. Return false when all entries have been already
    166   // returned.
    167   bool GetEntry(disk_cache::EntryStore* entry);
    168 
    169   // Loads a specific block from the block files.
    170   bool LoadEntry(disk_cache::CacheAddr addr, disk_cache::EntryStore* entry);
    171   bool LoadRankings(disk_cache::CacheAddr addr,
    172                     disk_cache::RankingsNode* rankings);
    173 
    174  private:
    175   base::FilePath path_;
    176   disk_cache::BlockFiles block_files_;
    177   scoped_refptr<disk_cache::MappedFile> index_file_;
    178   disk_cache::Index* index_;
    179   int current_hash_;
    180   disk_cache::CacheAddr next_addr_;
    181   std::set<disk_cache::CacheAddr> dumped_entries_;
    182   DISALLOW_COPY_AND_ASSIGN(CacheDumper);
    183 };
    184 
    185 bool CacheDumper::Init() {
    186   if (!block_files_.Init(false)) {
    187     printf("Unable to init block files\n");
    188     return false;
    189   }
    190 
    191   base::FilePath index_name(path_.Append(kIndexName));
    192   index_file_ = new disk_cache::MappedFile;
    193   index_ = reinterpret_cast<disk_cache::Index*>(
    194       index_file_->Init(index_name, 0));
    195   if (!index_) {
    196     printf("Unable to map index\n");
    197     return false;
    198   }
    199 
    200   return true;
    201 }
    202 
    203 bool CacheDumper::GetEntry(disk_cache::EntryStore* entry) {
    204   if (dumped_entries_.find(next_addr_) != dumped_entries_.end()) {
    205     printf("Loop detected\n");
    206     next_addr_ = 0;
    207     current_hash_++;
    208   }
    209 
    210   if (next_addr_) {
    211     if (LoadEntry(next_addr_, entry))
    212       return true;
    213 
    214     printf("Unable to load entry at address 0x%x\n", next_addr_);
    215     next_addr_ = 0;
    216     current_hash_++;
    217   }
    218 
    219   for (int i = current_hash_; i < index_->header.table_len; i++) {
    220     // Yes, we'll crash if the table is shorter than expected, but only after
    221     // dumping every entry that we can find.
    222     if (index_->table[i]) {
    223       current_hash_ = i;
    224       if (LoadEntry(index_->table[i], entry))
    225         return true;
    226 
    227       printf("Unable to load entry at address 0x%x\n", index_->table[i]);
    228     }
    229   }
    230   return false;
    231 }
    232 
    233 bool CacheDumper::LoadEntry(disk_cache::CacheAddr addr,
    234                             disk_cache::EntryStore* entry) {
    235   disk_cache::Addr address(addr);
    236   disk_cache::MappedFile* file = block_files_.GetFile(address);
    237   if (!file)
    238     return false;
    239 
    240   disk_cache::StorageBlock<disk_cache::EntryStore> entry_block(file, address);
    241   if (!entry_block.Load())
    242     return false;
    243 
    244   memcpy(entry, entry_block.Data(), sizeof(*entry));
    245   printf("Entry at 0x%x\n", addr);
    246 
    247   // Prepare for the next entry to load.
    248   next_addr_ = entry->next;
    249   if (next_addr_) {
    250     dumped_entries_.insert(addr);
    251   } else {
    252     current_hash_++;
    253     dumped_entries_.clear();
    254   }
    255   return true;
    256 }
    257 
    258 bool CacheDumper::LoadRankings(disk_cache::CacheAddr addr,
    259                                disk_cache::RankingsNode* rankings) {
    260   disk_cache::Addr address(addr);
    261   disk_cache::MappedFile* file = block_files_.GetFile(address);
    262   if (!file)
    263     return false;
    264 
    265   disk_cache::StorageBlock<disk_cache::RankingsNode> rank_block(file, address);
    266   if (!rank_block.Load())
    267     return false;
    268 
    269   memcpy(rankings, rank_block.Data(), sizeof(*rankings));
    270   printf("Rankings at 0x%x\n", addr);
    271   return true;
    272 }
    273 
    274 void DumpEntry(const disk_cache::EntryStore& entry) {
    275   std::string key;
    276   if (!entry.long_key) {
    277     key = entry.key;
    278     if (key.size() > 50)
    279       key.resize(50);
    280   }
    281 
    282   printf("hash: 0x%x\n", entry.hash);
    283   printf("next entry: 0x%x\n", entry.next);
    284   printf("rankings: 0x%x\n", entry.rankings_node);
    285   printf("key length: %d\n", entry.key_len);
    286   printf("key: \"%s\"\n", key.c_str());
    287   printf("key addr: 0x%x\n", entry.long_key);
    288   printf("reuse count: %d\n", entry.reuse_count);
    289   printf("refetch count: %d\n", entry.refetch_count);
    290   printf("state: %d\n", entry.state);
    291   for (int i = 0; i < 4; i++) {
    292     printf("data size %d: %d\n", i, entry.data_size[i]);
    293     printf("data addr %d: 0x%x\n", i, entry.data_addr[i]);
    294   }
    295   printf("----------\n\n");
    296 }
    297 
    298 void DumpRankings(const disk_cache::RankingsNode& rankings) {
    299   printf("next: 0x%x\n", rankings.next);
    300   printf("prev: 0x%x\n", rankings.prev);
    301   printf("entry: 0x%x\n", rankings.contents);
    302   printf("dirty: %d\n", rankings.dirty);
    303   printf("hash: 0x%x\n", rankings.self_hash);
    304   printf("----------\n\n");
    305 }
    306 
    307 }  // namespace.
    308 
    309 // -----------------------------------------------------------------------
    310 
    311 int GetMajorVersion(const base::FilePath& input_path) {
    312   base::FilePath index_name(input_path.Append(kIndexName));
    313 
    314   int version = GetMajorVersionFromFile(index_name);
    315   if (!version)
    316     return 0;
    317 
    318   base::FilePath data_name(input_path.Append(FILE_PATH_LITERAL("data_0")));
    319   if (version != GetMajorVersionFromFile(data_name))
    320     return 0;
    321 
    322   data_name = input_path.Append(FILE_PATH_LITERAL("data_1"));
    323   if (version != GetMajorVersionFromFile(data_name))
    324     return 0;
    325 
    326   data_name = input_path.Append(FILE_PATH_LITERAL("data_2"));
    327   if (version != GetMajorVersionFromFile(data_name))
    328     return 0;
    329 
    330   data_name = input_path.Append(FILE_PATH_LITERAL("data_3"));
    331   if (version != GetMajorVersionFromFile(data_name))
    332     return 0;
    333 
    334   return version;
    335 }
    336 
    337 // Dumps the headers of all files.
    338 int DumpHeaders(const base::FilePath& input_path) {
    339   base::FilePath index_name(input_path.Append(kIndexName));
    340   disk_cache::CacheAddr stats_addr = 0;
    341   DumpIndexHeader(index_name, &stats_addr);
    342 
    343   base::FileEnumerator iter(input_path, false,
    344                             base::FileEnumerator::FILES,
    345                             FILE_PATH_LITERAL("data_*"));
    346   for (base::FilePath file = iter.Next(); !file.empty(); file = iter.Next())
    347     DumpBlockHeader(file);
    348 
    349   DumpStats(input_path, stats_addr);
    350   return 0;
    351 }
    352 
    353 // Dumps all entries from the cache.
    354 int DumpContents(const base::FilePath& input_path) {
    355   DumpHeaders(input_path);
    356 
    357   // We need a message loop, although we really don't run any task.
    358   base::MessageLoop loop(base::MessageLoop::TYPE_IO);
    359   CacheDumper dumper(input_path);
    360   if (!dumper.Init())
    361     return -1;
    362 
    363   disk_cache::EntryStore entry;
    364   while (dumper.GetEntry(&entry)) {
    365     DumpEntry(entry);
    366     disk_cache::RankingsNode rankings;
    367     if (dumper.LoadRankings(entry.rankings_node, &rankings))
    368       DumpRankings(rankings);
    369   }
    370 
    371   printf("Done.\n");
    372 
    373   return 0;
    374 }
    375