
/*****************************************************************************
 *
 *   (C) Copyright MICROSOFT Corp., 1988-1990
 *
 *   Title:     PERF.H - Include file for perf monitor
 *
 *   Version:   1.00
 *
 *   Date:
 *
 *   Author:	FCF
 *
 *-----------------------------------------------------------------------------
 *
 *   Change log:
 *
 *   DATE        REV DESCRIPTION
 *   ----------- --- -----------------------------------------------------------
 */

/*  See dos\dos386\vxd\perf\example for a sample VxD that registers as */
/*  a perf server.                                                     */

/* defines */

#define MAXNAMELEN			50	/* maximum number of characters in
								   service name, stat name, or key names */
#define MAXCOMPLEXSUBSTAT	8	/* maximum number of stats making
								   up a complex stat */
	
/* structures and flags used for the ring-0 interface */

struct perf_server_0 {
    unsigned long	psrv0_Level;	/* Must be zero for this level */
    unsigned long	psrv0_Flags;
    char 	       *psrv0_pszServerName;
    char	       *psrv0_pszServerNodeName;
    void	       *psrv0_pControlFunc;
};

struct perf_stat_0 {
    unsigned long	pst0_Level;	/* Must be zero for this level */
    unsigned long	pst0_Flags;
    char	       *pst0_pszStatName;
    char	       *pst0_pszStatNodeName;
    char	       *pst0_pszStatUnitName;
    char	       *pst0_pszStatDescription;
    void	       *pst0_pStatFunc;
};

/* Values for psrv0_Flags follow */

/* Values for pst0_Flags follow */

/* pst0_pStatFunc points either directly to data (always a DWORD for now) */
/* or, if PSTF_FUNCPTR_BIT is set, to a _cdecl function.  This function   */
/* accepts a stat handle as it's argument and returns the stat in eax     */

#define PSTF_FUNCPTR		0x00000001

/* The data referenced by this stat is always a counter, e.g. number of   */
/* bytes read.  It is up to the client to differentiate this into a rate. */
/* If PSTF_RATE is set, then the text associated with this stat assumes   */
/* that the stat will be differentiated with respect to time.  It's 	  */
/* possible that two stats will refer to the same data - one with this    */
/* bit set and one without, with help text appropriate for each.          */

#define PSTF_COUNT		0x00000000
#define PSTF_RATE		0x00000002

/* A recommendation as to the frequency of update.  Bytes read/second     */
/* can change quite rapidly, but updating # of shares will not change     */
/* as often, and (currently) vcache cache size is static.  Perf clients   */
/* are free to ignore these values, and free to define update frequency.  */

#define PSTF_FREQ_HIGH		0x00000000
#define PSTF_FREQ_LOW		0x00000004
#define PSTF_FREQ_STATIC	0x00000008
#define PSTF_FREQ_MASK		(PSTF_FREQ_STATIC | PSTF_FREQ_LOW |	\
				 PSTF_FREQ_HIGH)


/* A recommendation as to the scale type.  Bytes read/second may be more  */
/* appropriately displayed on a log10 scale, while memory available might */
/* be better on a linear scale.  Perf clients are free to ignore this.    */

#define PSTF_SCALE_LINEAR	0x00000000
#define PSTF_SCALE_LOG10	0x00000010
#define PSTF_SCALE_LOG2		0x00000020
#define PSTF_SCALE_MASK		(PSTF_SCALE_LINEAR | PSTF_SCALE_LOG10 | \
				 PSTF_SCALE_LOG2)

/* XLATOFF */

unsigned long PERF_Server_Register( struct perf_server_0 * );
void PERF_Server_Deregister( unsigned long hReg );
unsigned long PERF_Server_Add_Stat( unsigned long hReg, struct perf_stat_0 );
void PERF_Server_Remove_Stat( unsigned long hStat );

/* XLATON */

/* Control messages sent to perf server's control function.  The control */
/* function is optional, set it to NULL if you don't want any control    */
/* messages.  Perf servers are free to ignore any messages they want.    */
/* Control functions take two DWORD parameters; a message (dwMsg) and a  */
/* DWORD of message-dependent data (dwData). 							 */

