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, 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 kipsegcs equ kipeflags equ 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