xbox-kernel/private/ntos/xprof/xbbt.cpp
2020-09-30 17:17:25 +02:00

473 lines
13 KiB
C++

/*++
Copyright (c) Microsoft Corporation. All rights reserved.
Module Name:
xbbt.cpp
Abstract:
BBT instrumented binary runtime support functions
--*/
#ifdef _XBOX_ENABLE_PROFILING
#include "ntos.h"
#include "xprofp.h"
#include "xbbt.h"
#ifdef _XBOX_ENABLE_BBT
IRTCLIENTINFO* IrtClientInfo; // Client-init information
LONG IrtSweepingFlag; // Whether we're currently sweep data to disk
KTIMER IrtTimer; // Background timer
KDPC IrtTimerDpc; // and associated DPC
#define IsIrtClientInited() (IrtClientInfo != NULL)
#define IrtAssert(cond) do { if (!(cond)) KeBugCheckEx(0, __LINE__, 'IRTS', 0, 0); } while(0)
//
// Allocate space for an additional TLE structure
//
TLE* PtleCreate(IDFHDR* pidfhdr, TLH* ptlh, DWORD iInterval)
{
DWORD ibtleNew = InterlockedExchangeAdd( (PLONG)&pidfhdr->cb, sizeof(TLE) );
IrtAssert(ibtleNew + sizeof(TLE) <= pidfhdr->cbMax);
// Fill in the TLE
TLE* ptle = (TLE*) ((BYTE*) pidfhdr + ibtleNew);
ptle->ibtleNext = ptlh->ibtleFirst;
ptle->tob.iInterval = (iInterval & 0xFFFFFF80);
// Link it into the list
ptlh->ibtleFirst = ibtleNew;
return ptle;
}
//
// Sweep TOB data
//
#define PtlhFromIfun(pidfhdr, ifun) \
((TLH*) ((BYTE*) pidfhdr + pidfhdr->ibrgtlhCode) + ifun)
#define COPYTOBBITS() { \
ptlh = PtlhFromIfun(pidfhdr, i); \
ptlh->qwTOBFanin++; \
ibtle = ptlh->ibtleFirst; \
if (ibtle != 0) { \
ptle = (TLE*) ((BYTE*) pidfhdr + ibtle); \
dwTimeSlot = iIntervalCur - ptle->tob.iInterval; \
if (dwTimeSlot < cdwTob * cbitDw) { \
ptle->tob.rgdwBit[dwTimeSlot>>5] |= 1 << (dwTimeSlot & 0x1F); \
continue; \
} \
} \
ptle = PtleCreate(pidfhdr, ptlh, iIntervalCur); \
dwTimeSlot = iIntervalCur - ptle->tob.iInterval; \
ptle->tob.rgdwBit[dwTimeSlot>>5] |= 1 << (dwTimeSlot & 0x1F); \
}
VOID
IrtCopyCurrentTOBBits(IRTCLIENTINFO* pci)
{
// Figure out the current interval index
IDFHDR* pidfhdr = pci->pidfhdr;
const IRTP* pirtp = pci->pirtp;
DWORD iIntervalCur = pci->tobSweepTime / pirtp->cmsTimer;
pci->tobSweepTime %= pirtp->cmsTimer;
pci->intervalCount += iIntervalCur;
pidfhdr->iIntervalCur = iIntervalCur = pci->intervalCount - 1;
BYTE* rgTobBytes = pci->rgTobCounts;
DWORD* rgCounts = pci->rgCounts;
DWORDLONG* pqwDest = (DWORDLONG*) ((BYTE*) pidfhdr + pidfhdr->ibrgqwCount);
DWORD i, n, ibtle, dwTimeSlot;
TLH* ptlh;
TLE* ptle;
n = pidfhdr->ctlhCode;
if (pidfhdr->tov == tovCTO) {
// Make tobs from the counts
// for each TOB
for (i=0; i < n; i++) {
// tob bytes are intialized to 1 and set to 0 when hit.
if (rgCounts[i]) {
// this byte was hit in the last interval - reset it for the next interval
// Save counts too!
pqwDest[i] += rgCounts[i];
rgCounts[i] = 0;
COPYTOBBITS();
}
}
} else {
IrtAssert((pidfhdr->tov & tov4Mask) == 0);
// for each TOB
for (i=0; i < n; i++) {
// tob bytes are intialized to 1 and set to 0 when hit.
if (rgTobBytes[i] == 0) {
//this byte was hit in the last interval - reset it for the next interval
rgTobBytes[i] = 1;
COPYTOBBITS();
}
}
}
}
//
// Sweep static edge counts
//
VOID IrtCopyCounts(IRTCLIENTINFO* pci)
{
DWORD* srcCounts = pci->rgCounts;
IDFHDR* pidfhdr = pci->pidfhdr;
DWORDLONG* dstCounts = (DWORDLONG*) ((BYTE*) pidfhdr + pidfhdr->ibrgqwCount);
DWORD i, n;
n = pidfhdr->cqwCount;
for (i=0; i < n; i++) {
*dstCounts++ += *srcCounts;
*srcCounts++ = 0;
}
if (pci->rgSeqNums) {
n = pidfhdr->ctlhCode;
for (i=0; i < n; i++) {
TLH* ptlh = PtlhFromIfun(pidfhdr, i);
ptlh->iSequenceInit = pci->rgSeqNums[i+1];
}
pidfhdr->iSequence = pci->rgSeqNums[0];
}
pci->copyCountsTime = 0;
}
VOID
IrtTimerProc(
PKDPC dpc,
VOID* context,
VOID* param1,
VOID* param2
)
/*++
Routine Description:
IRT runtime background sweeping timer procedure
Arguments:
See DDK documentation
Return Value:
NONE
Notes:
This function runs at dispatch level.
--*/
{
IRTCLIENTINFO* pci = IrtClientInfo;
IrtAssert(IsIrtClientInited());
// Check if we need to do a sweep yet. Note that if we're currently
// in the process of writing IDF data to disk, then skip this round.
pci->currentTime += IRT_SWEEP_INTERVAL;
pci->tobSweepTime += IRT_SWEEP_INTERVAL;
pci->copyCountsTime += IRT_SWEEP_INTERVAL;
if (IrtSweepingFlag) return;
if (pci->tobSweepTime >= pci->pirtp->cmsTimer) {
IrtCopyCurrentTOBBits(pci);
}
// Call IrtCopyCounts once every 4 seconds
if (pci->copyCountsTime >= 4000) {
IrtCopyCounts(pci);
}
}
//
// Initialize IDF header information
//
VOID IrtInitializeIdfHeader(IRTCLIENTINFO* pci)
{
DWORD cb = CbRoundToPage(sizeof(*pci));
IDFHDR* pidfhdr = (IDFHDR*) ((BYTE*) pci + cb);
pci->pidfhdr = pidfhdr;
DWORD cbMax = IRT_BUFFER_SIZE - cb;
memset(pidfhdr, 0, cbMax);
const IRTP* pirtp = pci->pirtp;
pidfhdr->dwSignature = dwIdfSignature;
pidfhdr->dwVersion = dwIdfVerCurrent;
pidfhdr->dwId = pirtp->dwId;
pidfhdr->dwGeneration = pirtp->dwGeneration;
pidfhdr->tov = pirtp->tov;
pidfhdr->fSweepable = pirtp->fSweepable ? 1 : 0;
// Calculate the minimum size for the shared memory region
cb = CbRound(sizeof(IDFHDR), sizeof(DWORDLONG));
if (pirtp->szIdfKeyPath) {
pidfhdr->ibszIdfKeyPath = cb;
cb += strlen(pirtp->szIdfKeyPath) + 1;
}
cb = CbRound(cb, sizeof(DWORDLONG));
pidfhdr->cqwCount = pirtp->cbrc;
pidfhdr->ibrgqwCount = cb;
cb += pidfhdr->cqwCount * sizeof(DWORDLONG);
// Save space for indirect branches
pidfhdr->ciblh = pirtp->cibd ? pirtp->cibd : pirtp->cibs;
pidfhdr->ibrgiblh = cb;
cb += pidfhdr->ciblh * sizeof(IBLH);
cb = CbRound(cb, sizeof(DWORDLONG));
IrtAssert(pirtp->cmsTimer != 0);
if (pirtp->cfun != 0) {
pidfhdr->ctlhCode = pirtp->cfun;
pidfhdr->ibrgtlhCode = cb;
cb += pidfhdr->ctlhCode * sizeof(TLH);
cb = CbRound(cb, sizeof(DWORDLONG));
}
if (pirtp->crsc != 0) {
pidfhdr->ctlhResource = pirtp->crsc;
pidfhdr->ibrgtlhResource = cb;
cb += pidfhdr->ctlhResource * sizeof(TLH);
cb = CbRound(cb, sizeof(DWORDLONG));
}
pidfhdr->cb = cb;
// Calculate the minimum size for the shared memory region
DWORD cbMin = cb + sizeof(THD);
cbMin += pidfhdr->ciblh * sizeof(IBLE);
if (pirtp->cfun != 0)
cbMin += pidfhdr->ctlhCode * sizeof(TLE);
if (pirtp->crsc != 0)
cbMin += pidfhdr->ctlhResource * sizeof(TLE);
IrtAssert(cbMin <= cbMax);
pidfhdr->cbMax = cbMax;
pidfhdr->cbAllocated = cbMax;
pidfhdr->cmsTimer = pirtp->cmsTimer;
pidfhdr->cpsActive = 1;
pidfhdr->ifunEndBoot = pirtp->ifunEndBoot;
pidfhdr->iSequenceEndBoot = (DWORD) -1;
pidfhdr->iIntervalEndBoot = (DWORD) -1;
pidfhdr->fFastRT = (pci->rgCounts != NULL);
pidfhdr->iIntervalStart = pci->intervalCount;
pidfhdr->cmsStartTime = pci->currentTime;
KeQuerySystemTime((LARGE_INTEGER*) &pidfhdr->qwStartTime);
if (pirtp->szIdfKeyPath) {
strcpy((char*) pidfhdr + pidfhdr->ibszIdfKeyPath, pirtp->szIdfKeyPath);
}
}
DWORD IrtClientNewIndFast(const IRTP* pirtp, DWORD rvaDest, long iIndSrc)
{
if (IrtSweepingFlag) return 0;
IRTCLIENTINFO* pci = IrtClientInfo;
IDFHDR* pidfhdr = pci->pidfhdr;
IBLH* rgiblh = (IBLH*) ((BYTE*) pidfhdr + pidfhdr->ibrgiblh);
IBLH* piblh = rgiblh + iIndSrc;
DWORD* pibible = &piblh->ibibleFirst;
// Allocate a new ible
DWORD ibible = InterlockedExchangeAdd((PLONG)&pidfhdr->cb, sizeof(IBLE));
IrtAssert(ibible + sizeof(IBLE) <= pidfhdr->cbMax);
IBLE *pible = (IBLE *) ((BYTE *) pidfhdr + ibible);
// Fill in the IBLE
pible->blkidSrc = rvaDest;
pible->qwCount = 0;
// Atomically add to the front of the list
DWORD ibibleFirst;
do
{
ibibleFirst = piblh->ibibleFirst;
pible->ibibleNext = ibibleFirst;
}
while (ibibleFirst != InterlockedCompareExchange((PLONG)&piblh->ibibleFirst, ibible, ibibleFirst ));
InterlockedIncrement((PLONG)&pidfhdr->cible);
return ibible;
}
//
// This function is called when a BBT instrumented binary is run.
// We assume this function is only called once.
//
extern "C" VOID
IrtClientInitFast(
const IRTP* pirtp,
DWORD* rgCounts,
BYTE* rgTobCounts,
FASTRTPARAM* pfrtp,
DWORD* rgSeqNums,
FASTINDDATA* pfid,
VOID* unused
)
{
if (IsIrtClientInited()) return;
// Allocate the necessary memory buffers
IrtSweepingFlag = 0;
IrtClientInfo = (IRTCLIENTINFO*) MmAllocateSystemMemory(IRT_BUFFER_SIZE, PAGE_READWRITE);
IrtAssert(IrtClientInfo != NULL);
// Remember the client init parameters
memset(IrtClientInfo, 0, sizeof(IRTCLIENTINFO));
IrtClientInfo->pirtp = pirtp;
IrtClientInfo->rgCounts = rgCounts;
IrtClientInfo->rgTobCounts = rgTobCounts;
IrtClientInfo->rgSeqNums = rgSeqNums;
// Initialize IDF header information
IrtInitializeIdfHeader(IrtClientInfo);
if (pfid) {
IBLH* rgiblh = (IBLH*) ((BYTE*) IrtClientInfo->pidfhdr + IrtClientInfo->pidfhdr->ibrgiblh);
pfid->rgiblh = (DWORD_PTR) rgiblh;
pfid->negaddrbase = -(LONGLONG) pirtp->pvImageBase;
pfid->pidfhdr = (DWORD_PTR) IrtClientInfo->pidfhdr;
pfid->pfnInd = (DWORD_PTR) IrtClientNewIndFast;
}
// Start the background timer
LARGE_INTEGER dueTime;
dueTime.QuadPart = -10000*IRT_SWEEP_INTERVAL;
KeInitializeDpc(&IrtTimerDpc, IrtTimerProc, NULL);
KeInitializeTimer(&IrtTimer);
KeSetTimerEx(&IrtTimer, dueTime, IRT_SWEEP_INTERVAL, &IrtTimerDpc);
}
//
// Sweep the BBT data into a disk file
//
NTSTATUS IrtSweep(HANDLE file)
{
NTSTATUS status = STATUS_INVALID_PARAMETER;
//
// Make sure the client has been initialized
// and we're not sweeping at the moment
//
if (!IsIrtClientInited() ||
InterlockedCompareExchange(&IrtSweepingFlag, 1, 0) != 0) {
status = STATUS_INVALID_PARAMETER;
} else {
// Sweep static edge counts
IrtCopyCounts(IrtClientInfo);
// Write the in-memory IDF data out to the disk
IO_STATUS_BLOCK iostatusBlock;
FILE_END_OF_FILE_INFORMATION endOfFile;
FILE_ALLOCATION_INFORMATION allocation;
IDFHDR* pidfhdr = IrtClientInfo->pidfhdr;
DWORD count = pidfhdr->cb;
status = NtWriteFile(
file,
NULL,
NULL,
NULL,
&iostatusBlock,
pidfhdr,
(count + 511) & ~511,
NULL);
// Set file size
if (NT_SUCCESS(status)) {
endOfFile.EndOfFile.QuadPart = count;
status = NtSetInformationFile(
file,
&iostatusBlock,
&endOfFile,
sizeof(endOfFile),
FileEndOfFileInformation);
}
if (NT_SUCCESS(status)) {
allocation.AllocationSize.QuadPart = count;
status = NtSetInformationFile(
file,
&iostatusBlock,
&allocation,
sizeof(allocation),
FileAllocationInformation);
}
// Clean up the in-memory data
IrtInitializeIdfHeader(IrtClientInfo);
IrtSweepingFlag = 0;
}
return status;
}
#else // !_XBOX_ENABLE_BBT
//
// When BBTBUILD is not enabled, just stub out these two functions
//
extern "C" VOID
IrtClientInitFast(
const IRTP* pirtp,
DWORD* rgCounts,
BYTE* rgTobCounts,
FASTRTPARAM* pfrtp,
DWORD* rgSeqNums,
VOID* unused
) {}
NTSTATUS IrtSweep(HANDLE file) { return STATUS_NOT_IMPLEMENTED; }
#endif // !_XBOX_ENABLE_BBT
#endif // _XBOX_ENABLE_PROFILING