/* The following defines are values for dwMsg:							 */
#define PMSG_START_STAT				0x11
#define PMSG_STOP_STAT				0x12

/* PMSG_START_STAT: Notifies that a perf client is going to start        */
/*     watching this stat.  dwData contains the stat handle.             */
/* PMSG_STOP_STAT: Notifies that a perf client is no longer              */
/*     watching this stat.  dwData contains the stat handle.             */
/* Stats which are expensive to maintain should only be kept while some- */
/* one is watching them.  Note that there can be more than one stat      */
/* client, so don't just stop keeping track of a stat if you receive     */
/* a PMSG_STOP_STAT.  The server should keep a counter of the number     */
/* of PMSG_START_STAT's it receives for a particular counter, decrement  */
/* it for each PMSG_STOP_STAT and stop keeping track of the stat when the*/
/* counter reaches zero.                                                 */
/* Most stats are trivial to maintain and just involve incrementing a    */
/* counter.  For stats like these, perf servers should always increment  */
/* the counter and ignore messages to start and stop.                    */


/* IOCTL apis understood by perf (from ring 3 client) */

#define IOCTL_PERF_GET_STATS		0x10
#define IOCTL_PERF_START_STAT		0x11
#define IOCTL_PERF_STOP_STAT		0x12
/* On entry to the IOCTL_PERF_GET_STATS ioctl:
    lpvInBuffer 	pointer to array of DWORD stat handles
    cbInBuffer		size of array, in bytes
    lpvOutBuffer	pointer to result array (can be same as lpvInBuffer)
    cbOutBuffer		size of destination array
*/

/* ASM

; Ring-0 macros to aid stat registration

Reg_Perf_Srv MACRO level:REQ, flags:REQ, servername:REQ, nodename:REQ, controlfunc:REQ
	local	nothere
	VxDcall	PERF_Get_Version
	or	eax, eax
	jz	nothere

	IF (OPATTR(controlfunc)) AND 00010000y 	;; register
		push	controlfunc
	ELSE
		push	OFFSET32 controlfunc
	ENDIF

	IF (OPATTR(nodename)) AND 00010000y		;; register
		push	nodename
	ELSE
		push	OFFSET32 nodename
	ENDIF

	IF (OPATTR(servername)) AND 00010000y		;; register
		push	servername
	ELSE
		push	OFFSET32 servername
	ENDIF
	
	push	flags
        push	level
	push	esp
	VxDcall	PERF_Server_Register
	add	esp, 6*4
nothere:
	ENDM

Reg_Perf_Stat MACRO srvhandle:REQ, level:REQ, flags:REQ, name:REQ, nodename:REQ, unitname:REQ, desc:REQ, func:REQ
	IF (OPATTR(func)) AND 00010000y		;; register
		push	func
	ELSE
		push	OFFSET32 func
	ENDIF

	IF (OPATTR(desc)) AND 00010000y		;; register
		push	desc
	ELSE
		push	OFFSET32 desc
	ENDIF

	IF (OPATTR(unitname)) AND 00010000y	;; register
		push	unitname
	ELSE
		push	OFFSET32 unitname
	ENDIF

	IF (OPATTR(nodename)) AND 00010000y	;; register
		push	nodename
	ELSE
		push	OFFSET32 nodename
	ENDIF

	IF (OPATTR(name)) AND 00010000y		;; register
		push	name
	ELSE
		push	OFFSET32 name
	ENDIF
	
	push	flags
	push	level
	push	esp
	push	srvhandle
	VxDcall	PERF_Server_Add_Stat
	add	esp, 9*4
	ENDM

Begin_Service_Table PERF
PERF_Service PERF_Get_Version, LOCAL
PERF_Service PERF_Server_Register, LOCAL
PERF_Service PERF_Server_Deregister, LOCAL
PERF_Service PERF_Server_Add_Stat, LOCAL
PERF_Service PERF_Server_Remove_Stat, LOCAL
End_Service_Table PERF

*/

