;       SCCSID = @(#)profint.asm	6.3 92/02/05
SCCSID equ "@(#)profint.asm	6.3 92/02/05"
PAGE    ,132
TITLE   PROFINT.ASM - Interrupt time Profiling routines
NAME    PROFINT

;****************************************************************************
;*
;*  Interrupt handler for Profiling
;*
;*  Author:
;*      Wayne Johnson
;*
;*
;*
;*  IBM/Microsoft Confidential
;*
;*  Copyright (c) IBM Corporation 1987, 1989
;*  Copyright (c) Microsoft Corporation 1987, 1989
;*
;*  All Rights Reserved
;*
;*
;*
;*  Exported Routines:
;*      PROIntProfile
;*      _proError
;*      _proSetTimer
;*
;*
;*  Imported Routines:
;*      None
;*
;*  History:
;*      01-Nov-1989     created by Wayne Johnson
;*
;****************************************************************************

INCL_TYPES  EQU 1                ;Enable Standard Type Checking
INCL_DEF    EQU 1                ;Enable typedefs


.xlist
include fnoperf.inc
include dosseg.inc
include osmaca.inc
include basemaca.inc
include kmode.inc
include basedef.inc
include osconst.inc
include pgexport.inc
include tkexport.inc
        include vmexport.inc
        include infoseg.inc
        include dossymc.inc
        include curdir.inc
        include dirent.inc
        include find.inc
        include sigdef.inc
        include sem.inc
        include screen.inc
        include except.inc
include tkptda.inc
include selxport.inc
include seldesc.inc
include mi.inc
include doshlp.inc
include iodelay.inc
include int.inc                 ;BUGBUG this shouldn't be need when better
                                ;interrupt controls are added
        include profuser.inc
include proxport.inc
include pvwxport.inc
include pvwp.inc                                                       ;aa04677
.list

        ModName profint, FNO_PROFINT_ASM
        CpuMode 386

extrn           _SELVirtToLin:near
extrn           _PROSysArena:near
extrn           _PROShrArena:near

ReferGlobal     DHF,            DWORD           ; Dos Help Function table.
ReferFlat       _ppmb,          DWORD
ReferFlat       _pPTDACur,      DWORD
ReferFlat       _pTSDCur,       DWORD
ReferFlat       fIdle,          BYTE
ReferFlat       _SELGDTLaddr,   DWORD
ReferFlat       _SELMaxGDT,     WORD




;****************************************************************************
;***************************************************************************

        FARCODE HIGH32
HIGH32CODE segment






;**EP   PROIntProfile - Profile timer tick
;
;   This will collect profile info for the currently interrupted process.
;
;   ENTRY
;       (EBP)   = ISF frame pointer
;
;   EXIT
;       ZF set if profiled disabled
;       ZF clear if profiled enabled
;
;   CONTEXT
;       Called At interrupt time (INT0) to collect profile info
;
;   REGISTER USAGE - ALL
;       eax - profile return value
;       ebx - PMB, PTDA, TSD
;       ecx - PCB
;       Others used locally for random count operations
;
;   PSEUDO-CODE
;       get PMB
;       if ppmb = 0
;           return
;       if fRandomCount
;           if (--cRandomWait)
;               return
;           else
;               cRamdomWait = aRandomCounts[iRandomCount]
;               if (--iRandomCount < 0)
;                   iRandomCount=cRandomCount - 1
;       if System PCB != 0
;           collect system data
;       if Process PCB != 0
;           if in thread mode
;               collect thread data
;           else
;               collect process data
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure   PROIntProfile,near

    xor  eax,eax                        ;(eax) = 0  =  profiling not enabled
    mov  ebx,[_ppmb]                    ;(ebx) = ppmb
    or   ebx,ebx                        ;check for
    jz   short ipfExit                  ;   null _ppmb
    mov  eax,not 0                      ;(eax) = -1 = profiling enabled
    test BYTE PTR [ebx].pmb_fRandomSample,not 0;Begin if not
    jz   short ipfChkSysPCB             ;       doing a random count

;   REGISTER USAGE For random sample section
;       eax - profile return value
;       ebx - PMB
;       ecx - New random wait count (pmb_cRandomWait)
;       edx - Index into random count table (pmb_iRandomCount)
;       esi - Beginning of aRandomCount

    dec  BYTE PTR [ebx].pmb_cRandomWait ;Advance to next wait value
    jnz  short ipfExit                  ;return if not zero
    movzx edx, BYTE PTR [ebx].pmb_iRandomCount  ;(edx) = pmb_iRandomCount
    mov  esi, [ebx].pmb_aRandomCount    ;load
    mov  cl, BYTE PTR [esi][edx]        ;    new
    mov  [ebx].pmb_cRandomWait, cl      ;       count value
    dec  edx                            ;pmb_iRandomCount--
    jge  short ipfLeaveRandomSample     ;go profile if valid index
    mov  dl, [ebx].pmb_cRandomCount     ;get next value for
    dec  dl                             ;       iRandomCount

ipfLeaveRandomSample:
    mov  [ebx].pmb_iRandomCount, dl     ;write iRandomCount back out

ipfChkSysPCB:
    mov  ecx,[ebx].pmb_ppcbSystem       ;(ecx) = ppcbSystem
    or   ecx,ecx                        ;check for
    jz   short ipfChkProcPCB            ;       null _ppcbSystem
    test [ecx].pcb_fsOptions,PROI_ENABLED;Exit if profiling
    jz   short ipfExit                  ;       not enabled
    CCALL32 proLogProfileData           ;record data for system

ipfChkProcPCB:
    mov  ebx,[_pPTDACur]                ;(ebx) = PTDA
    mov  ecx,[ebx].ptda_pPCB            ;(ecx) = pPCB (process)
    or   ecx,ecx                        ;check for
    jz   short ipfExit                  ;   null pPCB
    test [ecx].pcb_fsOptions,PROI_ENABLED;Exit if profiling
    jz   short ipfExit                  ;       not enabled

    test [ecx].pcb_fsOptions,PROI_PCB_TMB;check for thread mode
    jz   short ipfCallWorker            ;     NO? get data in Proc mode.

    mov  ebx,[_pTSDCur]                 ;(ebx) = TSD
    mov  ecx,[ebx].TSDpPCB              ;(ecx) = pPCB (process)
    or   ecx,ecx                        ;check for
    jz   short ipfExit                  ;   null pPCB

ipfCallWorker:
    CCALL32 proLogProfileData           ;record data for system

ipfExit:
    test eax,eax                        ;set ZF to indicate profiling state
    RETC

EndProc PROIntProfile




HIGH32CODE ENDS

;***********************************************************************

        FARCODE HIGH32
PROFLOCK_TEXT segment




;**LP   proLogProfileData       - Collect data for interuppted process.
;
;   This will find out if the current module is being profiled,
;   and data appropriate data.
;
;   ENTRY
;       (ECX)   = PCB
;       (EBP)   = ISF frame pointer
;
;   EXIT
;       NONE
;
;   CONTEXT
;       Called At interrupt time (INT0) by PROIntProfile
;
;   REGISTER USAGE
;       eax - laddr from current MPR
;       ebx - current MPR
;       ecx - PCB
;       edx - sampled laddr
;       esi - Head MPR
;       edi - Previous MPR
;       ebp - ISF frame pointer
;
;   PSEUDO-CODE
;       check for idle
;       check for V86
;       get current LADDR
;       current=last=head
;       while not End_OF_List
;           scan list
;           if found
;               Move to front of List
;               store data depending on collection method
;               return
;           else
;               goto next
;       if GDT
;           other_GDT++
;       else
;           other LDT++
;       return
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure   proLogProfileData,near
    test  [fIdle],-1                    ;System Idling?
    je    short lpdChkV86                       ;   no, check for v86 mode
    inc   [ecx].pcb_cticIdle
    jmp   lpdExit                       ;Done

lpdChkV86:
    test  [ebp].isf_eflag,F_VM          ;V86 mode?
    je    short lpdGetLinAddr           ;   no, go get address
    inc   [ecx].pcb_cticV86
    jmp   lpdExit                       ;Done

lpdGetLinAddr:
    SaveReg < ecx >
    CCALL32 _SELVirtToLin < [bp].isf_eip, DWORD PTR [bp].isf_cs >
    RestoreReg < ecx >
    cmp  eax,-1                         ;If error
    je   lpdError                       ;   go to error processing


;Begin Searching for which module LADDR belongs to

    mov  edx, eax                       ;(edx) = Sampled LADDR
    mov  ESI,[ECX].pcb_pmprHead         ;(esi) = Pointer to head of mpr list
    mov  ebx,esi                        ;(ebx) = currently searched MPR

lpdChkNextMPR:
    or   ebx,ebx                        ;check for
    jz   short lpdEndOfList             ;   end of list

    mov  eax,[ebx].mpr_laddrModule      ;(eax) = module start address
    sub  eax,edx                        ;Is start of module before sampled laddr?
    jg   short lpdNotFound              ;   No? Get next module
    add  eax,[ebx].mpr_cbModule         ;Is end of module after sampled laddr?
    jg   short lpdFound                 ;   Yes? We found our module

lpdNotFound:
    mov  edi,ebx                        ;(edi) = last MPR
    mov  ebx,[edi].mpr_pmprNext         ;(ebx) = new MPR
    jmp  short lpdChkNextMPR            ;Continue searching the list

lpdFound:
    cmp  ebx,esi                        ;Is found module already at head?
    je   short lpdDoneSorting           ;   Yes, Don't move if already there

    mov  eax,[ebx].mpr_pmprNext         ;Move
    mov  [edi].mpr_pmprNext,eax         ;   Found MPR
    mov  [ebx].mpr_pmprNext,esi         ;       to front
    mov  [ecx].pcb_pmprHead,ebx         ;           of list

lpdDoneSorting:
    mov  al,byte ptr [ebx].mpr_fbCollectType;(al) = collect type
    cmp  al, PROC_MODULE                ;Per module counts?
    jz   short lpdCountModule           ;   Yes?  Do it
    cmp  al, PROC_IP_COUNT              ;Per IP counts?
    jz   short lpdCountIP               ;   Yes?  Do it
    cmp  al, PROC_IP_RECORD             ;Record IP?
    jnz  short lpdError                 ;   No? Illegal Collection Specified

lpdRecordIP:
    mov  eax,[ecx].pcb_iladdrSampled    ;(eax) = index to collection buffer
    cmp  eax,[ecx].pcb_claddrSampled    ;Past end of array?
    jl   short lpdRecordIPWrite         ;   No, record the data
    or   WORD PTR [ecx].pcb_fsOptions,PROI_IP_RECORD_FULL ;set Full Bit
    cmp  WORD PTR [ecx].pcb_fsOptions,PROO_WRAP_RECORD    ;exit if no wrap
    jnz  short lpdExit
    xor  eax, eax                       ;reset the index
lpdRecordIPWrite:
    mov  edi,[ecx].pcb_aladdrSampled    ;(edi) = collection buffer
    mov  [edi][eax*4],edx               ;copy sampled address to buffer
    inc  eax                            ; Increment buffer index
    mov  DWORD PTR [ecx].pcb_iladdrSampled, eax
    jmp  short lpdExit

lpdCountIP:
    mov  eax,[ebx].mpr_laddrModule      ;(eax) = module start address
    sub  edx,eax                        ;(edx) = offset into module
    mov  cl,[ecx].pcb_bShiftValue       ;(cl) = shift value
    shr  edx,cl                         ;round off sampled address
    mov  edi,[ebx].mpr_ulProfData       ;(edi) = collection buffer
    inc  DWORD PTR [edi][edx*4]         ;increment IP counter
    jmp  short lpdExit

lpdCountModule:
    inc  DWORD PTR [ebx].mpr_ulProfData ;increment module counter
    jmp  short lpdExit

lpdEndOfList:
    SaveReg < ecx, edx >
    CCALL32 _PROSysArena <edx>          ; check for laddr in system arena
    RestoreReg < edx, ecx >
    or   eax, eax                       ; laddr in system arena?
    jnz   short lpdIncSysArena          ; yes, inc count for SYS arena
    SaveReg < ecx, edx >
    CCALL32 _PROShrArena <edx>          ; check for laddr in shared arena
    RestoreReg < edx, ecx >
    or   eax, eax                       ; laddr in shared arena?
    jnz   short lpdIncShrArena          ; yes, inc count for SHR arena
    inc  [ecx].pcb_cticEXE              ; no, inc count for EXE
    jmp  short lpdExit
lpdIncSysArena:
    inc  [ecx].pcb_cticSYS
    jmp  short lpdExit
lpdIncShrArena:
    inc  [ecx].pcb_cticSHR
    jmp  short lpdExit

lpdError:
    CCALL32 _proError                   ;Increment Error count

lpdExit:
    mov  eax,-1                         ;Return profiled (eax = -1)
    RETC

EndProc proLogProfileData




;**LP   _proError       - Increment profiler error count
;
;
;   ENTRY
;       NONE
;   EXIT
;       NONE
;
;   CONTEXT
;       Called by kernal routine in the presence of an error
;
;   REGISTER USAGE
;       NONE
;
;   PSEUDO-CODE
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure   _proError,near

    mov  eax, [_ppmb]                   ;check for valid ppmb
    test eax, -1
    jz   short errExit
    inc  BYTE PTR [eax].pmb_cProfErrors ;increment error count

errExit:
    RETC

EndProc _proError





PROFLOCK_TEXT ENDS

;***********************************************************************

        FARCODE HIGH32
PROFSWAP_TEXT segment






;**LP   _proSetTimer    - Set up timer 0 for profiling
;
;   This routine sets the counter to roll over at the number of milliseconds
;   specified.  If zero is specified, it is reset to its maximum period.
;
;   ENTRY
;       period          -Number of millisecs between samples.  A value of
;                        zero means to reset the timer to its maximum period.
;
;   EXIT
;       NONE
;
;       CONTEXT
;
;   REGISTER USAGE
;        eax - period
;        ecx - shift counter, converted period
;
;   PSEUDO-CODE
;       convert to timer ticks
;       call DosHlp function to do work
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure   _proSetTimer,near

    ArgVar  period,WORD

    EnterProc

        movzx   eax,[period]            ; (eax) = period
        or      eax,eax
        jnz     short pst5              ; Jump if not a timer reset.
        mov     ecx, TMR_MAX_ROLLOVER
        jmp     short pst8              ; (ecx) = max. period (in timer ticks)

pst5:   mov     cl, 16                  ; convert uSecs to Tics by shifting
        shl     eax, cl                 ;   to upper word, and then dividing
        cdq                             ;   by ratio of (timer/uSecs)*10000h
        mov     ecx, PRO_PERIOD_MAX
        idiv    ecx

        mov     ecx, eax                ; (ecx) = period (in timer ticks)
pst8:   CCALL32 DHF.DosHlpTmrSetRollover

    LeaveProc
    RETC

EndProc _proSetTimer



;**LP   _proCollectOn   - Set up Int 0 for profiling
;
;   This is a short term solution.  It will go away when the appropriate
;   device driver services exist.
;
;   ENTRY
;       NONE
;
;   EXIT
;       NONE
;
;       CONTEXT
;         Called by kernal entry point to enable data collection
;
;   REGISTER USAGE
;        al - int mask
;       ebx - ppmb
;
;   PSEUDO-CODE
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure   _proCollectOn,near


    EnterProc


        mov     ebx, _ppmb              ; (ebx) = ppmb

        cli
        in      al,PIC1_PORT1           ; Read in current INT mask

        mov     cl,al                   ; (cl) = int mask
        and     cl,01h                  ; set ppmb with status
        mov     [ebx].pmb_fInt0Clear,cl ;    of int0

        and     al,not 01h              ; mask off timer 0 bit
        kIODelay32 ebx
        out     PIC1_PORT1,al           ; Write out current INT mask
        sti

    LeaveProc
    RETC

EndProc _proCollectOn



;**LP   _proCollectOff  - Turn off Int 0 for profiling
;
;   This is a short term solution.  It will go away when the appropriate
;   device driver services exist.
;
;   ENTRY
;       NONE
;
;   EXIT
;       NONE
;
;       CONTEXT
;         Called by kernal entry point to disable data collection
;
;   REGISTER USAGE
;        al - int mask
;       ebx - ppmb
;
;   PSEUDO-CODE
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

Procedure   _proCollectOff,near


    EnterProc


        mov     ebx, _ppmb              ; (ebx) = ppmb
        test    byte ptr [ebx].pmb_fInt0Clear,not 0
        jz      short cofDone           ; don't Disable interupt if in use

        cli
        in      al,PIC1_PORT1           ; Read in current INT mask
        or      al,01h                  ; set bit to clear timer 0
        kIODelay32 ebx
        out     PIC1_PORT1,al           ; Write out current INT mask
        sti

cofDone:

    LeaveProc
    RETC

EndProc _proCollectOff



PROFSWAP_TEXT ENDS

           END
