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