214 lines
5.5 KiB
C
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;
|
|
}
|