/* Registry constants follow.  A sample perf registry tree might look like
   this:

HKEY_LOCAL_MACHINE\STATS\VFAT			
			  NAME="32-bit file system"
			     \READS
		       +---    NAME="Reads per second"
		       |       HANDLE=<4 byte binary value>
	      Required |       DESCRIPTION="The number of file read requests
		       |                    per second"
		       |       VALUE=<some dynamic registry identifier>
		       +---    DIFFERENTIATE="TRUE"

		       +---    MIBID="1.3.4.7.3"
		       |       STARTSCALE=1000
	      Optional |       FREQUENCY="HIGH"
		       +---    SCALETYPE="LOG10"

*/

#define HKEY_PERF_ROOT			HKEY_LOCAL_MACHINE
#define PERF_REG_KEY			"STATS"
#define PERF_REG_NAME_SRV_NAME		"NAME"
#define PERF_REG_NAME_STAT_NAME		"NAME"
#define PERF_REG_NAME_STAT_FREQ		"FREQUENCY"
#define PERF_REG_NAME_STAT_HANDLE	"HANDLE"
#define PERF_REG_NAME_STAT_DESC		"DESCRIPTION"
#define PERF_REG_NAME_STAT_VALUE	"VALUE"
#define PERF_REG_NAME_STAT_DIFF		"DIFFERENTIATE"
#define PERF_REG_NAME_STAT_SCALETYPE	"SCALETYPE"
#define PERF_REG_NAME_STAT_STARTSCALE	"STARTSCALE"

#define PERF_REG_VAL_STAT_TRUE		"TRUE"
#define PERF_REG_VAL_STAT_FALSE		"FALSE"
#define PERF_REG_VAL_STAT_HIGH		"HIGH"
#define PERF_REG_VAL_STAT_LOW		"LOW"
#define PERF_REG_VAL_STAT_LINEAR	"LINEAR"
#define PERF_REG_VAL_STAT_LOG10		"LOG10"
#define PERF_STAT_PREFIX			"STAT"

/* complex stat defines */

#define PSTF_INT_COMPLEX	0x00000010
#define PSTF_EXT_COMPLEX	0x00000020
/* A complex statistic has no data value of its own-- it just defines two */
/* or more regular stats that may be added together to appear as a single */
/* value in the UI.  For example: VFAT->bytes written/sec and VFAT->bytes */
/* read/sec are simplex (normal) stats that each have a data counter.     */
/* VFAT->total bytes/sec is a complex stat made up of those two simplex   */
/* stats.  When the UI wants to display a complex stat's value, it gets   */
/* the data value for the simplex stats contained in it and adds those    */
/* values together.  To define a complex stat, set either PSTF_INT_COMPLEX*/
/* or PSTF_EXT_COMPLEX when registering the stat.  The pst0_pStatFunc     */
/* member should be set to point at a table of pointers to strings        */
/* containing the registry key names of the simplex stats.  The pointer   */
/* table should be null-terminated.  If PSTF_INT_COMPLEX is set, all	  */
/* stats must be internal to the VxD.  If PST_EXT_COMPLEX, the stats can  */
/* be from other VxDs (including the one registering the complex stat).   */
/* For internal complex stats, the key names in the table are the same	  */
/* as the key names used to register the stat.  For example: VFAT         */
/* registers itself with the "VFAT" key name, and registers two simple    */
/* stats with "BReadSec" and "BWriteSec" key names.  It then registers a  */
/* complex stat with a "BTotSec" key name, where the pst0_pStatFunc		  */
/* points to a table of pointers; the first pointer would point to        */
/* "BReadSec", the next pointer in the table would point to "BWriteSec",  */
/* and the next pointer would be NULL to signifiy the end of the list.    */
/* This is convenient because the "BReadSec" string already exists.  If   */
/* PSTF_EXT_COMPLEX is set, the strings that the pointer table points to  */
/* also have to contain the registry key name of the VxD which registered */
/* them.  In the above example, if VFAT were to set the PSTF_EXT_COMPLEX  */
/* flag the strings pointed to by the table would have to be              */
/* "VFAT\BReadSec" and "VFAT\BWriteSec".  The advantage of setting this   */
/* flag is that you can specify external stats, like "NDIS\PacketsSec".   */

/* This is complicated to explain, but very easy to do.                   */
/* See dos\dos386\vxd\perf\example\example.asm for an example of how to   */
/* register a complex stat.                                               */
