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