dump_files.cc revision 3f50c38dc070f4bb515c1b64450dae14f316474e
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 <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
20namespace {
21
22const wchar_t kIndexName[] = L"index";
23const wchar_t kDataPrefix[] = L"data_";
24
25// Reads the |header_size| bytes from the beginning of file |name|.
26bool 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
43int 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.
52void 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.
78void 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 = FilePath(name).BaseName().value();
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.
106class CacheDumper {
107 public:
108  explicit CacheDumper(const std::wstring& path)
109      : path_(path),
110        block_files_(FilePath::FromWStringHack(path)),
111        index_(NULL),
112        current_hash_(0),
113        next_addr_(0) {
114  }
115
116  bool Init();
117
118  // Reads an entry from disk. Return false when all entries have been already
119  // returned.
120  bool GetEntry(disk_cache::EntryStore* entry);
121
122  // Loads a specific block from the block files.
123  bool LoadEntry(disk_cache::CacheAddr addr, disk_cache::EntryStore* entry);
124  bool LoadRankings(disk_cache::CacheAddr addr,
125                    disk_cache::RankingsNode* rankings);
126
127 private:
128  std::wstring path_;
129  disk_cache::BlockFiles block_files_;
130  scoped_refptr<disk_cache::MappedFile> index_file_;
131  disk_cache::Index* index_;
132  int current_hash_;
133  disk_cache::CacheAddr next_addr_;
134  DISALLOW_COPY_AND_ASSIGN(CacheDumper);
135};
136
137bool CacheDumper::Init() {
138  if (!block_files_.Init(false)) {
139    printf("Unable to init block files\n");
140    return false;
141  }
142
143  std::wstring index_name(path_);
144  file_util::AppendToPath(&index_name, kIndexName);
145  index_file_ = new disk_cache::MappedFile;
146  index_ = reinterpret_cast<disk_cache::Index*>(index_file_->Init(
147      FilePath::FromWStringHack(index_name), 0));
148  if (!index_) {
149    printf("Unable to map index\n");
150    return false;
151  }
152
153  return true;
154}
155
156bool CacheDumper::GetEntry(disk_cache::EntryStore* entry) {
157  if (next_addr_) {
158    if (LoadEntry(next_addr_, entry)) {
159      next_addr_ = entry->next;
160      if (!next_addr_)
161        current_hash_++;
162      return true;
163    } else {
164      printf("Unable to load entry at address 0x%x\n", next_addr_);
165      next_addr_ = 0;
166      current_hash_++;
167    }
168  }
169
170  for (int i = current_hash_; i < index_->header.table_len; i++) {
171    // Yes, we'll crash if the table is shorter than expected, but only after
172    // dumping every entry that we can find.
173    if (index_->table[i]) {
174      current_hash_ = i;
175      if (LoadEntry(index_->table[i], entry)) {
176        next_addr_ = entry->next;
177        if (!next_addr_)
178          current_hash_++;
179        return true;
180      } else {
181        printf("Unable to load entry at address 0x%x\n", index_->table[i]);
182      }
183    }
184  }
185  return false;
186}
187
188bool CacheDumper::LoadEntry(disk_cache::CacheAddr addr,
189                            disk_cache::EntryStore* entry) {
190  disk_cache::Addr address(addr);
191  disk_cache::MappedFile* file = block_files_.GetFile(address);
192  if (!file)
193    return false;
194
195  disk_cache::CacheEntryBlock entry_block(file, address);
196  if (!entry_block.Load())
197    return false;
198
199  memcpy(entry, entry_block.Data(), sizeof(*entry));
200  printf("Entry at 0x%x\n", addr);
201  return true;
202}
203
204bool CacheDumper::LoadRankings(disk_cache::CacheAddr addr,
205                               disk_cache::RankingsNode* rankings) {
206  disk_cache::Addr address(addr);
207  disk_cache::MappedFile* file = block_files_.GetFile(address);
208  if (!file)
209    return false;
210
211  disk_cache::CacheRankingsBlock rank_block(file, address);
212  if (!rank_block.Load())
213    return false;
214
215  memcpy(rankings, rank_block.Data(), sizeof(*rankings));
216  printf("Rankings at 0x%x\n", addr);
217  return true;
218}
219
220void DumpEntry(const disk_cache::EntryStore& entry) {
221  std::string key;
222  if (!entry.long_key) {
223    key = entry.key;
224    if (key.size() > 50)
225      key.resize(50);
226  }
227
228  printf("hash: 0x%x\n", entry.hash);
229  printf("next entry: 0x%x\n", entry.next);
230  printf("rankings: 0x%x\n", entry.rankings_node);
231  printf("key length: %d\n", entry.key_len);
232  printf("key: \"%s\"\n", key.c_str());
233  printf("key addr: 0x%x\n", entry.long_key);
234  printf("reuse count: %d\n", entry.reuse_count);
235  printf("refetch count: %d\n", entry.refetch_count);
236  printf("state: %d\n", entry.state);
237  for (int i = 0; i < 4; i++) {
238    printf("data size %d: %d\n", i, entry.data_size[i]);
239    printf("data addr %d: 0x%x\n", i, entry.data_addr[i]);
240  }
241  printf("----------\n\n");
242}
243
244void DumpRankings(const disk_cache::RankingsNode& rankings) {
245  printf("next: 0x%x\n", rankings.next);
246  printf("prev: 0x%x\n", rankings.prev);
247  printf("entry: 0x%x\n", rankings.contents);
248  printf("dirty: %d\n", rankings.dirty);
249  printf("pointer: 0x%x\n", rankings.dummy);
250  printf("----------\n\n");
251}
252
253}  // namespace.
254
255// -----------------------------------------------------------------------
256
257int GetMajorVersion(const std::wstring& input_path) {
258  std::wstring index_name(input_path);
259  file_util::AppendToPath(&index_name, kIndexName);
260
261  int version = GetMajorVersionFromFile(index_name);
262  if (!version)
263    return 0;
264
265  std::wstring data_name(input_path);
266  file_util::AppendToPath(&data_name, L"data_0");
267  if (version != GetMajorVersionFromFile(data_name))
268    return 0;
269
270  data_name = input_path;
271  file_util::AppendToPath(&data_name, L"data_1");
272  if (version != GetMajorVersionFromFile(data_name))
273    return 0;
274
275  return version;
276}
277
278// Dumps the headers of all files.
279int DumpHeaders(const std::wstring& input_path) {
280  std::wstring index_name(input_path);
281  file_util::AppendToPath(&index_name, kIndexName);
282  DumpIndexHeader(index_name);
283
284  std::wstring pattern(kDataPrefix);
285  pattern.append(L"*");
286  file_util::FileEnumerator iter(FilePath::FromWStringHack(input_path), false,
287                                 file_util::FileEnumerator::FILES, pattern);
288  for (std::wstring file = iter.Next().ToWStringHack(); !file.empty();
289       file = iter.Next().ToWStringHack()) {
290    DumpBlockHeader(file);
291  }
292
293  return 0;
294}
295
296// Dumps all entries from the cache.
297int DumpContents(const std::wstring& input_path) {
298  DumpHeaders(input_path);
299
300  // We need a message loop, although we really don't run any task.
301  MessageLoop loop(MessageLoop::TYPE_IO);
302  CacheDumper dumper(input_path);
303  if (!dumper.Init())
304    return -1;
305
306  disk_cache::EntryStore entry;
307  while (dumper.GetEntry(&entry)) {
308    DumpEntry(entry);
309    disk_cache::RankingsNode rankings;
310    if (dumper.LoadRankings(entry.rankings_node, &rankings))
311      DumpRankings(rankings);
312  }
313
314  printf("Done.\n");
315
316  return 0;
317}
318