Global Metrics
path: .metrics.halstead.bugs
old: 1.6610538565683188
new: 2.0811604723074724
path: .metrics.halstead.N2
old: 1010.0
new: 574.0
path: .metrics.halstead.effort
old: 351768.9098607888
new: 493333.07713167695
path: .metrics.halstead.vocabulary
old: 247.0
new: 189.0
path: .metrics.halstead.n1
old: 10.0
new: 28.0
path: .metrics.halstead.N1
old: 1067.0
new: 733.0
path: .metrics.halstead.difficulty
old: 21.30801687763713
new: 49.91304347826087
path: .metrics.halstead.n2
old: 237.0
new: 161.0
path: .metrics.halstead.length
old: 2077.0
new: 1307.0
path: .metrics.halstead.volume
old: 16508.758740001376
new: 9883.850848456945
path: .metrics.halstead.level
old: 0.04693069306930693
new: 0.020034843205574915
path: .metrics.halstead.time
old: 19542.717214488264
new: 27407.393173982055
path: .metrics.halstead.purity_ratio
old: 0.9161537943850558
new: 1.006031794333639
path: .metrics.halstead.estimated_program_length
old: 1902.851430937761
new: 1314.883555194066
path: .metrics.nom.functions
old: 0.0
new: 15.0
path: .metrics.nom.closures
old: 0.0
new: 2.0
path: .metrics.nom.total
old: 0.0
new: 17.0
path: .metrics.cognitive.average
old: null
new: 1.1764705882352942
path: .metrics.cognitive.sum
old: 0.0
new: 20.0
path: .metrics.mi.mi_original
old: 16.654019488795512
new: 14.057110188185035
path: .metrics.mi.mi_visual_studio
old: 9.739192683506149
new: 8.220532273792418
path: .metrics.mi.mi_sei
old: -40.522422291641625
new: -17.49561427264998
path: .metrics.nargs.average
old: null
new: 3.411764705882353
path: .metrics.nargs.sum
old: 0.0
new: 58.0
path: .metrics.loc.ploc
old: 484.0
new: 292.0
path: .metrics.loc.lloc
old: 0.0
new: 77.0
path: .metrics.loc.cloc
old: 12.0
new: 103.0
path: .metrics.loc.blank
old: 95.0
new: 62.0
path: .metrics.loc.sloc
old: 591.0
new: 457.0
path: .metrics.cyclomatic.sum
old: 2.0
new: 43.0
path: .metrics.cyclomatic.average
old: 1.0
new: 2.15
path: .metrics.nexits.average
old: null
new: 0.8823529411764706
path: .metrics.nexits.sum
old: 0.0
new: 15.0
Spaces Data
Minimal test - lines (33, 388)
path: .spaces[0].metrics.nom.closures
old: 0.0
new: 1.0
path: .spaces[0].metrics.nom.functions
old: 0.0
new: 13.0
path: .spaces[0].metrics.nom.total
old: 0.0
new: 14.0
path: .spaces[0].metrics.cyclomatic.average
old: 1.0
new: 2.3125
path: .spaces[0].metrics.cyclomatic.sum
old: 1.0
new: 37.0
path: .spaces[0].metrics.loc.cloc
old: 0.0
new: 81.0
path: .spaces[0].metrics.loc.sloc
old: 568.0
new: 356.0
path: .spaces[0].metrics.loc.ploc
old: 476.0
new: 227.0
path: .spaces[0].metrics.loc.blank
old: 92.0
new: 48.0
path: .spaces[0].metrics.loc.lloc
old: 0.0
new: 54.0
path: .spaces[0].metrics.nargs.average
old: null
new: 4.142857142857143
path: .spaces[0].metrics.nargs.sum
old: 0.0
new: 58.0
path: .spaces[0].metrics.mi.mi_original
old: 17.535912016900838
new: 20.539086189106413
path: .spaces[0].metrics.mi.mi_sei
old: -50.300058828360235
new: -8.625764022220217
path: .spaces[0].metrics.mi.mi_visual_studio
old: 10.254919308129145
new: 12.01116151409732
path: .spaces[0].metrics.nexits.sum
old: 0.0
new: 14.0
path: .spaces[0].metrics.nexits.average
old: null
new: 1.0
path: .spaces[0].metrics.cognitive.sum
old: 0.0
new: 18.0
path: .spaces[0].metrics.cognitive.average
old: null
new: 1.2857142857142858
path: .spaces[0].metrics.halstead.vocabulary
old: 246.0
new: 148.0
path: .spaces[0].metrics.halstead.estimated_program_length
old: 1893.523040598268
new: 963.4328092906352
path: .spaces[0].metrics.halstead.length
old: 2075.0
new: 1119.0
path: .spaces[0].metrics.halstead.effort
old: 351961.0876984651
new: 458361.5446619526
path: .spaces[0].metrics.halstead.n2
old: 236.0
new: 120.0
path: .spaces[0].metrics.halstead.purity_ratio
old: 0.9125412243847076
new: 0.8609765945403353
path: .spaces[0].metrics.halstead.time
old: 19553.39376102584
new: 25464.530258997365
path: .spaces[0].metrics.halstead.bugs
old: 1.6616587777173484
new: 1.9816070019897325
path: .spaces[0].metrics.halstead.level
old: 0.046825396825396826
new: 0.01760046934584922
path: .spaces[0].metrics.halstead.N2
old: 1008.0
new: 487.0
path: .spaces[0].metrics.halstead.n1
old: 10.0
new: 28.0
path: .spaces[0].metrics.halstead.N1
old: 1067.0
new: 632.0
path: .spaces[0].metrics.halstead.difficulty
old: 21.35593220338983
new: 56.81666666666667
path: .spaces[0].metrics.halstead.volume
old: 16480.71759857892
new: 8067.3783161387955
Code
namespace {
// Keep track of poisoned state. Notice that there is no reason to lock access
// to this variable as it's only changed in InitPoisonIOInterposer and
// ClearPoisonIOInterposer which may only be called on the main-thread when no
// other threads are running.
static bool sIOPoisoned = false;
/************************ Internal NT API Declarations ************************/
/*
* Function pointer declaration for internal NT routine to create/open files.
* For documentation on the NtCreateFile routine, see MSDN.
*/
typedef NTSTATUS(NTAPI* NtCreateFileFn)(
PHANDLE aFileHandle, ACCESS_MASK aDesiredAccess,
POBJECT_ATTRIBUTES aObjectAttributes, PIO_STATUS_BLOCK aIoStatusBlock,
PLARGE_INTEGER aAllocationSize, ULONG aFileAttributes, ULONG aShareAccess,
ULONG aCreateDisposition, ULONG aCreateOptions, PVOID aEaBuffer,
ULONG aEaLength);
/**
* Function pointer declaration for internal NT routine to read data from file.
* For documentation on the NtReadFile routine, see ZwReadFile on MSDN.
*/
typedef NTSTATUS(NTAPI* NtReadFileFn)(HANDLE aFileHandle, HANDLE aEvent,
PIO_APC_ROUTINE aApc, PVOID aApcCtx,
PIO_STATUS_BLOCK aIoStatus, PVOID aBuffer,
ULONG aLength, PLARGE_INTEGER aOffset,
PULONG aKey);
/**
* Function pointer declaration for internal NT routine to read data from file.
* No documentation exists, see wine sources for details.
*/
typedef NTSTATUS(NTAPI* NtReadFileScatterFn)(
HANDLE aFileHandle, HANDLE aEvent, PIO_APC_ROUTINE aApc, PVOID aApcCtx,
PIO_STATUS_BLOCK aIoStatus, FILE_SEGMENT_ELEMENT* aSegments, ULONG aLength,
PLARGE_INTEGER aOffset, PULONG aKey);
/**
* Function pointer declaration for internal NT routine to write data to file.
* For documentation on the NtWriteFile routine, see ZwWriteFile on MSDN.
*/
typedef NTSTATUS(NTAPI* NtWriteFileFn)(HANDLE aFileHandle, HANDLE aEvent,
PIO_APC_ROUTINE aApc, PVOID aApcCtx,
PIO_STATUS_BLOCK aIoStatus,
PVOID aBuffer, ULONG aLength,
PLARGE_INTEGER aOffset, PULONG aKey);
/**
* Function pointer declaration for internal NT routine to write data to file.
* No documentation exists, see wine sources for details.
*/
typedef NTSTATUS(NTAPI* NtWriteFileGatherFn)(
HANDLE aFileHandle, HANDLE aEvent, PIO_APC_ROUTINE aApc, PVOID aApcCtx,
PIO_STATUS_BLOCK aIoStatus, FILE_SEGMENT_ELEMENT* aSegments, ULONG aLength,
PLARGE_INTEGER aOffset, PULONG aKey);
/**
* Function pointer declaration for internal NT routine to flush to disk.
* For documentation on the NtFlushBuffersFile routine, see ZwFlushBuffersFile
* on MSDN.
*/
typedef NTSTATUS(NTAPI* NtFlushBuffersFileFn)(HANDLE aFileHandle,
PIO_STATUS_BLOCK aIoStatusBlock);
typedef struct _FILE_NETWORK_OPEN_INFORMATION* PFILE_NETWORK_OPEN_INFORMATION;
/**
* Function pointer delaration for internal NT routine to query file attributes.
* (equivalent to stat)
*/
typedef NTSTATUS(NTAPI* NtQueryFullAttributesFileFn)(
POBJECT_ATTRIBUTES aObjectAttributes,
PFILE_NETWORK_OPEN_INFORMATION aFileInformation);
/*************************** Auxiliary Declarations ***************************/
// Cache of filenames associated with handles.
// `static` to be shared between all calls to `Filename()`.
// This assumes handles are not reused, at least within a windows of 32
// handles.
// Profiling showed that during startup, around half of `Filename()` calls are
// resolved with the first entry (best case), and 32 entries cover >95% of
// cases, reducing the average `Filename()` cost by 5-10x.
using HandleToFilenameCache = mozilla::SmallArrayLRUCache;
static mozilla::UniquePtr sHandleToFilenameCache;
/**
* RAII class for timing the duration of an I/O call and reporting the result
* to the mozilla::IOInterposeObserver API.
*/
class WinIOAutoObservation : public mozilla::IOInterposeObserver::Observation {
public:
WinIOAutoObservation(mozilla::IOInterposeObserver::Operation aOp,
HANDLE aFileHandle, const LARGE_INTEGER* aOffset)
: mozilla::IOInterposeObserver::Observation(
aOp, sReference,
!mozilla::IsDebugFile(reinterpret_cast(aFileHandle))),
mFileHandle(aFileHandle),
mFileHandleType(GetFileType(aFileHandle)),
mHasQueriedFilename(false) {
if (mShouldReport) {
mOffset.QuadPart = aOffset ? aOffset->QuadPart : 0;
}
}
WinIOAutoObservation(mozilla::IOInterposeObserver::Operation aOp,
nsAString& aFilename)
: mozilla::IOInterposeObserver::Observation(aOp, sReference),
mFileHandle(nullptr),
mFileHandleType(FILE_TYPE_UNKNOWN),
mHasQueriedFilename(false) {
if (mShouldReport) {
nsAutoString dosPath;
if (mozilla::NtPathToDosPath(aFilename, dosPath)) {
mFilename = dosPath;
} else {
// If we can't get a dosPath, what we have is better than nothing.
mFilename = aFilename;
}
mHasQueriedFilename = true;
mOffset.QuadPart = 0;
}
}
void SetHandle(HANDLE aFileHandle) {
mFileHandle = aFileHandle;
if (aFileHandle) {
// Note: `GetFileType()` is fast enough that we don't need to cache it.
mFileHandleType = GetFileType(aFileHandle);
if (mHasQueriedFilename) {
// `mHasQueriedFilename` indicates we already have a filename, add it to
// the cache with the now-known handle.
sHandleToFilenameCache->Add(aFileHandle, mFilename);
}
}
}
const char* FileType() const override;
void Filename(nsAString& aFilename) override;
~WinIOAutoObservation() { Report(); }
private:
HANDLE mFileHandle;
DWORD mFileHandleType;
LARGE_INTEGER mOffset;
bool mHasQueriedFilename;
nsString mFilename;
static const char* sReference;
};
const char* WinIOAutoObservation::sReference = "PoisonIOInterposer";
// Get filename for this observation
void WinIOAutoObservation::Filename(nsAString& aFilename) {
// If mHasQueriedFilename is true, then filename is already stored in
// mFilename
if (mHasQueriedFilename) {
aFilename = mFilename;
return;
}
if (mFileHandle) {
mFilename = sHandleToFilenameCache->FetchOrAdd(mFileHandle, [&]() {
nsString filename;
if (!mozilla::HandleToFilename(mFileHandle, mOffset, filename)) {
// HandleToFilename could fail (return false) but still have added
// something to `filename`, so it should be cleared in this case.
filename.Truncate();
}
return filename;
});
}
mHasQueriedFilename = true;
aFilename = mFilename;
}
const char* WinIOAutoObservation::FileType() const {
if (mFileHandle) {
switch (mFileHandleType) {
case FILE_TYPE_CHAR:
return "Char";
case FILE_TYPE_DISK:
return "File";
case FILE_TYPE_PIPE:
return "Pipe";
case FILE_TYPE_REMOTE:
return "Remote";
case FILE_TYPE_UNKNOWN:
default:
break;
}
}
// Fallback to base class default implementation.
return mozilla::IOInterposeObserver::Observation::FileType();
}
/*************************** IO Interposing Methods ***************************/
// Function pointers to original functions
static mozilla::WindowsDllInterceptor::FuncHookType
gOriginalNtCreateFile;
static mozilla::WindowsDllInterceptor::FuncHookType
gOriginalNtReadFile;
static mozilla::WindowsDllInterceptor::FuncHookType
gOriginalNtReadFileScatter;
static mozilla::WindowsDllInterceptor::FuncHookType
gOriginalNtWriteFile;
static mozilla::WindowsDllInterceptor::FuncHookType
gOriginalNtWriteFileGather;
static mozilla::WindowsDllInterceptor::FuncHookType
gOriginalNtFlushBuffersFile;
static mozilla::WindowsDllInterceptor::FuncHookType
gOriginalNtQueryFullAttributesFile;
static NTSTATUS NTAPI InterposedNtCreateFile(
PHANDLE aFileHandle, ACCESS_MASK aDesiredAccess,
POBJECT_ATTRIBUTES aObjectAttributes, PIO_STATUS_BLOCK aIoStatusBlock,
PLARGE_INTEGER aAllocationSize, ULONG aFileAttributes, ULONG aShareAccess,
ULONG aCreateDisposition, ULONG aCreateOptions, PVOID aEaBuffer,
ULONG aEaLength) {
// Report IO
const wchar_t* buf =
aObjectAttributes ? aObjectAttributes->ObjectName->Buffer : L"";
uint32_t len = aObjectAttributes
? aObjectAttributes->ObjectName->Length / sizeof(WCHAR)
: 0;
nsDependentSubstring filename(buf, len);
WinIOAutoObservation timer(mozilla::IOInterposeObserver::OpCreateOrOpen,
filename);
// Something is badly wrong if this function is undefined
MOZ_ASSERT(gOriginalNtCreateFile);
// Execute original function
NTSTATUS status = gOriginalNtCreateFile(
aFileHandle, aDesiredAccess, aObjectAttributes, aIoStatusBlock,
aAllocationSize, aFileAttributes, aShareAccess, aCreateDisposition,
aCreateOptions, aEaBuffer, aEaLength);
if (NT_SUCCESS(status) && aFileHandle) {
timer.SetHandle(*aFileHandle);
}
return status;
}
static NTSTATUS NTAPI InterposedNtReadFile(HANDLE aFileHandle, HANDLE aEvent,
PIO_APC_ROUTINE aApc, PVOID aApcCtx,
PIO_STATUS_BLOCK aIoStatus,
PVOID aBuffer, ULONG aLength,
PLARGE_INTEGER aOffset,
PULONG aKey) {
// Report IO
WinIOAutoObservation timer(mozilla::IOInterposeObserver::OpRead, aFileHandle,
aOffset);
// Something is badly wrong if this function is undefined
MOZ_ASSERT(gOriginalNtReadFile);
// Execute original function
return gOriginalNtReadFile(aFileHandle, aEvent, aApc, aApcCtx, aIoStatus,
aBuffer, aLength, aOffset, aKey);
}
static NTSTATUS NTAPI InterposedNtReadFileScatter(
HANDLE aFileHandle, HANDLE aEvent, PIO_APC_ROUTINE aApc, PVOID aApcCtx,
PIO_STATUS_BLOCK aIoStatus, FILE_SEGMENT_ELEMENT* aSegments, ULONG aLength,
PLARGE_INTEGER aOffset, PULONG aKey) {
// Report IO
WinIOAutoObservation timer(mozilla::IOInterposeObserver::OpRead, aFileHandle,
aOffset);
// Something is badly wrong if this function is undefined
MOZ_ASSERT(gOriginalNtReadFileScatter);
// Execute original function
return gOriginalNtReadFileScatter(aFileHandle, aEvent, aApc, aApcCtx,
aIoStatus, aSegments, aLength, aOffset,
aKey);
}
// Interposed NtWriteFile function
static NTSTATUS NTAPI InterposedNtWriteFile(HANDLE aFileHandle, HANDLE aEvent,
PIO_APC_ROUTINE aApc, PVOID aApcCtx,
PIO_STATUS_BLOCK aIoStatus,
PVOID aBuffer, ULONG aLength,
PLARGE_INTEGER aOffset,
PULONG aKey) {
// Report IO
WinIOAutoObservation timer(mozilla::IOInterposeObserver::OpWrite, aFileHandle,
aOffset);
// Something is badly wrong if this function is undefined
MOZ_ASSERT(gOriginalNtWriteFile);
// Execute original function
return gOriginalNtWriteFile(aFileHandle, aEvent, aApc, aApcCtx, aIoStatus,
aBuffer, aLength, aOffset, aKey);
}
// Interposed NtWriteFileGather function
static NTSTATUS NTAPI InterposedNtWriteFileGather(
HANDLE aFileHandle, HANDLE aEvent, PIO_APC_ROUTINE aApc, PVOID aApcCtx,
PIO_STATUS_BLOCK aIoStatus, FILE_SEGMENT_ELEMENT* aSegments, ULONG aLength,
PLARGE_INTEGER aOffset, PULONG aKey) {
// Report IO
WinIOAutoObservation timer(mozilla::IOInterposeObserver::OpWrite, aFileHandle,
aOffset);
// Something is badly wrong if this function is undefined
MOZ_ASSERT(gOriginalNtWriteFileGather);
// Execute original function
return gOriginalNtWriteFileGather(aFileHandle, aEvent, aApc, aApcCtx,
aIoStatus, aSegments, aLength, aOffset,
aKey);
}
static NTSTATUS NTAPI InterposedNtFlushBuffersFile(
HANDLE aFileHandle, PIO_STATUS_BLOCK aIoStatusBlock) {
// Report IO
WinIOAutoObservation timer(mozilla::IOInterposeObserver::OpFSync, aFileHandle,
nullptr);
// Something is badly wrong if this function is undefined
MOZ_ASSERT(gOriginalNtFlushBuffersFile);
// Execute original function
return gOriginalNtFlushBuffersFile(aFileHandle, aIoStatusBlock);
}
static NTSTATUS NTAPI InterposedNtQueryFullAttributesFile(
POBJECT_ATTRIBUTES aObjectAttributes,
PFILE_NETWORK_OPEN_INFORMATION aFileInformation) {
// Report IO
const wchar_t* buf =
aObjectAttributes ? aObjectAttributes->ObjectName->Buffer : L"";
uint32_t len = aObjectAttributes
? aObjectAttributes->ObjectName->Length / sizeof(WCHAR)
: 0;
nsDependentSubstring filename(buf, len);
WinIOAutoObservation timer(mozilla::IOInterposeObserver::OpStat, filename);
// Something is badly wrong if this function is undefined
MOZ_ASSERT(gOriginalNtQueryFullAttributesFile);
// Execute original function
return gOriginalNtQueryFullAttributesFile(aObjectAttributes,
aFileInformation);
}
} // namespace