Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

928 lines
29 KiB

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
extrn _PPerfGlobalGroupMask:DWORD
EXTRNP PerfProfileInterrupt,2,,FASTCALL
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]
cmp _PPerfGlobalGroupMask, 0 ; check if event tracing is on
je short kipi03
;; add profile interrupt to perfinfo
mov ecx, [esp+8]
mov edx,kipieip
fstCall PerfProfileInterrupt
mov ebp, dword ptr [esp+4] ; (ebp)-> trap frame
kipi03:
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