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