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