From:	CRDGW2::CRDGW2::MRGATE::"SMTP::CRVAX.SRI.COM::RELAY-INFO-VAX"  2-AUG-1989 23:01
To:	MRGATE::"ARISIA::EVERHART"
Subj:	GET_CPU_STATS.MAR (back by popular demand)

Received: From KL.SRI.COM by CRVAX.SRI.COM with TCP; Wed,  2 AUG 89 19:18:37 PDT
Received: from ucbvax.Berkeley.EDU by KL.SRI.COM with TCP; Wed, 2 Aug 89 19:03:17 PDT
Received: by ucbvax.Berkeley.EDU (5.61/1.37)
	id AA28630; Wed, 2 Aug 89 19:00:00 -0700
Received: from USENET by ucbvax.Berkeley.EDU with netnews
	for info-vax@kl.sri.com (info-vax@kl.sri.com)
	(contact usenet@ucbvax.Berkeley.EDU if you have questions)
Date: 1 Aug 89 19:57:53 GMT
From: leah!rpi!crdgw1!ge-dab!peora!ucf-cs!morris@csd4.milw.wisc.edu  (Carl Morris)
Organization: Univ. of Central Florida, Orlando
Subject: GET_CPU_STATS.MAR (back by popular demand)
Message-Id: <1217@ucf-cs.UCF.EDU>
References: <none>
Sender: info-vax-request@kl.sri.com
To: info-vax@kl.sri.com



I wrote a Macro subroutine to return the active CPU mask, active CPU
count, and an array containing the busy percentages for each active CPU. 
The source code follows (read the comments for details), and a C 
language routine is included to show how to call it and one possible
use for the routine.  If you like it, use it.  Sorry, no warranty
is implied!
------------------ cut here -------------------------------------
; ...   Title:          Get_CPU_Stats.Mar
; 
; ...   Version:        V1.1-000
;
; ...   Facility:       System Subroutine
;
; ...   Abstract:       This subroutine returns the active CPU mask, the
;                       count of active CPUs, and the busy percent for each
;                       CPU since the last call to this routine.  Requires
;                       no privileges.
;
; ...   Use:            Call as a subroutine.  The C language function 
;                       prototype for this subroutine is:
;
;       int get_cpu_stats( unsigned int *active_cpu_mask,
;                          unsigned int *active_cpu_count,
;                          double busy_stats []);
;
;                       where busy_stats is an array of 32 D_FLOATS.  The mask
;                       will contain a copy of SMP$GL_ACTIVE_CPUS, the count
;                       will be how many bits are set in the mask.  The array
;                       will contain the busy percent for each CPU, the 
;                       location corresponds to the CPU number (busy_stats [0]
;                       will contain the busy percentage for CPU #0 since the
;                       last call to the routine).  Note: the routine must be
;                       called once for initialization before busy percents 
;                       will be returned for active CPUs.
;
; ...   Returns:        SS$_NORMAL
;
; ...   Environment:    V5 of VMS.
;
; ...   Author:         Carl Morris
;
; ...   Date:           15-Jul-1989
;
; ...   Modified by:
;
;       V1.1-000        Carl Morris     17-Jul-1989
;               Changed some addressing modes.  Put all scratch space inside
;               routine rather than have calling routine keep track of it.
;
;-----------------------------------------------------------------------------
;
;
        .title  Get_CPU_Stats.Mar
        .ident  'V1.1-000'
 
        .library                'sys$library:lib.mlb'
 
        .link                   'sys$system:sysdef.stb' /selective_search, -
                                'sys$system:sys.stb'    /selective_search
 
        $cpudef                                         ; CPU-specific offsets
        $ssdef                                          ; completion codes
 
active_cpu_mask   =  4                                  ; offset to mask addr
active_cpu_count  =  8                                  ; offset to count addr
cpu_busy_vector   = 12                                  ; offset to vector addr
 
max_cpus = 32                                           ; 32 CPUs max under
                                                        ; current SMP
 
system_time:                                            ; last time we were 
        .blkq   1                                       ; called (64 bits)
 
delta_ticks:                                            ; 10ms ticks since last
        .blkl   1                                       ; call to routine
 
float_ticks:                                            ; this will contain
        .blkd   1                                       ; delta_ticks/100 so
                                                        ; when we divide by it
                                                        ; we'll get percents
 
current_null_vector:                                    ; vector of current
        .blkl   max_cpus                                ;   null tick counts
 
