Bug 888534: Add support for Create, Stat and Close operations to IOInterposer. r=jonasfj

--HG--
extra : rebase_source : 6b7e75ae35e3250383f3990dce2680df44f4ec68
This commit is contained in:
Aaron Klotz 2013-10-01 13:53:19 -06:00
parent 18fe7ab8b2
commit 44e3459f45
5 changed files with 154 additions and 36 deletions

View File

@ -81,11 +81,27 @@ public:
{
}
~IOThreadAutoTimer() {
/**
* This constructor is for when we want to report an operation to
* IOInterposer but do not require a telemetry probe.
*
* @param aOp IO Operation to report through the IOInterposer.
*/
IOThreadAutoTimer(IOInterposeObserver::Operation aOp)
: start(TimeStamp::Now()),
id(Telemetry::HistogramCount),
op(aOp)
{
}
~IOThreadAutoTimer()
{
TimeStamp end(TimeStamp::Now());
uint32_t mainThread = NS_IsMainThread() ? 1 : 0;
Telemetry::AccumulateTimeDelta(static_cast<Telemetry::ID>(id + mainThread),
start, end);
if (id != Telemetry::HistogramCount) {
Telemetry::AccumulateTimeDelta(static_cast<Telemetry::ID>(id + mainThread),
start, end);
}
#ifdef MOZ_ENABLE_PROFILER_SPS
if (IOInterposer::IsObservedOperation(op)) {
const char* main_ref = "sqlite-mainthread";
@ -128,7 +144,10 @@ xClose(sqlite3_file *pFile)
{
telemetry_file *p = (telemetry_file *)pFile;
int rc;
rc = p->pReal->pMethods->xClose(p->pReal);
{ // Scope for IOThreadAutoTimer
IOThreadAutoTimer ioTimer(IOInterposeObserver::OpClose);
rc = p->pReal->pMethods->xClose(p->pReal);
}
if( rc==SQLITE_OK ){
delete p->base.pMethods;
p->base.pMethods = nullptr;
@ -204,6 +223,7 @@ xSync(sqlite3_file *pFile, int flags)
int
xFileSize(sqlite3_file *pFile, sqlite_int64 *pSize)
{
IOThreadAutoTimer ioTimer(IOInterposeObserver::OpStat);
telemetry_file *p = (telemetry_file *)pFile;
int rc;
rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
@ -333,7 +353,8 @@ int
xOpen(sqlite3_vfs* vfs, const char *zName, sqlite3_file* pFile,
int flags, int *pOutFlags)
{
IOThreadAutoTimer ioTimer(Telemetry::MOZ_SQLITE_OPEN_MS);
IOThreadAutoTimer ioTimer(Telemetry::MOZ_SQLITE_OPEN_MS,
IOInterposeObserver::OpCreateOrOpen);
Telemetry::AutoTimer<Telemetry::MOZ_SQLITE_OPEN_MS> timer;
sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
int rc;

View File

@ -42,9 +42,12 @@ struct ObserverLists {
// These are implemented as vectors since they are allowed to survive gecko,
// without reporting leaks. This is necessary for the IOInterposer to be used
// for late-write checks.
std::vector<IOInterposeObserver*> mCreateObservers;
std::vector<IOInterposeObserver*> mReadObservers;
std::vector<IOInterposeObserver*> mWriteObservers;
std::vector<IOInterposeObserver*> mFSyncObservers;
std::vector<IOInterposeObserver*> mStatObservers;
std::vector<IOInterposeObserver*> mCloseObservers;
};
/**
@ -139,6 +142,11 @@ IOInterposeObserver::Operation IOInterposer::sObservedOperations =
// Decide which list of observers to inform
std::vector<IOInterposeObserver*>* observers = nullptr;
switch (aObservation.ObservedOperation()) {
case IOInterposeObserver::OpCreateOrOpen:
{
observers = &sObserverLists->mCreateObservers;
}
break;
case IOInterposeObserver::OpRead:
{
observers = &sObserverLists->mReadObservers;
@ -154,6 +162,16 @@ IOInterposeObserver::Operation IOInterposer::sObservedOperations =
observers = &sObserverLists->mFSyncObservers;
}
break;
case IOInterposeObserver::OpStat:
{
observers = &sObserverLists->mStatObservers;
}
break;
case IOInterposeObserver::OpClose:
{
observers = &sObserverLists->mCloseObservers;
}
break;
default:
{
// Invalid IO operation, see documentation comment for Report()
@ -186,6 +204,10 @@ IOInterposeObserver::Operation IOInterposer::sObservedOperations =
// You can register to observe multiple types of observations
// but you'll never be registered twice for the same observations.
if (aOp & IOInterposeObserver::OpCreateOrOpen &&
!VectorContains(sObserverLists->mCreateObservers, aObserver)) {
sObserverLists->mCreateObservers.push_back(aObserver);
}
if (aOp & IOInterposeObserver::OpRead &&
!VectorContains(sObserverLists->mReadObservers, aObserver)) {
sObserverLists->mReadObservers.push_back(aObserver);
@ -198,6 +220,14 @@ IOInterposeObserver::Operation IOInterposer::sObservedOperations =
!VectorContains(sObserverLists->mFSyncObservers, aObserver)) {
sObserverLists->mFSyncObservers.push_back(aObserver);
}
if (aOp & IOInterposeObserver::OpStat &&
!VectorContains(sObserverLists->mStatObservers, aObserver)) {
sObserverLists->mStatObservers.push_back(aObserver);
}
if (aOp & IOInterposeObserver::OpClose &&
!VectorContains(sObserverLists->mCloseObservers, aObserver)) {
sObserverLists->mCloseObservers.push_back(aObserver);
}
// Update field of observed operation with the operations that the new
// observer is observing.

View File

@ -21,11 +21,14 @@ public:
enum Operation
{
OpNone = 0,
OpRead = (1 << 0),
OpWrite = (1 << 1),
OpFSync = (1 << 2),
OpCreateOrOpen = (1 << 0),
OpRead = (1 << 1),
OpWrite = (1 << 2),
OpFSync = (1 << 3),
OpStat = (1 << 4),
OpClose = (1 << 5),
OpWriteFSync = (OpWrite | OpFSync),
OpAll = (OpRead | OpWrite | OpFSync)
OpAll = (OpCreateOrOpen | OpRead | OpWrite | OpFSync | OpStat | OpClose)
};
/** A representation of an I/O observation */

View File

@ -13,9 +13,12 @@ namespace {
using namespace mozilla;
/* Original IO methods */
PRCloseFN sCloseFn = nullptr;
PRReadFN sReadFn = nullptr;
PRWriteFN sWriteFn = nullptr;
PRFsyncFN sFSyncFn = nullptr;
PRFileInfoFN sFileInfoFn = nullptr;
PRFileInfo64FN sFileInfo64Fn = nullptr;
/**
* RAII class for timing the duration of an NSPR I/O call and reporting the
@ -49,6 +52,15 @@ private:
bool mShouldObserve;
};
PRStatus PR_CALLBACK interposedClose(PRFileDesc* aFd)
{
// If we don't have a valid original function pointer something is very wrong.
NS_ASSERTION(sCloseFn, "NSPR IO Interposing: sCloseFn is NULL");
NSPRIOAutoObservation timer(IOInterposeObserver::OpClose);
return sCloseFn(aFd);
}
int32_t PR_CALLBACK interposedRead(PRFileDesc* aFd, void* aBuf, int32_t aAmt)
{
// If we don't have a valid original function pointer something is very wrong.
@ -77,6 +89,24 @@ PRStatus PR_CALLBACK interposedFSync(PRFileDesc* aFd)
return sFSyncFn(aFd);
}
PRStatus PR_CALLBACK interposedFileInfo(PRFileDesc *aFd, PRFileInfo *aInfo)
{
// If we don't have a valid original function pointer something is very wrong.
NS_ASSERTION(sFileInfoFn, "NSPR IO Interposing: sFileInfoFn is NULL");
NSPRIOAutoObservation timer(IOInterposeObserver::OpStat);
return sFileInfoFn(aFd, aInfo);
}
PRStatus PR_CALLBACK interposedFileInfo64(PRFileDesc *aFd, PRFileInfo64 *aInfo)
{
// If we don't have a valid original function pointer something is very wrong.
NS_ASSERTION(sFileInfo64Fn, "NSPR IO Interposing: sFileInfo64Fn is NULL");
NSPRIOAutoObservation timer(IOInterposeObserver::OpStat);
return sFileInfo64Fn(aFd, aInfo);
}
} // anonymous namespace
namespace mozilla {
@ -84,7 +114,8 @@ namespace mozilla {
void InitNSPRIOInterposing()
{
// Check that we have not interposed any of the IO methods before
MOZ_ASSERT(!sReadFn && !sWriteFn && !sFSyncFn);
MOZ_ASSERT(!sCloseFn && !sReadFn && !sWriteFn && !sFSyncFn && !sFileInfoFn &&
!sFileInfo64Fn);
// We can't actually use this assertion because we initialize this code
// before XPCOM is initialized, so NS_IsMainThread() always returns false.
@ -101,22 +132,29 @@ void InitNSPRIOInterposing()
return;
}
// Store original read, write, sync functions
sReadFn = methods->read;
sWriteFn = methods->write;
sFSyncFn = methods->fsync;
// Store original functions
sCloseFn = methods->close;
sReadFn = methods->read;
sWriteFn = methods->write;
sFSyncFn = methods->fsync;
sFileInfoFn = methods->fileInfo;
sFileInfo64Fn = methods->fileInfo64;
// Overwrite with our interposed read, write, sync functions
methods->read = &interposedRead;
methods->write = &interposedWrite;
methods->fsync = &interposedFSync;
// Overwrite with our interposed functions
methods->close = &interposedClose;
methods->read = &interposedRead;
methods->write = &interposedWrite;
methods->fsync = &interposedFSync;
methods->fileInfo = &interposedFileInfo;
methods->fileInfo64 = &interposedFileInfo64;
}
void ClearNSPRIOInterposing()
{
// If we have already cleared IO interposing, or not initialized it this is
// actually bad.
MOZ_ASSERT(sReadFn && sWriteFn && sFSyncFn);
MOZ_ASSERT(sCloseFn && sReadFn && sWriteFn && sFSyncFn && sFileInfoFn &&
sFileInfo64Fn);
// Get IO methods from NSPR and const cast the structure so we can modify it.
PRIOMethods* methods = const_cast<PRIOMethods*>(PR_GetFileMethods());
@ -129,15 +167,22 @@ void ClearNSPRIOInterposing()
return;
}
// Restore original read, write, sync functions
methods->read = sReadFn;
methods->write = sWriteFn;
methods->fsync = sFSyncFn;
// Restore original functions
methods->close = sCloseFn;
methods->read = sReadFn;
methods->write = sWriteFn;
methods->fsync = sFSyncFn;
methods->fileInfo = sFileInfoFn;
methods->fileInfo64 = sFileInfo64Fn;
// Forget about original functions
sReadFn = nullptr;
sWriteFn = nullptr;
sFSyncFn = nullptr;
sCloseFn = nullptr;
sReadFn = nullptr;
sWriteFn = nullptr;
sFSyncFn = nullptr;
sFileInfoFn = nullptr;
sFileInfo64Fn = nullptr;
}
} // namespace mozilla

View File

@ -10,15 +10,34 @@ using namespace mozilla;
void ProfilerIOInterposeObserver::Observe(Observation& aObservation)
{
// TODO: The profile might want to take notice of non-main-thread IO, as
// well as noting what files or references causes the IO.
if (NS_IsMainThread()) {
const char* ops[] = {"none", "read", "write", "invalid", "fsync"};
ProfilerBacktrace* stack = profiler_get_backtrace();
IOMarkerPayload* markerPayload = new IOMarkerPayload(aObservation.Reference(),
aObservation.Start(),
aObservation.End(),
stack);
PROFILER_MARKER_PAYLOAD(ops[aObservation.ObservedOperation()], markerPayload);
const char* str = nullptr;
switch (aObservation.ObservedOperation()) {
case IOInterposeObserver::OpCreateOrOpen:
str = "create/open";
break;
case IOInterposeObserver::OpRead:
str = "read";
break;
case IOInterposeObserver::OpWrite:
str = "write";
break;
case IOInterposeObserver::OpFSync:
str = "fsync";
break;
case IOInterposeObserver::OpStat:
str = "stat";
break;
case IOInterposeObserver::OpClose:
str = "close";
break;
default:
return;
}
ProfilerBacktrace* stack = profiler_get_backtrace();
IOMarkerPayload* markerPayload = new IOMarkerPayload(aObservation.Reference(),
aObservation.Start(),
aObservation.End(),
stack);
PROFILER_MARKER_PAYLOAD(str, markerPayload);
}