mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-24 11:27:49 +00:00
Bug 888534: Add support for Create, Stat and Close operations to IOInterposer. r=jonasfj
--HG-- extra : rebase_source : 6b7e75ae35e3250383f3990dce2680df44f4ec68
This commit is contained in:
parent
18fe7ab8b2
commit
44e3459f45
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user