dump_files.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
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
21namespace {
22
23const wchar_t kIndexName[] = L"index";
24const wchar_t kDataPrefix[] = L"data_";
25
26// Reads the |header_size| bytes from the beginning of file |name|.
27bool 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
44int 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.
53void 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.
80void 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.
108class 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
140bool 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
159bool 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
189bool 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
214bool 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
230void 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
254void 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
267int 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.
289int 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.
307int 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