Bug 774146 - Get more detail on reason for DISK_CACHE_CORRUPT failures. r=michal

This commit is contained in:
Brian R. Bondy 2012-07-20 19:47:52 -04:00
Родитель 0d9b0cf38c
Коммит 343d8a43ac
7 изменённых файлов: 130 добавлений и 31 удалений

29
netwerk/cache/nsDiskCache.h поставляемый
Просмотреть файл

@ -23,6 +23,35 @@ public:
enum { kData, kMetaData };
// Stores the reason why the cache is corrupt.
// Note: I'm only listing the enum values explicitly for easy mapping when
// looking at telemetry data.
enum CorruptCacheInfo {
kNotCorrupt = 0,
kInvalidArgPointer = 1,
kUnexpectedError = 2,
kOpenCacheMapError = 3,
kBlockFilesShouldNotExist = 4,
kOutOfMemory = 5,
kCreateCacheSubdirectories = 6,
kBlockFilesShouldExist = 7,
kHeaderSizeNotRead = 8,
kHeaderIsDirty = 9,
kVersionMismatch = 10,
kRecordsIncomplete = 11,
kHeaderIncomplete = 12,
kNotEnoughToRead = 13,
kEntryCountIncorrect = 14,
kCouldNotGetBlockFileForIndex = 15,
kCouldNotCreateBlockFile = 16,
kBlockFileSizeError = 17,
kBlockFileBitMapWriteError = 18,
kBlockFileSizeLessThanBitMap = 19,
kBlockFileBitMapReadError = 20,
kBlockFileEstimatedSizeError = 21,
kFlushHeaderError = 22
};
// Parameter initval initializes internal state of hash function. Hash values are different
// for the same text when different initval is used. It can be any random number.
//

19
netwerk/cache/nsDiskCacheBlockFile.cpp поставляемый
Просмотреть файл

