xbox-kernel/private/ntos/dm/xbdm/dmprofil.c
2020-09-30 17:17:25 +02:00

214 lines
5.5 KiB
C

/*
*
* dmprofil.c
*
* Profile objects
*
*/
#include "dmp.h"
// Profile object
typedef struct _POB {
LIST_ENTRY le;
ULONG ulNext;
int iulInterval;
PDMPROFILE_HANDLER pfn;
} POB, *PPOB;
void DeletePob(PVOID);
extern KEVENT kevtNull;
OBJECT_TYPE obtPob = { DmAllocatePoolWithTag, DmFreePool, NULL, DeletePob, NULL, &kevtNull, 'opmd' };
LIST_ENTRY lePobHead = { &lePobHead, &lePobHead };
BOOL g_fResetInterval;
ULONG ulNextProfInt;
ULONG ulCurProfBase;
ULONG rgulIntervalTable[] =
{
1221,
2441,
4883,
9766,
19531,
39063,
78125,
156250,
312500,
625000,
1250000,
2500000,
5000000,
5000000 | 0x80000000
};
void QueuePob(PPOB ppob)
{
ULONG ulNext;
PLIST_ENTRY ple;
PPOB ppobBefore;
BOOL fStart;
/* Dequeue if already on the list */
_asm cli
if(ppob->le.Flink) {
RemoveEntryList(&ppob->le);
}
_asm sti
/* Start the profile interrupt or reset the profile interval if
* necessary */
ulNext = 1 << ppob->iulInterval;
fStart = ulCurProfBase == 0;
if(fStart || ulNext < ulCurProfBase) {
g_dmi.HalProfileIntervalRoutine(rgulIntervalTable[ppob->iulInterval]);
ulCurProfBase = ulNext;
if(fStart)
g_dmi.HalStartProfileRoutine(0);
}
/* Now set the time to fire and queue */
_asm cli
ppob->ulNext = ulNextProfInt + ulNext;
ple = lePobHead.Flink;
while(ple != &lePobHead) {
ppobBefore = CONTAINING_RECORD(ple, POB, le);
if((int)(ppob->ulNext - ppobBefore->ulNext) < 0)
/* Here's where we insert */
break;
ple = ple->Flink;
}
/* ple now points to the following entry or to the list head if we go at
* the end */
ppob->le.Flink = ple;
ppob->le.Blink = ple->Blink;
ple->Blink->Flink = &ppob->le;
ple->Blink = &ppob->le;
_asm sti
}
void DeletePob(PVOID pv)
{
PPOB ppob = (PPOB)pv;
KIRQL irqlSav = KeRaiseIrqlToDpcLevel();
/* Dequeue this guy */
_asm {
pushfd
cli
}
RemoveEntryList(&ppob->le);
_asm popfd
/* If the list is now empty, end the interrupt */
if(lePobHead.Flink == &lePobHead) {
g_dmi.HalStopProfileRoutine(0);
ulCurProfBase = 0;
} else if(1 << ppob->iulInterval == ulCurProfBase) {
/* May need to change the interval, but we need to wait until the next
* scheduled interrupt is sent so we keep the interrupt chain timed
* correctly */
PLIST_ENTRY ple;
_asm {
pushfd
cli
}
g_fResetInterval = 32;
for(ple = lePobHead.Flink; ple != &lePobHead; ple = ple->Flink) {
ppob = CONTAINING_RECORD(ple, POB, le);
if(ppob->iulInterval < g_fResetInterval)
g_fResetInterval = ppob->iulInterval + 1;
}
_asm popfd
}
KeLowerIrql(irqlSav);
}
HRESULT DmStartProfile(PHANDLE ph, ULONG ulInterval, PDMPROFILE_HANDLER pfn)
{
PPOB ppob;
NTSTATUS st;
int iul;
st = ObCreateObject(&obtPob, NULL, sizeof(POB), &ppob);
if(!NT_SUCCESS(st))
return HrFromStatus(st, E_FAIL);
RtlZeroMemory(ppob, sizeof *ppob);
/* Make sure we can get a handle for this guy */
st = ObOpenObjectByPointer(ppob, &obtPob, ph);
if(!NT_SUCCESS(st)) {
ObDereferenceObject(ppob);
return HrFromStatus(st, E_FAIL);
}
/* Find an interval to fire on */
ulInterval &= 0x7fffffff;
for(iul = 0; (int)rgulIntervalTable[iul] >= 0; ++iul) {
if(ulInterval <= rgulIntervalTable[iul])
break;
}
if(iul) {
/* See whether greater or smaller interval is closest */
if(rgulIntervalTable[iul] - ulInterval >
ulInterval - rgulIntervalTable[iul - 1])
--iul;
}
if((int)rgulIntervalTable[iul] < 0)
--iul;
ppob->iulInterval = iul;
ppob->pfn = pfn;
/* Queue the object and return */
ObDereferenceObject(ppob);
QueuePob(ppob);
return XBDM_NOERR;
}
HRESULT DmStopProfile(HANDLE h)
{
PPOB ppob;
NTSTATUS st;
/* If we're at raised irql (like inside the ISR), this isn't going to
* work */
if(KeGetCurrentIrql() > DISPATCH_LEVEL)
return E_FAIL;
st = NtClose(h);
return NT_SUCCESS(st) ? XBDM_NOERR : HrFromStatus(st, E_FAIL);
}
void ProfInt(PKTRAP_FRAME ptf)
{
DMN_PROFINT dmpi;
PLIST_ENTRY ple;
BOOL fReset;
dmpi.SegCs = (USHORT)ptf->SegCs;
dmpi.EFlags = ptf->EFlags;
dmpi.Eip = ptf->Eip;
/* Walk the list of installed interrupt handlers and call anybody who's
* due */
ple = lePobHead.Flink;
while(ple != &lePobHead) {
PPOB ppob = CONTAINING_RECORD(ple, POB, le);
if((int)(ppob->ulNext - ulNextProfInt) > 0)
/* Nobody else due */
break;
ple = ple->Flink;
if(ppob->ulNext == ulNextProfInt) {
if(g_fResetInterval) {
g_dmi.HalProfileIntervalRoutine(rgulIntervalTable[g_fResetInterval -
1]);
ulCurProfBase = 1 << (g_fResetInterval - 1);
g_fResetInterval = 0;
}
ppob->pfn(&dmpi);
QueuePob(ppob);
}
}
/* Update our interrupt time */
ulNextProfInt += ulCurProfBase;
}