зеркало из https://github.com/mozilla/gecko-dev.git
Bug 774146 - Get more detail on reason for DISK_CACHE_CORRUPT failures. r=michal
This commit is contained in:
Родитель
0d9b0cf38c
Коммит
343d8a43ac
|
@ -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.
|
||||
//
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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")
|
||||
|
|
Загрузка…
Ссылка в новой задаче