| OLD | NEW |
| (Empty) |
| 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 } | |
| OLD | NEW |