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