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