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