last_null_vector:                                       ; vector of last call's
        .blkl   max_cpus                                ;   null tick counts
 
        .entry  get_cpu_stats,^m<r2,r3>                 ; subroutine entry pnt
 
                                                        ; get last time we 
        movq    system_time,r2                          ;  were called
        $gettim_s -                                     ; get current time
                system_time
        movq    system_time,r0                          ; copy current time
 
                                                        ; calc delta time since
        subl2   r2,r0                                   ;  last call to routine
        sbwc    r3,r1                                   ;  in 100ns ticks
        ediv    #100000,r0,delta_ticks,r1               ; calc 10ms ticks
        cvtld   delta_ticks,float_ticks                 ; get it in d float
        divd    #100.0,float_ticks                      ; divide by 100 for
                                                        ;  percent conversion
 
        movl    g^smp$gl_active_cpus, -                 ; return active mask
                @active_cpu_mask(ap)
 
        clrl    @active_cpu_count(ap)                   ; init active CPU cnt
        movl    g^smp$gl_active_cpus,r0                 ; get active CPU mask
        clrl    r1                                      ; init loop index
 
10$:    clrd    @cpu_busy_vector(ap)[r1]                ; clear busy percent
        bbs     r1,r0,20$                               ; if CPU active, calc
 
        clrl    current_null_vector[r1]                 ; clear current ticks
        brb     30$                                     ; do next CPU
 
20$:    incl    @active_cpu_count(ap)                   ; increment active cnt
        movl    current_null_vector[r1], -              ; save last null ticks
                last_null_vector[r1]
        movl    g^smp$gl_cpu_data[r1],r2                ; get ptr to CPU data
        movl    cpu$l_nullcpu(r2), -                    ; save new null ticks
                current_null_vector[r1]
        tstl    last_null_vector[r1]                    ; check old ticks
        beql    30$                                     ; if CPU has not been
                                                        ;  seen before don't do
                                                        ;  calc (this avoids 
                                                        ;  getting a busy 
                                                        ;  percent > 100 on 
                                                        ;  first call or when
                                                        ;  a CPU (re)appears)
 
                                                        ; subtract null ticks
                                                        ;  from delta ticks to
                                                        ;  get busy ticks & 
                                                        ;  make a percentage 
 
25$:    movl    delta_ticks,r2                          ; get delta ticks
        subl    current_null_vector[r1],r2              ; subtract new ticks
        addl    last_null_vector[r1],r2                 ; add old ticks
        cvtld   r2,@cpu_busy_vector(ap)[r1]             ; get float busy ticks
        divd    float_ticks,@cpu_busy_vector(ap)[r1]    ; get busy percent
 
30$:    aoblss  #max_cpus,r1,10$                        ; inc index
 
        movzbl  #ss$_normal,r0                          ; return success
        ret
 
        .end
------------------ cut here -------------------------------------
/* Example C routine to demonstrate use of get_cpu_stats().  When run, it    */
/* calls get_cpu_stats() once to init the null cpu counts for each cpu, then */
/* enters a loop which: waits ten seconds, calls the subroutine, calculates  */
/* the average busy percent for all active CPUs, prints the results and      */
/* loops again.                                                              */
 
#define MAX_CPUS 32                               /* max CPUs to look for    */
                                                   
int get_cpu_stats( unsigned int *active_cpu_mask, /* function prototype      */
                    unsigned int *active_cpu_count,
                    double busy_stats []);
main()
{
    double accumulator,
           busy_stats [MAX_CPUS],
           ten_seconds = 10.0;
 
    unsigned int active_cpu_count,
                 active_cpu_mask,
                 index;
                                                  /* init busy stats         */
    get_cpu_stats( &active_cpu_mask, &active_cpu_count, busy_stats);
 
    for(;;){
      lib$wait( &ten_seconds);                    /* wait ten seconds        */
 
                                                  /* get busy stats          */
      get_cpu_stats( &active_cpu_mask, &active_cpu_count, busy_stats);
 
                                                  /* add up busy figures     */
      for( accumulator = 0, index = 0; index < MAX_CPUS; ++index){
        accumulator += busy_stats [index];                                   
      }
                                                  /* print stats & loop      */
      printf("%d CPUs, the active mask is %08X, avg busy = %3.2f%%\n",
        active_cpu_count, active_cpu_mask, accumulator/active_cpu_count);
    }
 
}