@ -21,10 +21,16 @@ using namespace mozilla;
nsresult
nsDiskCacheBlockFile::Open(nsIFile * blockFile,
PRUint32 blockSize,
PRUint32 bitMapSize)
PRUint32 bitMapSize,
nsDiskCache::CorruptCacheInfo * corruptInfo)
{
if (bitMapSize % 32)
NS_ENSURE_ARG_POINTER(corruptInfo);
*corruptInfo = nsDiskCache::kUnexpectedError;
if (bitMapSize % 32) {
*corruptInfo = nsDiskCache::kInvalidArgPointer;
return NS_ERROR_INVALID_ARG;
}
mBlockSize = blockSize;
mBitMapWords = bitMapSize / 32;
@ -33,6 +39,7 @@ nsDiskCacheBlockFile::Open(nsIFile * blockFile,
// open the file - restricted to user, the data could be confidential
nsresult rv = blockFile->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE, 00600, &mFD);
if (NS_FAILED(rv)) {
*corruptInfo = nsDiskCache::kCouldNotCreateBlockFile;
CACHE_LOG_DEBUG(("CACHE: nsDiskCacheBlockFile::Open "
"[this=%p] unable to open or create file: %d",
this, rv));
@ -46,16 +53,20 @@ nsDiskCacheBlockFile::Open(nsIFile * blockFile,
mFileSize = PR_Available(mFD);
if (mFileSize < 0) {
// XXX an error occurred. We could call PR_GetError(), but how would that help?
*corruptInfo = nsDiskCache::kBlockFileSizeError;
rv = NS_ERROR_UNEXPECTED;
goto error_exit;
}
if (mFileSize == 0) {
// initialize bit map and write it
memset(mBitMap, 0, bitMapBytes);
if (!Write(0, mBitMap, bitMapBytes))
if (!Write(0, mBitMap, bitMapBytes)) {
*corruptInfo = nsDiskCache::kBlockFileBitMapWriteError;
goto error_exit;
}
} else if ((PRUint32)mFileSize < bitMapBytes) {
*corruptInfo = nsDiskCache::kBlockFileSizeLessThanBitMap;
rv = NS_ERROR_UNEXPECTED; // XXX NS_ERROR_CACHE_INVALID;
goto error_exit;
@ -63,6 +74,7 @@ nsDiskCacheBlockFile::Open(nsIFile * blockFile,
// read the bit map
const PRInt32 bytesRead = PR_Read(mFD, mBitMap, bitMapBytes);
if ((bytesRead < 0) || ((PRUint32)bytesRead < bitMapBytes)) {
*corruptInfo = nsDiskCache::kBlockFileBitMapReadError;
rv = NS_ERROR_UNEXPECTED;
goto error_exit;
}
@ -77,6 +89,7 @@ nsDiskCacheBlockFile::Open(nsIFile * blockFile,
// because the last block will generally not be 'whole'.
const PRUint32 estimatedSize = CalcBlockFileSize();
if ((PRUint32)mFileSize + blockSize < estimatedSize) {
*corruptInfo = nsDiskCache::kBlockFileEstimatedSizeError;
rv = NS_ERROR_UNEXPECTED;
goto error_exit;
}

5
netwerk/cache/nsDiskCacheBlockFile.h поставляемый
Просмотреть файл

@ -8,6 +8,7 @@
#define _nsDiskCacheBlockFile_h_
#include "nsIFile.h"
#include "nsDiskCache.h"
/******************************************************************************
* nsDiskCacheBlockFile
@ -30,9 +31,9 @@ public:
~nsDiskCacheBlockFile() { (void) Close(true); }
nsresult Open( nsIFile * blockFile, PRUint32 blockSize,
PRUint32 bitMapSize);
PRUint32 bitMapSize, nsDiskCache::CorruptCacheInfo * corruptInfo);
nsresult Close(bool flush);
/*
* Trim
* Truncates the block file to the end of the last allocated block.

13
netwerk/cache/nsDiskCacheDevice.cpp поставляемый
Просмотреть файл

@ -986,12 +986,16 @@ nsDiskCacheDevice::OpenDiskCache()
if (exists) {
// Try opening cache map file.
rv = mCacheMap.Open(mCacheDirectory);
nsDiskCache::CorruptCacheInfo corruptInfo;
rv = mCacheMap.Open(mCacheDirectory, &corruptInfo);
// move "corrupt" caches to trash
if (NS_SUCCEEDED(rv)) {
Telemetry::Accumulate(Telemetry::DISK_CACHE_CORRUPT, 0);
Telemetry::Accumulate(Telemetry::DISK_CACHE_CORRUPT_DETAILS,
corruptInfo);
} else if (rv == NS_ERROR_FILE_CORRUPTED) {
Telemetry::Accumulate(Telemetry::DISK_CACHE_CORRUPT, 1);
Telemetry::Accumulate(Telemetry::DISK_CACHE_CORRUPT_DETAILS,
corruptInfo);
// delay delete by 1 minute to avoid IO thrash at startup
rv = nsDeleteDir::DeleteDir(mCacheDirectory, true, 60000);
if (NS_FAILED(rv))
@ -1013,7 +1017,8 @@ nsDiskCacheDevice::OpenDiskCache()
return rv;
// reopen the cache map
rv = mCacheMap.Open(mCacheDirectory);
nsDiskCache::CorruptCacheInfo corruptInfo;
rv = mCacheMap.Open(mCacheDirectory, &corruptInfo);
if (NS_FAILED(rv))
return rv;
}

88
netwerk/cache/nsDiskCacheMap.cpp поставляемый
Просмотреть файл

@ -27,8 +27,13 @@
*/
nsresult
nsDiskCacheMap::Open(nsIFile * cacheDirectory)
nsDiskCacheMap::Open(nsIFile * cacheDirectory,
nsDiskCache::CorruptCacheInfo * corruptInfo)
{
NS_ENSURE_ARG_POINTER(corruptInfo);
// Assume we have an unexpected error until we find otherwise.
*corruptInfo = nsDiskCache::kUnexpectedError;
NS_ENSURE_ARG_POINTER(cacheDirectory);
if (mMapFD) return NS_ERROR_ALREADY_INITIALIZED;
@ -43,7 +48,11 @@ nsDiskCacheMap::Open(nsIFile * cacheDirectory)
// open the file - restricted to user, the data could be confidential
rv = file->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE, 00600, &mMapFD);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FILE_CORRUPTED);
if (NS_FAILED(rv)) {
*corruptInfo = nsDiskCache::kOpenCacheMapError;
NS_WARNING("Could not open cache map file");
return NS_ERROR_FILE_CORRUPTED;
}
bool cacheFilesExist = CacheFilesExist();
rv = NS_ERROR_FILE_CORRUPTED; // presume the worst
@ -53,11 +62,15 @@ nsDiskCacheMap::Open(nsIFile * cacheDirectory)
if (mapSize == 0) { // creating a new _CACHE_MAP_
// block files shouldn't exist if we're creating the _CACHE_MAP_
if (cacheFilesExist)
goto error_exit;
if (cacheFilesExist) {
*corruptInfo = nsDiskCache::kBlockFilesShouldNotExist;
goto error_exit;
}
if (NS_FAILED(CreateCacheSubDirectories()))
if (NS_FAILED(CreateCacheSubDirectories())) {
*corruptInfo = nsDiskCache::kCreateCacheSubdirectories;
goto error_exit;
}
// create the file - initialize in memory
memset(&mHeader, 0, sizeof(nsDiskCacheHeader));
@ -66,41 +79,59 @@ nsDiskCacheMap::Open(nsIFile * cacheDirectory)
mRecordArray = (nsDiskCacheRecord *)
PR_CALLOC(mHeader.mRecordCount * sizeof(nsDiskCacheRecord));
if (!mRecordArray) {
*corruptInfo = nsDiskCache::kOutOfMemory;
rv = NS_ERROR_OUT_OF_MEMORY;
goto error_exit;
}
} else if (mapSize >= sizeof(nsDiskCacheHeader)) { // read existing _CACHE_MAP_
// if _CACHE_MAP_ exists, so should the block files
if (!cacheFilesExist)
if (!cacheFilesExist) {
*corruptInfo = nsDiskCache::kBlockFilesShouldExist;
goto error_exit;
}
CACHE_LOG_DEBUG(("CACHE: nsDiskCacheMap::Open [this=%p] reading map", this));
// read the header
PRUint32 bytesRead = PR_Read(mMapFD, &mHeader, sizeof(nsDiskCacheHeader));
if (sizeof(nsDiskCacheHeader) != bytesRead) goto error_exit;
if (sizeof(nsDiskCacheHeader) != bytesRead) {
*corruptInfo = nsDiskCache::kHeaderSizeNotRead;
goto error_exit;
}
mHeader.Unswap();
if (mHeader.mIsDirty || (mHeader.mVersion != nsDiskCache::kCurrentVersion))
if (mHeader.mIsDirty) {
*corruptInfo = nsDiskCache::kHeaderIsDirty;
goto error_exit;
}
if (mHeader.mVersion != nsDiskCache::kCurrentVersion) {
*corruptInfo = nsDiskCache::kVersionMismatch;
goto error_exit;
}
PRUint32 recordArraySize =
mHeader.mRecordCount * sizeof(nsDiskCacheRecord);
if (mapSize < recordArraySize + sizeof(nsDiskCacheHeader))
if (mapSize < recordArraySize + sizeof(nsDiskCacheHeader)) {
*corruptInfo = nsDiskCache::kRecordsIncomplete;
goto error_exit;
}
// Get the space for the records
mRecordArray = (nsDiskCacheRecord *) PR_MALLOC(recordArraySize);
if (!mRecordArray) {
*corruptInfo = nsDiskCache::kOutOfMemory;
rv = NS_ERROR_OUT_OF_MEMORY;
goto error_exit;
}
// Read the records
bytesRead = PR_Read(mMapFD, mRecordArray, recordArraySize);
if (bytesRead < recordArraySize)
if (bytesRead < recordArraySize) {
*corruptInfo = nsDiskCache::kNotEnoughToRead;
goto error_exit;
}
// Unswap each record
PRInt32 total = 0;
@ -114,20 +145,29 @@ nsDiskCacheMap::Open(nsIFile * cacheDirectory)
}
// verify entry count
if (total != mHeader.mEntryCount)
if (total != mHeader.mEntryCount) {
*corruptInfo = nsDiskCache::kEntryCountIncorrect;
goto error_exit;
}
} else {
*corruptInfo = nsDiskCache::kHeaderIncomplete;
goto error_exit;
}
rv = OpenBlockFiles();
if (NS_FAILED(rv)) goto error_exit;
rv = OpenBlockFiles(corruptInfo);
if (NS_FAILED(rv)) {
// corruptInfo is set in the call to OpenBlockFiles
goto error_exit;
}
// set dirty bit and flush header
mHeader.mIsDirty = true;
rv = FlushHeader();
if (NS_FAILED(rv)) goto error_exit;
if (NS_FAILED(rv)) {
*corruptInfo = nsDiskCache::kFlushHeaderError;
goto error_exit;
}
{
// extra scope so the compiler doesn't barf on the above gotos jumping
@ -137,6 +177,7 @@ nsDiskCacheMap::Open(nsIFile * cacheDirectory)
overhead);
}
*corruptInfo = nsDiskCache::kNotCorrupt;
return NS_OK;
error_exit:
@ -585,23 +626,32 @@ nsDiskCacheMap::EvictRecords( nsDiskCacheRecordVisitor * visitor)
nsresult
nsDiskCacheMap::OpenBlockFiles()
nsDiskCacheMap::OpenBlockFiles(nsDiskCache::CorruptCacheInfo * corruptInfo)
{
NS_ENSURE_ARG_POINTER(corruptInfo);
// create nsIFile for block file
nsCOMPtr<nsIFile> blockFile;
nsresult rv = NS_OK;
*corruptInfo = nsDiskCache::kUnexpectedError;
for (int i = 0; i < kNumBlockFiles; ++i) {
rv = GetBlockFileForIndex(i, getter_AddRefs(blockFile));
if (NS_FAILED(rv)) break;
if (NS_FAILED(rv)) {
*corruptInfo = nsDiskCache::kCouldNotGetBlockFileForIndex;
break;
}
PRUint32 blockSize = GetBlockSizeForIndex(i+1); // +1 to match file selectors 1,2,3
PRUint32 bitMapSize = GetBitMapSizeForIndex(i+1);
rv = mBlockFile[i].Open(blockFile, blockSize, bitMapSize);
if (NS_FAILED(rv)) break;
rv = mBlockFile[i].Open(blockFile, blockSize, bitMapSize, corruptInfo);
if (NS_FAILED(rv)) {
// corruptInfo was set inside the call to mBlockFile[i].Open
break;
}
}
// close all files in case of any error
if (NS_FAILED(rv))
if (NS_FAILED(rv))
(void)CloseBlockFiles(false); // we already have an error to report
return rv;

5
netwerk/cache/nsDiskCacheMap.h поставляемый
Просмотреть файл

@ -396,7 +396,8 @@ public:
* Creates a new cache map file if one doesn't exist.
* Returns error if it detects change in format or cache wasn't closed.
*/
nsresult Open( nsIFile * cacheDirectory);
nsresult Open( nsIFile * cacheDirectory,
nsDiskCache::CorruptCacheInfo * corruptInfo);
nsresult Close(bool flush);
nsresult Trim();
@ -478,7 +479,7 @@ private:
/**
* Private methods
*/
nsresult OpenBlockFiles();
nsresult OpenBlockFiles(nsDiskCache::CorruptCacheInfo * corruptInfo);
nsresult CloseBlockFiles(bool flush);
bool CacheFilesExist();

Просмотреть файл

@ -195,7 +195,7 @@ HISTOGRAM(SPDY_SETTINGS_IW, 1, 1000, 50, EXPONENTIAL, "SPDY: Settings IW (round
#undef _HTTP_HIST
#undef HTTP_HISTOGRAMS
HISTOGRAM(DISK_CACHE_CORRUPT, 0, 1, 2, BOOLEAN, "Was the HTTP disk cache corrupt at startup?")
HISTOGRAM(DISK_CACHE_CORRUPT_DETAILS, 1, 50, 51, LINEAR, "Why the HTTP disk cache was corrupted at startup")
HISTOGRAM_ENUMERATED_VALUES(HTTP_CACHE_DISPOSITION_2, 5, "HTTP Cache Hit, Reval, Failed-Reval, Miss")
HISTOGRAM_ENUMERATED_VALUES(HTTP_DISK_CACHE_DISPOSITION_2, 5, "HTTP Disk Cache Hit, Reval, Failed-Reval, Miss")
HISTOGRAM_ENUMERATED_VALUES(HTTP_MEMORY_CACHE_DISPOSITION_2, 5, "HTTP Memory Cache Hit, Reval, Failed-Reval, Miss")