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/file_util.h" 17 #include "base/files/file_enumerator.h" 18 #include "base/format_macros.h" 19 #include "base/message_loop/message_loop.h" 20 #include "net/base/file_stream.h" 21 #include "net/disk_cache/block_files.h" 22 #include "net/disk_cache/disk_format.h" 23 #include "net/disk_cache/mapped_file.h" 24 #include "net/disk_cache/stats.h" 25 #include "net/disk_cache/storage_block-inl.h" 26 #include "net/disk_cache/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|. 33 bool ReadHeader(const base::FilePath& name, char* header, int header_size) { 34 net::FileStream file(NULL); 35 file.OpenSync(name, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ); 36 if (!file.IsOpen()) { 37 printf("Unable to open file %s\n", name.MaybeAsASCII().c_str()); 38 return false; 39 } 40 41 int read = file.ReadSync(header, header_size); 42 if (read != header_size) { 43 printf("Unable to read file %s\n", name.MaybeAsASCII().c_str()); 44 return false; 45 } 46 return true; 47 } 48 49 int GetMajorVersionFromFile(const base::FilePath& name) { 50 disk_cache::IndexHeader header; 51 if (!ReadHeader(name, reinterpret_cast<char*>(&header), sizeof(header))) 52 return 0; 53 54 return header.version >> 16; 55 } 56 57 // Dumps the contents of the Stats record. 58 void DumpStats(const base::FilePath& path, disk_cache::CacheAddr addr) { 59 // We need a message loop, although we really don't run any task. 60 base::MessageLoop loop(base::MessageLoop::TYPE_IO); 61 62 disk_cache::BlockFiles block_files(path); 63 if (!block_files.Init(false)) { 64 printf("Unable to init block files\n"); 65 return; 66 } 67 68 disk_cache::Addr address(addr); 69 disk_cache::MappedFile* file = block_files.GetFile(address); 70 if (!file) 71 return; 72 73 size_t length = (2 + disk_cache::Stats::kDataSizesLength) * sizeof(int32) + 74 disk_cache::Stats::MAX_COUNTER * sizeof(int64); 75 76 size_t offset = address.start_block() * address.BlockSize() + 77 disk_cache::kBlockHeaderSize; 78 79 scoped_ptr<int32[]> buffer(new int32[length]); 80 if (!file->Read(buffer.get(), length, offset)) 81 return; 82 83 printf("Stats:\nSignatrure: 0x%x\n", buffer[0]); 84 printf("Total size: %d\n", buffer[1]); 85 for (int i = 0; i < disk_cache::Stats::kDataSizesLength; i++) 86 printf("Size(%d): %d\n", i, buffer[i + 2]); 87 88 int64* counters = reinterpret_cast<int64*>( 89 buffer.get() + 2 + disk_cache::Stats::kDataSizesLength); 90 for (int i = 0; i < disk_cache::Stats::MAX_COUNTER; i++) 91 printf("Count(%d): %" PRId64 "\n", i, *counters++); 92 printf("-------------------------\n\n"); 93 } 94 95 // Dumps the contents of the Index-file header. 96 void DumpIndexHeader(const base::FilePath& name, 97 disk_cache::CacheAddr* stats_addr) { 98 disk_cache::IndexHeader header; 99 if (!ReadHeader(name, reinterpret_cast<char*>(&header), sizeof(header))) 100 return; 101 102 printf("Index file:\n"); 103 printf("magic: %x\n", header.magic); 104 printf("version: %d.%d\n", header.version >> 16, header.version & 0xffff); 105 printf("entries: %d\n", header.num_entries); 106 printf("total bytes: %d\n", header.num_bytes); 107 printf("last file number: %d\n", header.last_file); 108 printf("current id: %d\n", header.this_id); 109 printf("table length: %d\n", header.table_len); 110 printf("last crash: %d\n", header.crash); 111 printf("experiment: %d\n", header.experiment); 112 printf("stats: %x\n", header.stats); 113 for (int i = 0; i < 5; i++) { 114 printf("head %d: 0x%x\n", i, header.lru.heads[i]); 115 printf("tail %d: 0x%x\n", i, header.lru.tails[i]); 116 printf("size %d: 0x%x\n", i, header.lru.sizes[i]); 117 } 118 printf("transaction: 0x%x\n", header.lru.transaction); 119 printf("operation: %d\n", header.lru.operation); 120 printf("operation list: %d\n", header.lru.operation_list); 121 printf("-------------------------\n\n"); 122 123 *stats_addr = header.stats; 124 } 125 126 // Dumps the contents of a block-file header. 127 void DumpBlockHeader(const base::FilePath& name) { 128 disk_cache::BlockFileHeader header; 129 if (!ReadHeader(name, reinterpret_cast<char*>(&header), sizeof(header))) 130 return; 131 132 printf("Block file: %s\n", name.BaseName().MaybeAsASCII().c_str()); 133 printf("magic: %x\n", header.magic); 134 printf("version: %d.%d\n", header.version >> 16, header.version & 0xffff); 135 printf("file id: %d\n", header.this_file); 136 printf("next file id: %d\n", header.next_file); 137 printf("entry size: %d\n", header.entry_size); 138 printf("current entries: %d\n", header.num_entries); 139 printf("max entries: %d\n", header.max_entries); 140 printf("updating: %d\n", header.updating); 141 printf("empty sz 1: %d\n", header.empty[0]); 142 printf("empty sz 2: %d\n", header.empty[1]); 143 printf("empty sz 3: %d\n", header.empty[2]); 144 printf("empty sz 4: %d\n", header.empty[3]); 145 printf("user 0: 0x%x\n", header.user[0]); 146 printf("user 1: 0x%x\n", header.user[1]); 147 printf("user 2: 0x%x\n", header.user[2]); 148 printf("user 3: 0x%x\n", header.user[3]); 149 printf("-------------------------\n\n"); 150 } 151 152 // Simple class that interacts with the set of cache files. 153 class CacheDumper { 154 public: 155 explicit CacheDumper(const base::FilePath& path) 156 : path_(path), 157 block_files_(path), 158 index_(NULL), 159 current_hash_(0), 160 next_addr_(0) { 161 } 162 163 bool Init(); 164 165 // Reads an entry from disk. Return false when all entries have been already 166 // returned. 167 bool GetEntry(disk_cache::EntryStore* entry); 168 169 // Loads a specific block from the block files. 170 bool LoadEntry(disk_cache::CacheAddr addr, disk_cache::EntryStore* entry); 171 bool LoadRankings(disk_cache::CacheAddr addr, 172 disk_cache::RankingsNode* rankings); 173 174 private: 175 base::FilePath path_; 176 disk_cache::BlockFiles block_files_; 177 scoped_refptr<disk_cache::MappedFile> index_file_; 178 disk_cache::Index* index_; 179 int current_hash_; 180 disk_cache::CacheAddr next_addr_; 181 std::set<disk_cache::CacheAddr> dumped_entries_; 182 DISALLOW_COPY_AND_ASSIGN(CacheDumper); 183 }; 184 185 bool CacheDumper::Init() { 186 if (!block_files_.Init(false)) { 187 printf("Unable to init block files\n"); 188 return false; 189 } 190 191 base::FilePath index_name(path_.Append(kIndexName)); 192 index_file_ = new disk_cache::MappedFile; 193 index_ = reinterpret_cast<disk_cache::Index*>( 194 index_file_->Init(index_name, 0)); 195 if (!index_) { 196 printf("Unable to map index\n"); 197 return false; 198 } 199 200 return true; 201 } 202 203 bool CacheDumper::GetEntry(disk_cache::EntryStore* entry) { 204 if (dumped_entries_.find(next_addr_) != dumped_entries_.end()) { 205 printf("Loop detected\n"); 206 next_addr_ = 0; 207 current_hash_++; 208 } 209 210 if (next_addr_) { 211 if (LoadEntry(next_addr_, entry)) 212 return true; 213 214 printf("Unable to load entry at address 0x%x\n", next_addr_); 215 next_addr_ = 0; 216 current_hash_++; 217 } 218 219 for (int i = current_hash_; i < index_->header.table_len; i++) { 220 // Yes, we'll crash if the table is shorter than expected, but only after 221 // dumping every entry that we can find. 222 if (index_->table[i]) { 223 current_hash_ = i; 224 if (LoadEntry(index_->table[i], entry)) 225 return true; 226 227 printf("Unable to load entry at address 0x%x\n", index_->table[i]); 228 } 229 } 230 return false; 231 } 232 233 bool CacheDumper::LoadEntry(disk_cache::CacheAddr addr, 234 disk_cache::EntryStore* entry) { 235 disk_cache::Addr address(addr); 236 disk_cache::MappedFile* file = block_files_.GetFile(address); 237 if (!file) 238 return false; 239 240 disk_cache::StorageBlock<disk_cache::EntryStore> entry_block(file, address); 241 if (!entry_block.Load()) 242 return false; 243 244 memcpy(entry, entry_block.Data(), sizeof(*entry)); 245 printf("Entry at 0x%x\n", addr); 246 247 // Prepare for the next entry to load. 248 next_addr_ = entry->next; 249 if (next_addr_) { 250 dumped_entries_.insert(addr); 251 } else { 252 current_hash_++; 253 dumped_entries_.clear(); 254 } 255 return true; 256 } 257 258 bool CacheDumper::LoadRankings(disk_cache::CacheAddr addr, 259 disk_cache::RankingsNode* rankings) { 260 disk_cache::Addr address(addr); 261 disk_cache::MappedFile* file = block_files_.GetFile(address); 262 if (!file) 263 return false; 264 265 disk_cache::StorageBlock<disk_cache::RankingsNode> rank_block(file, address); 266 if (!rank_block.Load()) 267 return false; 268 269 memcpy(rankings, rank_block.Data(), sizeof(*rankings)); 270 printf("Rankings at 0x%x\n", addr); 271 return true; 272 } 273 274 void DumpEntry(const disk_cache::EntryStore& entry) { 275 std::string key; 276 if (!entry.long_key) { 277 key = entry.key; 278 if (key.size() > 50) 279 key.resize(50); 280 } 281 282 printf("hash: 0x%x\n", entry.hash); 283 printf("next entry: 0x%x\n", entry.next); 284 printf("rankings: 0x%x\n", entry.rankings_node); 285 printf("key length: %d\n", entry.key_len); 286 printf("key: \"%s\"\n", key.c_str()); 287 printf("key addr: 0x%x\n", entry.long_key); 288 printf("reuse count: %d\n", entry.reuse_count); 289 printf("refetch count: %d\n", entry.refetch_count); 290 printf("state: %d\n", entry.state); 291 for (int i = 0; i < 4; i++) { 292 printf("data size %d: %d\n", i, entry.data_size[i]); 293 printf("data addr %d: 0x%x\n", i, entry.data_addr[i]); 294 } 295 printf("----------\n\n"); 296 } 297 298 void DumpRankings(const disk_cache::RankingsNode& rankings) { 299 printf("next: 0x%x\n", rankings.next); 300 printf("prev: 0x%x\n", rankings.prev); 301 printf("entry: 0x%x\n", rankings.contents); 302 printf("dirty: %d\n", rankings.dirty); 303 printf("hash: 0x%x\n", rankings.self_hash); 304 printf("----------\n\n"); 305 } 306 307 } // namespace. 308 309 // ----------------------------------------------------------------------- 310 311 int GetMajorVersion(const base::FilePath& input_path) { 312 base::FilePath index_name(input_path.Append(kIndexName)); 313 314 int version = GetMajorVersionFromFile(index_name); 315 if (!version) 316 return 0; 317 318 base::FilePath data_name(input_path.Append(FILE_PATH_LITERAL("data_0"))); 319 if (version != GetMajorVersionFromFile(data_name)) 320 return 0; 321 322 data_name = input_path.Append(FILE_PATH_LITERAL("data_1")); 323 if (version != GetMajorVersionFromFile(data_name)) 324 return 0; 325 326 data_name = input_path.Append(FILE_PATH_LITERAL("data_2")); 327 if (version != GetMajorVersionFromFile(data_name)) 328 return 0; 329 330 data_name = input_path.Append(FILE_PATH_LITERAL("data_3")); 331 if (version != GetMajorVersionFromFile(data_name)) 332 return 0; 333 334 return version; 335 } 336 337 // Dumps the headers of all files. 338 int DumpHeaders(const base::FilePath& input_path) { 339 base::FilePath index_name(input_path.Append(kIndexName)); 340 disk_cache::CacheAddr stats_addr = 0; 341 DumpIndexHeader(index_name, &stats_addr); 342 343 base::FileEnumerator iter(input_path, false, 344 base::FileEnumerator::FILES, 345 FILE_PATH_LITERAL("data_*")); 346 for (base::FilePath file = iter.Next(); !file.empty(); file = iter.Next()) 347 DumpBlockHeader(file); 348 349 DumpStats(input_path, stats_addr); 350 return 0; 351 } 352 353 // Dumps all entries from the cache. 354 int DumpContents(const base::FilePath& input_path) { 355 DumpHeaders(input_path); 356 357 // We need a message loop, although we really don't run any task. 358 base::MessageLoop loop(base::MessageLoop::TYPE_IO); 359 CacheDumper dumper(input_path); 360 if (!dumper.Init()) 361 return -1; 362 363 disk_cache::EntryStore entry; 364 while (dumper.GetEntry(&entry)) { 365 DumpEntry(entry); 366 disk_cache::RankingsNode rankings; 367 if (dumper.LoadRankings(entry.rankings_node, &rankings)) 368 DumpRankings(rankings); 369 } 370 371 printf("Done.\n"); 372 373 return 0; 374 } 375