917 lines
28 KiB
NASM
917 lines
28 KiB
NASM
title "Interval Clock Interrupt"
|
|
|
|
|
|
; Copyright (c) 1989 Microsoft Corporation
|
|
|
|
; Module Name:
|
|
|
|
; clockint.asm
|
|
|
|
; Abstract:
|
|
|
|
; This module implements the code necessary to field and process the
|
|
; interval clock interrupt.
|
|
|
|
; Author:
|
|
|
|
; Shie-Lin Tzong (shielint) 12-Jan-1990
|
|
|
|
; Environment:
|
|
|
|
; Kernel mode only.
|
|
|
|
; Revision History:
|
|
|
|
; bryanwi 20-Sep-90
|
|
|
|
; Add KiSetProfileInterval, KiStartProfileInterrupt,
|
|
; KiStopProfileInterrupt procedures.
|
|
; KiProfileInterrupt ISR.
|
|
; KiProfileList, KiProfileLock are delcared here.
|
|
|
|
; shielint 10-Dec-90
|
|
; Add performance counter support.
|
|
; Move system clock to irq8, ie we now use RTC to generate system
|
|
; clock. Performance count and Profile use timer 1 counter 0.
|
|
; The interval of the irq0 interrupt can be changed by
|
|
; KiSetProfileInterval. Performance counter does not care about the
|
|
; interval of the interrupt as long as it knows the rollover count.
|
|
; Note: Currently I implemented 1 performance counter for the whole
|
|
; i386 NT. It works on UP and SystemPro.
|
|
|
|
|
|
|
|
.386p
|
|
.xlist
|
|
KERNELONLY equ 1
|
|
include ks386.inc
|
|
include callconv.inc ; calling convention macros
|
|
include i386\kimacro.inc
|
|
include mac386.inc
|
|
.list
|
|
|
|
EXTRNP Kei386EoiHelper
|
|
EXTRNP HalRequestSoftwareInterrupt,1,IMPORT,FASTCALL
|
|
EXTRNP _HalEndSystemInterrupt,2,IMPORT
|
|
extrn _KeTimeIncrement:DWORD
|
|
extrn _KeMaximumIncrement:DWORD
|
|
extrn _KeTickCount:DWORD
|
|
extrn _KeTimeAdjustment:DWORD
|
|
extrn _KiAdjustDpcThreshold:DWORD
|
|
extrn _KiIdealDpcRate:DWORD
|
|
extrn _KiMaximumDpcQueueDepth:DWORD
|
|
extrn _KiTickOffset:DWORD
|
|
extrn _KiTimerTableListHead:DWORD
|
|
extrn _KiTimerExpireDpc:DWORD
|
|
extrn _KiTimeUpdateNotifyRoutine:DWORD
|
|
extrn _KiProfileListHead:DWORD
|
|
extrn _KiProfileLock:DWORD
|
|
extrn _KiProfileInterval:DWORD
|
|
extrn _KdDebuggerEnabled:BYTE
|
|
EXTRNP _DbgBreakPoint
|
|
EXTRNP _DbgBreakPointWithStatus,1
|
|
EXTRNP _KdPollBreakIn
|
|
EXTRNP _KiDeliverApc,3
|
|
extrn _KeI386MachineType:DWORD
|
|
|
|
if DBG
|
|
extrn _DbgPrint:near
|
|
extrn _KiDPCTimeout:DWORD
|
|
extrn _MsgDpcTimeout:BYTE
|
|
endif
|
|
|
|
ifdef NT_UP
|
|
LOCK_INC equ inc
|
|
else
|
|
LOCK_INC equ lock inc
|
|
endif
|
|
|
|
|
|
_DATA SEGMENT DWORD PUBLIC 'DATA'
|
|
public ProfileCount
|
|
ProfileCount DD 0
|
|
|
|
_DATA ends
|
|
|
|
page ,132
|
|
subttl "Update System Time"
|
|
|
|
_TEXT$00 SEGMENT DWORD PUBLIC 'CODE'
|
|
ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
|
|
|
|
|
|
; VOID
|
|
; KeUpdateSystemTime (
|
|
; IN KIRQL PreviousIrql,
|
|
; IN KTRAP_FRAME TrapFrame
|
|
; )
|
|
|
|
; Routine Description:
|
|
|
|
; This routine is entered as the result of an interrupt generated by CLOCK2.
|
|
; Its function is to update the system time and check to determine if a timer
|
|
; has expired.
|
|
|
|
; N.B. This routine is executed on a single processor in a multiprocess
|
|
; system. The remainder of the processors only execute the quantum end
|
|
; and runtime update code.
|
|
|
|
; N.B. This routine is not called, but directly jumped to. Thus, there
|
|
; is no return address. It returns via the INTERRUPT_EXIT macro.
|
|
|
|
; Arguments:
|
|
|
|
; PreviousIrql (esp) - supplies previous irql of system
|
|
|
|
; HardwareVector (esp+4) - supplies hardware vector for EndSystemInterrupt
|
|
|
|
; TrapFrame (esp+8) - supplies base of trap frame
|
|
|
|
; EAX is the TimeIncrement value
|
|
|
|
; EBP is a pointer to the trap frame
|
|
|
|
|
|
; Environment:
|
|
|
|
; IRQL = CLOCK_LEVEL
|
|
|
|
; Return Value:
|
|
|
|
; None.
|
|
|
|
|
|
cPublicProc _KeUpdateSystemTime ,0
|
|
|
|
.FPO (2, 0, 0, 0, 0, 1) ; treat params as locals since functions is JMPed too
|
|
|
|
if DBG
|
|
cmp byte ptr PCR[PcPrcbData+PbSkipTick], 0
|
|
jnz kust_skiptick
|
|
endif
|
|
|
|
|
|
; Update interrupt time.
|
|
|
|
; N.B. The interrupt time is updated in a very strict manner so that an
|
|
; interlock does not have to be used in an MP system to read time.
|
|
|
|
|
|
mov ecx,USER_SHARED_DATA ; set address of user shared data
|
|
mov edi,[ecx].UsInterruptTime+0 ; get low interrupt time
|
|
mov esi,[ecx].UsInterruptTime+4 ; get high interrupt time
|
|
add edi,eax ; add time increment
|
|
adc esi,0 ; propagate carry
|
|
mov [ecx].UsInterruptTime+8,esi ; store high 2 interrupt time
|
|
mov [ecx].UsInterruptTime+0,edi ; store low interrupt time
|
|
mov [ecx].UsInterruptTime+4,esi ; store high 1 interrupt time
|
|
|
|
sub _KiTickOffset,eax ; subtract time increment
|
|
mov eax,_KeTickCount+0 ; get low tick count
|
|
mov ebx,eax ; copy low tick count
|
|
jg kust10 ; if greater, not complete tick
|
|
|
|
|
|
; Update system time.
|
|
|
|
; N.B. The system time is updated in a very strict manner so that an
|
|
; interlock does not have to be used in an MP system to read time.
|
|
|
|
|
|
mov ebx,USER_SHARED_DATA ; set address of user shared data
|
|
mov ecx,[ebx].UsSystemTime+0 ; get low interrupt time
|
|
mov edx,[ebx].UsSystemTime+4 ; get high interrupt time
|
|
add ecx,_KeTimeAdjustment ; add time increment
|
|
adc edx,0 ; propagate carry
|
|
mov [ebx].UsSystemTime+8,edx ; store high 2 interrupt time
|
|
mov [ebx].UsSystemTime+0,ecx ; store low interrupt time
|
|
mov [ebx].UsSystemTime+4,edx ; store high 1 interrupt time
|
|
mov ebx,eax ; restore low tick count
|
|
|
|
|
|
; Update tick count.
|
|
|
|
; N.B. The tick count is updated in a very strict manner so that an
|
|
; interlock does not have to be used in an MP system to read count.
|
|
|
|
|
|
mov ecx,eax ; copy low tick count
|
|
mov edx,_KeTickCount+4 ; get high tick count
|
|
add ecx,1 ; increment tick count
|
|
adc edx,0 ; propagate carry
|
|
mov _KeTickCount+8,edx ; store high 2 tick count
|
|
mov _KeTickCount+0,ecx ; store low tick count
|
|
mov _KeTickCount+4,edx ; store high 1 tick count
|
|
mov USERDATA[UsTickCountLow], ecx
|
|
|
|
if 0
|
|
; debug code
|
|
push eax
|
|
mov edx, esi
|
|
mov eax, edi ; (eax:edx) = InterruptTime
|
|
mov ecx, _KeMaximumIncrement
|
|
div ecx
|
|
cmp al, bl ; same bucket?
|
|
je short @f
|
|
int 3 ; no - stop
|
|
@@:
|
|
pop eax
|
|
endif
|
|
|
|
|
|
; Check to determine if a timer has expired.
|
|
; (edi:esi) = KiInterruptTime
|
|
; (eax) = KeTickCount.LowPart
|
|
; (ebx) = KeTickCount.LowPart
|
|
|
|
|
|
and eax,TIMER_TABLE_SIZE-1 ; isolate current hand value
|
|
lea ecx,_KiTimerTableListHead[eax*8] ; get listhead addrees
|
|
mov edx,[ecx] ; get first entry address
|
|
cmp ecx,edx ; check if list is empry
|
|
je short kust5 ; if equal, list is empty
|
|
cmp esi,[edx].TiDueTime.TmHighTime-TiTimerListEntry ; compare high
|
|
jb short kust5 ; if below, timer has not expired
|
|
ja short kust15 ; if above, timer has expired
|
|
cmp edi,[edx].TiDueTime.TmLowTime-TiTimerListEntry ; compare low
|
|
jae short kust15 ; if above or equal, time has expired
|
|
kust5: inc eax ; advance hand value to next entry
|
|
inc ebx
|
|
|
|
|
|
; Check to determine if a timer has expired.
|
|
; (edi:esi) = KiInterruptTime
|
|
; (eax) = bucket
|
|
; (ebx) = KeTickCount.LowPart
|
|
|
|
|
|
kust10: and eax,TIMER_TABLE_SIZE-1 ; isolate current hand value
|
|
lea ecx,_KiTimerTableListHead[eax*8] ; get listhead addrees
|
|
mov edx,[ecx] ; get first entry address
|
|
cmp ecx,edx ; check if list is empry
|
|
je kustxx ; if equal, list is empty
|
|
cmp esi,[edx].TiDueTime.TmHighTime-TiTimerListEntry ; compare high
|
|
jb kustxx ; if below, timer has not expired
|
|
ja short kust15 ; if above, timer has expired
|
|
cmp edi,[edx].TiDueTime.TmLowTime-TiTimerListEntry ; compare low
|
|
jb kustxx ; if below, timer has not expired
|
|
|
|
kust15:
|
|
|
|
; Timer has expired, put timer expiration DPC in the current processor's DPC
|
|
; queue.
|
|
|
|
; (ebx) = KeTickCount.LowPart
|
|
|
|
|
|
mov ecx,PCR[PcPrcb] ; get processor control block address
|
|
lea eax,_KiTimerExpireDpc+DpDpcListEntry ; get list entry address
|
|
lea edx,[ecx]+PbDpcLock ; get DPC lock address
|
|
cmp dword ptr [eax]+(DpLock-DpDpcListEntry), 0H ; check if inserted
|
|
jnz kustxx ; if nz, DPC already inserted
|
|
|
|
kust20: cli
|
|
ACQUIRE_SPINLOCK edx, kust60
|
|
|
|
inc dword ptr [ecx].PbDpcQueueDepth ; increment DPC queue depth
|
|
mov dword ptr [eax]+(DpLock-DpDpcListEntry), edx ; set lock address
|
|
mov [eax]+(DpSystemArgument1-DpDpcListEntry),ebx ; pass tick count
|
|
add ecx,PbDpcListHead ; compute DPC listhead address
|
|
mov ebx,[ecx]+LsBlink ; get address of last entry in list
|
|
mov [ecx]+LsBlink, eax ; set new address of last entry
|
|
mov [ebx]+LsFlink, eax ; set forward link in old last entry
|
|
mov [eax]+LsFlink, ecx ; set forward link in new last entry
|
|
mov [eax]+LsBlink, ebx ; set backward link in new last entry
|
|
|
|
RELEASE_SPINLOCK edx
|
|
sti ; enable interrupt
|
|
|
|
; request dispatch interrupt
|
|
|
|
mov ecx, DISPATCH_LEVEL
|
|
fstCall HalRequestSoftwareInterrupt
|
|
|
|
kustxx:
|
|
if DEVL
|
|
cmp _KdDebuggerEnabled, 0
|
|
jnz short kust45
|
|
kust30:
|
|
endif
|
|
cmp _KiTickOffset,0 ; check if full tick
|
|
jg short Kust40 ; if not less, not a full tick
|
|
|
|
mov eax,_KeMaximumIncrement ; get maximum time incrmeent
|
|
add _KiTickOffset,eax ; add maximum tine to residue
|
|
|
|
|
|
; call KeUpdateRunTime to do the acutal work
|
|
|
|
|
|
; TOS const PreviousIrql
|
|
push [esp]
|
|
call _KeUpdateRunTime@4
|
|
|
|
|
|
; Do interrupt exit processing
|
|
|
|
|
|
INTERRUPT_EXIT
|
|
|
|
kust40:
|
|
inc dword ptr PCR[PcPrcbData+PbInterruptCount]
|
|
INTERRUPT_EXIT
|
|
|
|
if DEVL
|
|
kust45:
|
|
stdCall _KdPollBreakIn
|
|
or al,al
|
|
jz short kust30
|
|
stdCall _DbgBreakPointWithStatus,<DBG_STATUS_CONTROL_C>
|
|
jmp short kust30
|
|
endif
|
|
|
|
if DBG
|
|
kust_skiptick:
|
|
mov byte ptr PCR[PcPrcbData+PbSkipTick], 0
|
|
jmp short kust40
|
|
endif
|
|
|
|
|
|
; Lock is currently owned; spin until free and then attempt to acquire
|
|
; lock again.
|
|
|
|
|
|
ALIGN 4
|
|
kust60: sti ; spin with interrupts enabled
|
|
SPIN_ON_SPINLOCK edx, kust20,,DbgMp
|
|
|
|
stdENDP _KeUpdateSystemTime
|
|
|
|
|
|
page ,132
|
|
subttl "Update Thread and Process Runtime"
|
|
|
|
|
|
; Routine Description:
|
|
|
|
; This routines does the actual work to update the runtime of the current
|
|
; thread, update the runtime of the current thread's process, and
|
|
; decrement the current thread's quantum.
|
|
|
|
; It also updates the system global counters for user and kernel mode time.
|
|
|
|
; It increments InterruptCount so that clock ticks get counted as
|
|
; interrupts.
|
|
|
|
; Arguments:
|
|
|
|
; esp+4 constant PreviousIrql
|
|
|
|
; ebp MUST point to the machine state frame.
|
|
|
|
; Return Value:
|
|
|
|
; None.
|
|
|
|
|
|
|
|
cPublicProc _KeUpdateRunTime ,1
|
|
cPublicFpo 1, 1
|
|
|
|
mov eax, PCR[PcSelfPcr]
|
|
if DBG
|
|
cmp byte ptr [eax]+PcPrcbData+PbSkipTick, 0
|
|
jnz kutp_skiptick
|
|
endif
|
|
push ebx ; we will destroy ebx
|
|
inc dword ptr [eax]+PcPrcbData+PbInterruptCount
|
|
mov ebx, [eax]+PcPrcbData+PbCurrentThread ; (ebx)->current thread
|
|
mov ecx, ThApcState+AsProcess[ebx]
|
|
; (ecx)->current thread's process
|
|
|
|
test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK
|
|
jne Kutp20 ; if ne, user mode
|
|
|
|
test byte ptr [ebp]+TsSegCs, MODE_MASK ; test if prev mode was kernel
|
|
jne Kutp20 ; if ne, user mode
|
|
|
|
|
|
; Update the total time spent in kernel mode
|
|
|
|
|
|
mov edx, 0 ; set kernel mode
|
|
inc dword ptr [eax].PcPrcbData.PbKernelTime
|
|
cmp byte ptr [esp+8], DISPATCH_LEVEL
|
|
jc short Kutp4 ; OldIrql<2, then kernel
|
|
ja short Kutp3 ; OldIrql>2, then interrupt
|
|
|
|
cmp dword ptr PCR[PcPrcbData.PbDpcRoutineActive], 0
|
|
jz short Kutp4 ; Executing Dpc?, no then thread time
|
|
|
|
inc dword ptr [eax].PcPrcbData.PbDpcTime
|
|
if DBG
|
|
|
|
; Check for dpcs which run for too long
|
|
|
|
|
|
inc dword ptr [eax].PcPrcbData.PbDebugDpcTime
|
|
mov edx, _KiDPCTimeout
|
|
cmp dword ptr [eax].PcPrcbData.PbDebugDpcTime, edx
|
|
jc Kutp51 ; Jump if not over limit
|
|
|
|
|
|
; Dpc time has exceeded the allowed quanta
|
|
|
|
|
|
push offset FLAT:_MsgDpcTimeout ; push message address
|
|
call _DbgPrint ; print debug message
|
|
add esp, 1 * 4 ; remove arguments from stack
|
|
|
|
cmp _KdDebuggerEnabled, 0 ; check if debugger enabled
|
|
je short Kutp6 ; if eq, no debugger, continue
|
|
stdCall _DbgBreakPoint ; break into debugger
|
|
|
|
Kutp6: mov eax, PCR[PcSelfPcr] ; restore PCR address
|
|
mov dword ptr [eax].PcPrcbData.PbDebugDpcTime, 0 ; Reset Time
|
|
endif
|
|
jmp Kutp51
|
|
|
|
ALIGN 4
|
|
Kutp3:
|
|
|
|
; Update the time spent at interrupt time for this processor
|
|
|
|
|
|
inc dword ptr [eax].PcPrcbData.PbInterruptTime
|
|
jmp Kutp51
|
|
|
|
ALIGN 4
|
|
Kutp4:
|
|
|
|
|
|
; Update the time spent in kernel mode for the current thread and the current
|
|
; thread's process.
|
|
|
|
inc dword ptr [ebx]+ThKernelTime
|
|
|
|
LOCK_INC dword ptr [ecx]+PrKernelTime
|
|
|
|
jmp Kutp50
|
|
|
|
|
|
|
|
; Update total time spent in user mode
|
|
|
|
|
|
ALIGN 4
|
|
Kutp20:
|
|
mov edx, 1 ; set user mode
|
|
inc dword ptr [eax].PcPrcbData.PbUserTime
|
|
|
|
; Update the time spend in user mode for the current thread and the current
|
|
; thread's process.
|
|
|
|
|
|
inc dword ptr [ebx]+ThUserTime
|
|
|
|
LOCK_INC dword ptr [ecx]+PrUserTime
|
|
|
|
|
|
; Notify registered callout routine of update time.
|
|
|
|
; N.B. The register edx contains the processor mode.
|
|
|
|
|
|
ALIGN 4
|
|
Kutp50: ;
|
|
|
|
ifndef NT_UP
|
|
|
|
cmp _KiTimeUpdateNotifyRoutine, 0 ; check for callout routine
|
|
je short Kutp51 ; if eq, no callout routine registered
|
|
mov ecx, [ebx].EtCid.CidUniqueThread ; set current thread unique id
|
|
call [_KiTimeUpdateNotifyRoutine] ; notify callout routine
|
|
mov eax, PCR[PcSelfPcr] ; restore PCR address
|
|
|
|
endif
|
|
|
|
|
|
; Update the DPC request rate which is computed as the average between
|
|
; the previous rate and the current rate.
|
|
|
|
|
|
ALIGN 4
|
|
Kutp51: mov ecx, [eax].PcPrcbData.PbDpcCount ; get current DPC count
|
|
mov edx, [eax].PcPrcbData.PbDpcLastCount ; get last DPC count
|
|
mov [eax].PcPrcbData.PbDpcLastCount, ecx ; set last DPC count
|
|
sub ecx, edx ; compute count during interval
|
|
add ecx, [eax].PcPrcbData.PbDpcRequestRate ; compute sum
|
|
shr ecx, 1 ; average current and last
|
|
mov [eax].PcPrcbData.PbDpcRequestRate, ecx ; set new DPC request rate
|
|
|
|
|
|
; If the current DPC queue depth is not zero, a DPC routine is not active,
|
|
; and a DPC interrupt has not been requested, then request a dispatch
|
|
; interrupt, decrement the maximum DPC queue depth, and reset the threshold
|
|
; counter if appropriate.
|
|
|
|
|
|
cmp dword ptr [eax].PcPrcbData.PbDpcQueueDepth, 0 ; check queue depth
|
|
je short Kutp53 ; if eq, DPC queue depth is zero
|
|
cmp dword ptr [eax].PcPrcbData.PbDpcRoutineActive, 0 ; check if DPC active
|
|
jne short Kutp53 ; if ne, DPC routine active
|
|
cmp dword ptr [eax].PcPrcbData.PbDpcInterruptRequested, 0 ; check if interrupt
|
|
jne short Kutp53 ; if ne, DPC routine active
|
|
mov ecx, DISPATCH_LEVEL ; request a dispatch interrupt
|
|
fstCall HalRequestSoftwareInterrupt ;
|
|
mov eax, PCR[PcSelfPcr] ; restore address of current PCR
|
|
mov ecx, [eax].PcPrcbData.PbDpcRequestRate ; get DPC request rate
|
|
mov edx, _KiAdjustDpcThreshold ; reset initial threshold counter
|
|
mov [eax].PcPrcbData.PbAdjustDpcThreshold, edx ;
|
|
cmp ecx, _KiIdealDpcRate ; test if current rate less than ideal
|
|
jge short Kutp55 ; if ge, rate greater or equal ideal
|
|
cmp [eax].PcPrcbData.PbMaximumDpcQueueDepth, 1 ; check if depth one
|
|
je short Kutp55 ; if eq, maximum depth is one
|
|
dec dword ptr [eax].PcPrcbData.PbMaximumDpcQueueDepth ; decrement depth
|
|
jmp short Kutp55 ;
|
|
|
|
|
|
; The DPC queue is empty or a DPC routine is active or a DPC interrupt
|
|
; has been requested. Count down the adjustment threshold and if the
|
|
; count reaches zero, then increment the maximum DPC queue depth, but
|
|
; no above the initial value and reset the adjustment threshold value.
|
|
|
|
|
|
Kutp53: dec dword ptr [eax].PcPrcbData.PbAdjustDpcThreshold ; decrement threshold
|
|
jnz short Kutp55 ; if nz, threshold not zero
|
|
mov ecx, _KiAdjustDpcThreshold ; reset initial threshold counter
|
|
mov [eax].PcprcbData.PbAdjustDpcThreshold, ecx ;
|
|
mov ecx, _KiMaximumDpcQueueDepth ; get maximum DPC queue depth
|
|
cmp ecx, [eax].PcPrcbData.PbMaximumDpcQueueDepth ; check depth
|
|
je short Kutp55 ; if eq, aleady a maximum level
|
|
inc dword ptr [eax].PcPrcbData.PbMaximumDpcQueueDepth ; increment maximum depth
|
|
|
|
|
|
; Decrement current thread quantum and check to determine if a quantum end
|
|
; has occurred.
|
|
|
|
|
|
ALIGN 4
|
|
Kutp55: sub byte ptr [ebx]+ThQuantum, CLOCK_QUANTUM_DECREMENT ; decrement quantum
|
|
jg Kutp75 ; if > 0, time remaining on quantum
|
|
|
|
|
|
; Set quantum end flag and initiate a dispather interrupt on the current
|
|
; processor.
|
|
|
|
|
|
cmp ebx,[eax].PcPrcbData.PbIdleThread ; check if idle thread
|
|
jz Kutp75 ; if z, then idle thread
|
|
mov [eax].PcPrcbData.PbQuantumEnd, esp ; set quantum end indicator
|
|
mov ecx, DISPATCH_LEVEL ; request dispatch interrupt
|
|
fstCall HalRequestSoftwareInterrupt ;
|
|
Kutp75: ;
|
|
pop ebx ;
|
|
stdRET _KeUpdateRunTime ;
|
|
|
|
if DBG
|
|
kutp_skiptick:
|
|
mov byte ptr [eax]+PcPrcbData+PbSkipTick, 0
|
|
stdRET _KeUpdateRunTime
|
|
endif
|
|
|
|
stdENDP _KeUpdateRunTime
|
|
|
|
|
|
|
|
|
|
; PROFILING SUPPORT
|
|
|
|
|
|
|
|
|
|
|
|
|
|
; VOID
|
|
; KeProfileInterrupt (
|
|
; IN PKTRAP_FRAME TrapFrame,
|
|
; )
|
|
|
|
; Routine Description:
|
|
|
|
; This procedure is the ISR for the profile sampling interrupt,
|
|
; which for x86 machines is driven off the 8254 timer1 channel 0.
|
|
|
|
; The procedure scans the list of profile objects, looking for those
|
|
; which match the address space and return program counter captured
|
|
; at entry. For each object that does match, the counter in its
|
|
; profile buffer matching the bucket the PC falls into is computed,
|
|
; and that counter is incremented.
|
|
|
|
; N.B. This routine is executed on all processors in a multiprocess
|
|
; system.
|
|
|
|
; Arguments:
|
|
|
|
; Return Address (esp)
|
|
|
|
; TrapFrame (esp+4) - supplies pointer to profile trap frame
|
|
|
|
; Environment:
|
|
|
|
; IRQL = KiProfileIrql
|
|
|
|
|
|
; Return Value:
|
|
|
|
; None.
|
|
|
|
; WARNING: Uses ALL registers
|
|
|
|
|
|
|
|
cPublicProc _KeProfileInterrupt ,1
|
|
|
|
; rearrange arguments to pass a source of 0 to KeProfileInterruptWithSource
|
|
|
|
pop eax ; return code in eax
|
|
pop ebx ; trap frame in ebx
|
|
push 0 ; push source of 0 (ProfileTime)
|
|
push ebx ; push trap frame
|
|
push eax ; push return address
|
|
jmp short _KeProfileInterruptWithSource@8
|
|
stdENDP _KeProfileInterrupt
|
|
|
|
|
|
|
|
; VOID
|
|
; KeProfileInterruptWithSource (
|
|
; IN PKTRAP_FRAME TrapFrame,
|
|
; IN KPROFILE_SOURCE ProfileSource
|
|
; )
|
|
|
|
; Routine Description:
|
|
|
|
; This procedure is the ISR for the multiple-source profile interrupt.
|
|
|
|
; Since no x86 HAL currently implements any source other than the
|
|
; clock interrupt, this routine is just a stub that calls KeProfileInterrupt
|
|
|
|
; Arguments:
|
|
|
|
; Return Address (esp)
|
|
|
|
; TrapFrame (esp+4) - supplies pointer to profile trap frame
|
|
|
|
; ProfileSource (esp+8) - supplies source of profile interrupt
|
|
|
|
; Environment:
|
|
|
|
; IRQL = KiProfileIrql
|
|
|
|
|
|
; Return Value:
|
|
|
|
; None.
|
|
|
|
; WARNING: Uses ALL registers
|
|
|
|
|
|
cPublicProc _KeProfileInterruptWithSource,2
|
|
|
|
kipieip equ <dword ptr [ebp+TsEip]>
|
|
kipsegcs equ <word ptr [ebp+TsSegCs]>
|
|
kipeflags equ <dword ptr [ebp+TsEFlags]>
|
|
|
|
mov ebp, dword ptr [esp+4] ; (ebp)-> trap frame
|
|
inc dword ptr PCR[PcPrcbData+PbInterruptCount]
|
|
|
|
ifndef NT_UP
|
|
lea eax,_KiProfileLock
|
|
kipi05: ACQUIRE_SPINLOCK eax,kipi96
|
|
endif
|
|
|
|
|
|
; Update profile data
|
|
|
|
; NOTE:
|
|
; System and Process update loops are duplicates, to avoid overhead
|
|
; of call instruction in what could be very high freq. interrupt.
|
|
; be sure to update both loops for changes.
|
|
|
|
; NOTE:
|
|
; The process loop contains code to update segment profile objects.
|
|
; This code is not present in the system loop, because we do not
|
|
; allow attachment of profile objects for non-flat segments on a
|
|
; system wide basis.
|
|
|
|
; NOTE:
|
|
; Profiling in V86 mode is handled by converting the CS:IP value to
|
|
; a linear address (CS<<4 + IP)
|
|
|
|
|
|
inc ProfileCount ; total number of hits
|
|
|
|
|
|
; Update system profile entries
|
|
|
|
|
|
mov ebx, kipieip
|
|
mov edx,offset FLAT:_KiProfileListHead
|
|
mov esi,[edx].LsFlink ; (esi) -> profile object
|
|
ifndef NT_UP
|
|
mov edi, PCR[PcSetMember] ; (edi) = current processor
|
|
endif
|
|
mov ecx, [esp+8] ; (cx) = profile source
|
|
cmp esi,edx
|
|
je kipi30 ; end of system list, go do process
|
|
|
|
|
|
; (ebx) = sample program counter
|
|
; (esi) -> profile object
|
|
|
|
|
|
ALIGN 4
|
|
kipi10: cmp ebx,[esi+PfRangeBase-PfProfileListEntry] ; >= base?
|
|
jb kipi20 ; no, skip entry
|
|
cmp ebx,[esi+PfRangeLimit-PfProfileListEntry] ; < limit?
|
|
jae kipi20 ; no, skip entry
|
|
cmp cx,word ptr [esi+PfSource-PfProfileListEntry] ; == source?
|
|
jne kipi20 ; no, skip entry
|
|
ifndef NT_UP
|
|
test edi,[esi+PfAffinity-PfProfileListEntry] ; affinity match?
|
|
jz kipi20 ; no, skip entry
|
|
endif
|
|
|
|
|
|
; RangeBase <= program counter < RangeLimit, we have a hit
|
|
|
|
|
|
sub ebx,[esi+PfRangeBase-PfProfileListEntry] ; (ebx) = offset in profile range
|
|
mov cl,[esi+PfBucketShift-PfProfileListEntry]
|
|
shr ebx,cl
|
|
and ebx,NOT 3 ; (ebx) = offset of counter for bucket
|
|
mov edi,[esi+PfBuffer-PfProfileListEntry] ; (edi) -> buffer
|
|
inc dword ptr [edi+ebx] ; record hit
|
|
mov ebx, kipieip ; (ebx) = sample pc
|
|
mov ecx, [esp+8] ; (cx) = profile source
|
|
ifndef NT_UP
|
|
mov edi, PCR[PcSetMember] ; (edi) = current processor
|
|
endif
|
|
|
|
|
|
|
|
; Go to next entry
|
|
|
|
|
|
ALIGN 4
|
|
kipi20: mov esi,[esi].LsFlink ; (esi) -> profile object
|
|
cmp esi,edx
|
|
jne kipi10 ; not end of list, repeat
|
|
|
|
|
|
|
|
; Update process profile entries
|
|
; (ebx) = sample program counter
|
|
|
|
|
|
ALIGN 4
|
|
kipi30: mov eax,PCR[PcPrcbData+PbCurrentThread] ; (eax)-> current thread
|
|
mov eax,ThApcState+AsProcess[eax] ; (eax)-> current process
|
|
lea edx,[eax]+PrProfileListHead ; (edx)-> listhead
|
|
mov esi,[edx].LsFlink ; (esi)-> profile object
|
|
cmp esi,edx
|
|
je kipi60 ; process list end, return
|
|
|
|
|
|
; Check for 16 bitness
|
|
|
|
movzx ecx,word ptr kipsegcs
|
|
test kipeflags,EFLAGS_V86_MASK
|
|
jnz kipi100 ; convert cs:ip to linear
|
|
|
|
cmp cx,KGDT_R0_CODE
|
|
je short kipi40
|
|
|
|
cmp cx,KGDT_R3_CODE or RPL_MASK
|
|
jne kipi110
|
|
|
|
|
|
; (ebx) = sample program counter
|
|
; (esi) -> profile object
|
|
|
|
|
|
ALIGN 4
|
|
kipi40: cmp [esi+PfSegment-PfProfileListEntry],word ptr 0 ; flat object?
|
|
jne kipi50 ; no, skip entry
|
|
cmp ebx,[esi+PfRangeBase-PfProfileListEntry] ; >= base?
|
|
jb kipi50 ; no, skip entry
|
|
cmp ebx,[esi+PfRangeLimit-PfProfileListEntry] ; < limit?
|
|
jae kipi50 ; no, skip entry
|
|
mov ecx, [esp+8] ; (cx) = profile source
|
|
cmp cx,word ptr [esi+PfSource-PfProfileListEntry] ; == source?
|
|
jne kipi50 ; no, skip entry
|
|
ifndef NT_UP
|
|
mov edi,PCR[PcSetMember] ; (edi) = set member
|
|
test edi,[esi+PfAffinity-PfProfileListEntry] ; affinity match?
|
|
jz kipi50 ; no, skip entry
|
|
endif
|
|
|
|
|
|
|
|
; RangeBase <= program counter < RangeLimit, we have a hit
|
|
|
|
|
|
sub ebx,[esi+PfRangeBase-PfProfileListEntry] ; (ebx) = offset in profile range
|
|
mov cl,[esi+PfBucketShift-PfProfileListEntry]
|
|
shr ebx,cl
|
|
and ebx,NOT 3 ; (ebx) = offset of counter for bucket
|
|
mov edi,[esi+PfBuffer-PfProfileListEntry] ; (edi) -> buffer
|
|
inc dword ptr [edi+ebx] ; record hit
|
|
mov ebx, kipieip ; (ebx) = sample pc
|
|
mov ecx, [esp+8] ; (cx) = profile source
|
|
|
|
|
|
; Go to next entry
|
|
|
|
|
|
ALIGN 4
|
|
kipi50: mov esi,[esi].LsFlink ; (esi) -> profile object
|
|
cmp esi,edx
|
|
jne kipi40 ; not end of list, repeat
|
|
|
|
ALIGN 4
|
|
kipi60:
|
|
|
|
ifndef NT_UP
|
|
lea eax,_KiProfileLock
|
|
RELEASE_SPINLOCK eax
|
|
endif
|
|
stdRet _KeProfileInterruptWithSource
|
|
|
|
ifndef NT_UP
|
|
ALIGN 4
|
|
kipi96: SPIN_ON_SPINLOCK eax,kipi05,,DbgMp
|
|
endif
|
|
|
|
ALIGN 4
|
|
kipi100:
|
|
shl ecx,4 ; segment -> paragraph
|
|
add ebx,ecx ; paragraph offset -> linear
|
|
jmp kipi40
|
|
|
|
|
|
; Update segment profile objects
|
|
|
|
|
|
|
|
; (ebx) = sample program counter
|
|
; (esi) -> profile object
|
|
|
|
|
|
ALIGN 4
|
|
kipi110:
|
|
cmp [esi+PfSegment-PfProfileListEntry],ecx ; This segment?
|
|
jne kipi120 ; no, skip entry
|
|
cmp ebx,[esi+PfRangeBase-PfProfileListEntry] ; >= base?
|
|
jb kipi120 ; no, skip entry
|
|
cmp ebx,[esi+PfRangeLimit-PfProfileListEntry] ; < limit?
|
|
jae kipi120 ; no, skip entry
|
|
mov ecx, [esp+8] ; (cx) = profile source
|
|
cmp cx,word ptr [esi+PfSource-PfProfileListEntry] ; == source?
|
|
jne kipi120 ; no, skip entry
|
|
ifndef NT_UP
|
|
mov edi,PCR[PcSetMember] ; (edi) = set member
|
|
test edi,[esi+PfAffinity-PfProfileListEntry] ; affinity match?
|
|
jnz kipi120 ; no, skip entry
|
|
endif
|
|
|
|
|
|
; RangeBase <= program counter < RangeLimit, we have a hit
|
|
|
|
|
|
sub ebx,[esi+PfRangeBase-PfProfileListEntry] ; (ebx) = offset in profile range
|
|
mov cl,[esi+PfBucketShift-PfProfileListEntry]
|
|
shr ebx,cl
|
|
and ebx,NOT 3 ; (ebx) = offset of counter for bucket
|
|
mov edi,[esi+PfBuffer-PfProfileListEntry] ; (edi) -> buffer
|
|
inc dword ptr [edi+ebx] ; record hit
|
|
mov ebx, kipieip ; (ebx) = sample pc
|
|
mov cx,kipsegcs ; ecx = sample cs
|
|
|
|
|
|
; Go to next entry
|
|
|
|
|
|
ALIGN 4
|
|
kipi120:
|
|
mov esi,[esi].LsFlink ; (esi) -> profile object
|
|
cmp esi,edx
|
|
jne kipi110 ; not end of list, repeat
|
|
|
|
jmp kipi60
|
|
|
|
stdENDP _KeProfileInterruptWithSource
|
|
_TEXT$00 ends
|
|
end
|