• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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|.
ReadHeader(const std::wstring & name,char * header,int header_size)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 
GetMajorVersionFromFile(const std::wstring & name)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.
DumpIndexHeader(const std::wstring & name)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.
DumpBlockHeader(const std::wstring & name)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:
CacheDumper(const std::wstring & path)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 
Init()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 
GetEntry(disk_cache::EntryStore * entry)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 
LoadEntry(disk_cache::CacheAddr addr,disk_cache::EntryStore * entry)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 
LoadRankings(disk_cache::CacheAddr addr,disk_cache::RankingsNode * rankings)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 
DumpEntry(const disk_cache::EntryStore & entry)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 
DumpRankings(const disk_cache::RankingsNode & rankings)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 
GetMajorVersion(const std::wstring & input_path)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.
DumpHeaders(const std::wstring & input_path)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.
DumpContents(const std::wstring & input_path)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