/*static char *SCCSID = "@(#)perfvwc.c  6.12 92/03/11";*/

#define SCCSID  "@(#)perfvwc.c  6.12 92/03/11"

/*
 *      Perfview Functions
 *
 *      This is an OS/2 2.x specific file
 *
 *      IBM/Microsoft Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1989
 *      Copyright (c) Microsoft Corporation 1987, 1989
 *
 *      All Rights Reserved
 *
 */
//     Modification History

//   Date     Programmer     For         Reason
// 2/28/91    Steve Best     DCR614.12   Added pvwcreateinstance_sem, pvwkillinstance_sem
// 6/3/91     Steve Best     b722136     Added pvwcreateinstance_fil, pvwkillinstance_fil
// 10/16/91   Steve Best     b728714     take out group that were no implement
//                                       wsq,que
// 10/29/91   Steve Best     b729110     take out VDM group
// 01/6/92    Steve Best     b731094     clear pbvdbfil in sft
// 02/2/92    Steve Best     aa04677     include pvwp.h private include for
//                                       DosConfigurePerf and DosAliasPerfCtrs
// 02/14/92   Steve Best     aa04705     return object name for file transient group
// 03/03/92   Steve Best     aa04740     clear bid after death msg is written to queue
// 03/08/92   Steve Best     b734380     take out 16 32 bit semaphores
//                                       take out process group
// 05/08/92   Steve Best     aa05233     call _pvwStopTimers to stop tmRun and
//                                       tmReady counters if they need to be
//                                       stopped
// 05/21/92   Steve Best     aa05408     create data blocks for pre-existing
//                                       transients
// 06/9/92    Steve Best     aa05446     check for bid =0 on de-registeration
// 07/28/92   Steve Best     49168       check to data blk address is valid
// 08/10/92   Steve Best     50175       check if there is db for thread if so
//                                       don't create one
// 08/20/92   Steve Best     51652       use task routines to get the process's
//                                       context when setting rpc_fl_config in data blk
// 09/08/92   Steve Best     53160       set pvwptcblast = 0 when msg queue is deconfig
// 09/24/92   Steve Best     sfb3        fix to get pid=1 data blk for pre-existing
//                                       transients
// 10/01/92   Steve Best     55134       use tasking routines to get the process's
//                                       context when re-setting rpc_fl_config in data blk
// 10/27/92   Steve Best     56354       do not free alias data block when doing
//                                       deconfigure DCF with free this memory
// 01/06/93   Steve Best     58461       check for invalid JFN address
// 08/09/93   Steve Best     pmr7x112    hob ptda not right for thread 1 of process
// 08/30/93   C. Cummings    71695       call VMFreeKHB after pvwWriteMsg because data block
//                                       can get blown away while VMFreeKHB is blocked leaving
//                                       bad pData for pvwWriteMsg
// 01/19/95   Bill Miller   108584       avoid dereferencing a null msg queue.
// 03/15/95   Change Team   101504       Attached(linkedlist) memory allocated by pvwduptextblk
//                                       to pPvwDataBlk(added to ptda struct). Added new
//                                       function "pvwDestroylink" so that when the process
//                                       is terminated it goes through the linkedlist and
//                                       frees the memory allocated by pvwDupTextBlk.
//

#define INCL_TYPES              // for basedef.h typedefs
#define INCL_DOSERRORS          // for bseerr.h error codes


#include <basemac.h>
#include <basedef.h>

#define FNO_NUM FNO_PERFVWC_C

//aa05408 - added following for readability purposes
#define REGISTER_FILE   (USHORT)1             //aa05408
#define REGISTER_THREAD (USHORT)0             //aa05408
#define USE_TCB         (USHORT)0xffff        //aa05408 - indicates invalid handle
#define NO_MFT          (ULONG)1              //46691

#include <fnoperf.h>

#include <osmac.h>
#include <ostypes.h>
#include <infoseg.h>
#include <osconst.h>
#include <bseerr.h>
#include <dossymc.h>
#include <seldesc.h>
#include <sigdef.h>
#include <except.h>
#include <mi.h>
#include <pgexport.h>
#include <vmexport.h>
#include <tkexport.h>
#include <semxport.h>
#include <proc.h>
#include <sem.h>
#include <screen.h>
#include <curdir.h>
#include <dirent.h>
#include <find.h>
#include <tkptda.h>
#include <sft.h>
#include <ldrxport.h>
#include <selxport.h>
#include <kmode.h>
#include <bsetib.h>                                                    //aa05408
#include <tklocal.h>                                                   //aa05408
#include <vmowner.h>
#include <memory.h>
#include <string.h>
#include <dos.h>                // _enable(), _disable().
#include <pvwxport.h>
#include <perfvw.h>             //needs mi.h
#include <pvwkdata.h>           //Kernel entity Data structures.
#include <pvwlocal.h>
#include <pvwp.h>               //DosConfigurePerf and DosAliasPerfCtrs //aa04677

/* BUGBUG 26-Jun-90, DaveBe, Currently, the following restrictions exist and
 * will be fixed at a later date:
 *      RegisterPerfCtrs():
 *              1) Limited as to the number of Registrants (RPC_MAX_DESC).
 *              2) Dos32RegisterPerfCtrs() does NOT use TKSuBuff() or its
 *                 derivatives; thus it does not yet validate User ptrs.
 *              3) When an instance of a (non-kernel) transient entity is
 *                 registered, its configuration chain should be scanned.  If
 *                 it is to be configured, and is not already (kernel
 *                 transients will ONLY be registered if they are configured),
 *                 then the instance should be CONFIGURED as well as
 *                 REGISTERED.  This is not currently done.
 *      ConfigurePerf():
 *              1) Only configures by BID, not by Entity or Process
 *                 name.
 *              2) No support for config. of instances of kernel transient
 *                 entities.  Configuration of a BID only succeeds for those
 *                 instances currently registered.  Since non-kernel entities
 *                 ALWAYS register their instances with Perfview, Reg. Perf.
 *                 Desc.s (RPD) exist for their instances and they can
 *                 be configured.  However, with kernel transient entities,
 *                 only instances of that group which are subsequently
 *                 created are configured for measurement (their
 *                 instance data blocks are allocated, etc.).  No
 *                 scan is performed of existing (unregistered) kernel
 *                 transient instances.  These were created before Perfview
 *                 was started, or were created while they were unconfigured.
 *                 Perfview should check for all instances of an entity which
 *                 is being configured, and simulate the creation process
 *                 (which would include a registration) for all appropriate
 *                 instances.  This is not currently done.
 *      General:
 *              1) A semaphore should be implemented to prevent concurrent
 *                 access to global variables by different threads.  (For
 *                 instance, I don't want mulitple threads accessing
 *                 bidPvwLastRPD.bid_usGroup through PVWRegisterPerfCtrs.
 *              2) When a transient kernel entity is created, an incomplete
 *                 configuration test is performed (just CFG_FL_ALL).  See
 *                 pvwTestConfig().
 *              3) Non-kernel transient entities are registered regardless
 *                 of whether they are configured.  Kernel entities are
 *                 registered ONLY if their Group is configured.  This is
 *                 not really a problem, but it does differentiate the 2
 *                 types of entities.  See above comments in
 *                 RegisterPerfCtrs() and ConfigurePerf().
 */


// PROTOTYPES

INT cdecl VR32AliasPerfCtrs ( THUNKSEF sef,
                              ULONG    ulRangeType,
                              ULONG    ulInfo,
                              PBYTE    *ppbRangeStart,
                              PULONG   pulRangeSize );

INT cdecl VR32ConfigurePerf ( THUNKSEF sef,
                              ULONG    ulEntityType,
                              ULONG    ulConfigType,
                              ULONG    ulInfo1,
                              ULONG    ulInfo2,
                              PSZ      pszConfigSpec,
                              BOOL     fExclude );

INT cdecl VR32DeconPerf ( THUNKSEF sef );

INT cdecl VR32RegisterPerfCtrs (  THUNKSEF sef,
                                  PBYTE    pbDataBlk,
                                  PBYTE    pbTextBlk,
                                  ULONG    flFlags );

#ifdef PERFVIEW

INT PVWSetHook ( ULONG ulEI,
                 PFN   pfnMt );

INT PVWUnSetHook ( ULONG ulEI );

PVOID pvwGet032Addrs( PVOID pBlk,
                      ULONG flFlags );

INT pvwCalCTimer( VOID );

INT pvwFindRPDesc ( PBID     pbidIn,
                    prpd_t * pprpdOut);

//aa05408ULONG PvwMtFilDataBlk(ptda_t *pptda);                         //b722136
ULONG PvwMtFilDataBlk(ptda_t *pptda,                                   //b722136
                      tcb_t  *ptcb,                                    //aa05408
                      USHORT handle);                                  //aa05408

PVOID pvwStopTimers( ULONG pbDataBlk);                                 //aa05233

// EXTERNALS

extern PFUNC PvwExcRouterTable[];
extern PCHAR PvwMtStartLock;
extern PCHAR PvwMtEndLock;
extern PFUNC PvwMtRouterTable[];
extern PBYTE *ppbPvwMtHook;

extern INT (*pfnTMR16AddTime)(PTIMR);
extern INT (*pfnTMR16SubTime)(PTIMR);
extern INT TMR32AddTime(PTIMR);
extern INT TMR32SubTime(PTIMR);

extern INT pvwCalibrateTimers(VOID);

//      Perfview kernel entity text blocks.

extern pvtbh_t pvtbh_cpu;
extern pvtbh_t pvtbh_mem;
extern pvtbh_t pvtbh_fbc;
extern pvtbh_t pvtbh_fil;
//extern pvtbh_t pvtbh_lib;                                            //b728714
//extern pvtbh_t pvtbh_prc;                                            //b734380
extern pvtbh_t pvtbh_thd;
//extern pvtbh_t pvtbh_wsq;                                            //b728714
extern pvtbh_t pvtbh_sem;
//extern pvtbh_t pvtbh_que;                                            //b728714
//extern pvtbh_t pvtbh_vdm;                                            //b729110
extern pvtbh_t pvtbh_spr;
extern pvtbh_t pvtbh_cal;
extern pvtbh_t pvtbh_instance;

extern ulong_t Pvwptcblast;                                            //53160
// GLOBAL VARIABLES

HKSEM hKSEMPerf = 0;          // Perf mutex sem.

//      Perfview status flag:  TRUE => Perfview active.

BOOL    fPerfview = FALSE;

//      Pointer to Perfview Message Queue (Message Queue Header (MQH)
//      followed by contiguous messages).

PBYTE   pbPvwMsgQ = NULL;

//      Perfview Kernel Initialization Reference count.

USHORT  cusPvwKInitRef = 0;

//      Global Perfview flags: (see perfvw.h for PVW_FL_...)

ULONG   flPvwFlags = 0;

//      Address of first byte after BMP header of Registered Perf counter
//      Descriptor object.

void *  pbhPvwRPD = NULL;

//      Address of first byte after BMP header of registered perf counter
//      Configuration specification object.

void *  pbhPvwCfg = NULL;

/*      Address of the HEAD of a doubly linked chain of Descriptors for
 *      Registered Perf counters.  The last GROUP in the chain is doubly
 *      linked to the HEAD.  The last INSTANCE in each GROUP is doubly linked
 *      to its corresponding GROUP.  The HEAD is initially linked to itself,
 *      and any GROUP without associated INSTANCES has INSTANCE pointers
 *      which are linked to itself.
 *
 *      The CONFIG specifier chain is also doubly linked in a similar manner.
 *      The CONFIG chain is singularly linked to its corresponding GROUP.
 *      The first CONFIG record (HDCONFIG) serves as the Head of the CONFIG
 *      chain and always exists.
 *
 *                    pPvwRPDHead
 *                || /
 *               =HEAD=
 *                ||
 *                ||
 *               =GROUP==INSTANCE==INSTANCE=
 *                ||  \
 *                ||  =HDCONFIG==CONFIG==CONFIG=
 *                ||
 *               =GROUP==INSTANCE=
 *                ||  \
 *                ||  =HDCONFIG==CONFIG=
 *                ||
 *               =GROUP=
 *                ||  \
 *                ||  =HDCONFIG=
 *                ||
 *               =GROUP==INSTANCE==INSTANCE==INSTANCE=
 *                ||  \
 *                    =HDCONFIG=
 */

prpd_t  pPvwRPDHead = NULL;

//      Last assigned Registered Perf counter Descriptor Block ID.

BID     bidPvwLastRPD = {0,0};

//      Handle of locked Perfview measurement function memory.

struct vmlh_s hPvwMtLock;

//      Kernel Entity Descriptors for Persistent entities.

ked_pers_t ked_pers = {
        {ET_FLM_KRNL_PERS, NULL, sizeof(pvdb_cpu_t), PVW_MAX_BLKS_KRNL_PERS, (PBYTE) &pvtbh_cpu},
        {ET_FLM_KRNL_PERS, NULL, sizeof(pvdb_mem_t), PVW_MAX_BLKS_KRNL_PERS, (PBYTE) &pvtbh_mem},
        {ET_FLM_KRNL_PERS, NULL, sizeof(pvdb_fbc_t), PVW_MAX_BLKS_KRNL_PERS, (PBYTE) &pvtbh_fbc},
                                                                       //b728714
//      {ET_FLM_KRNL_PERS, NULL, sizeof(pvdb_wsq_t), PVW_MAX_BLKS_KRNL_PERS, (PBYTE) &pvtbh_wsq},
                                                                       //b729110
//      {ET_FLM_KRNL_PERS, NULL, sizeof(pvdb_vdm_t), PVW_MAX_BLKS_KRNL_PERS, (PBYTE) &pvtbh_vdm},
        {ET_FLM_KRNL_PERS, NULL, sizeof(pvdb_spr_t), PVW_MAX_BLKS_KRNL_PERS, (PBYTE) &pvtbh_spr},
        {ET_FLM_KRNL_PERS, NULL, sizeof(pvdb_cal_t), PVW_MAX_BLKS_KRNL_PERS, (PBYTE) &pvtbh_cal},
};

//      Kernel Entity Descriptors for Transient entities.

ked_tran_t ked_tran = {
        {ET_FLM_KRNL_TRAN, NULL, sizeof(pvdb_fil_t), MAXSFN, (PBYTE) &pvtbh_fil},
//b734380{ET_FLM_KRNL_TRAN, NULL, sizeof(pvdb_prc_t), TKCPTDAMAX, (PBYTE) &pvtbh_prc},
        {ET_FLM_KRNL_TRAN, NULL, sizeof(pvdb_thd_t), TKCTCBMAX,  (PBYTE) &pvtbh_thd},
//      {ET_FLM_KRNL_TRAN, NULL, sizeof(pvdb_sem_t), PVW_MAX_BLKS_KRNL_TRAN, (PBYTE) &pvtbh_sem},
//b734380{ET_FLM_KRNL_TRAN, NULL, sizeof(pvdb_sem_t), MAXSFN, (PBYTE) &pvtbh_sem},
                                                                       //b728714
//      {ET_FLM_KRNL_TRAN, NULL, sizeof(pvdb_que_t), PVW_MAX_BLKS_KRNL_TRAN, (PBYTE) &pvtbh_que},
};


#ifdef TESTONLY
//      Maximum size ever reached by Message Queue before it is flushed.

ULONG   ulMaxMsgQSize = 0;
#endif //TESTONLY

#endif //PERFVIEW

#pragma code_seg("PERFSWAP_TEXT", "CODE")


#ifdef PERFVIEW

/***LP  pvwWriteMsg
 *
 *      Write a message to the message queue.
 *
 *      ENTRY   pbBlk     -- Msg block to be written.
 *              cbSize    -- Size of block to be written.
 *              flFlags   -- Msg flags.
 *
 *      EXIT    NO_ERROR - success
 *
 *      NOTES   Place block preamble (PRE) in message queue, along
 *              with a message block.  The message queue must exist
 *              (PVW_FL_MSGQ) and must NOT be in an overflow state
 *              (PVW_FL_MSGQOF); otherwise, the message is ignored.
 *
 */

INT pvwWriteMsg( PBYTE pbBlk,
                 ULONG cbSize,
                 ULONG flFlags)
{
        INT     irc;
        PBYTE   pbMsgWt;        // Ptr to message buffer write address.
        ULONG   cbMsgFree;      // Amount of free space in msg buffer.
        TIMR    tmStamp;        // Time stamp storage.

        if ((flPvwFlags & PVW_FL_MSGQ) && !(flPvwFlags & PVW_FL_MSGQOF))
        {
                //Take Message Queue mutex semaphore while writing to Queue.

                if ((irc = KSEMRequestMutex((HKSEM) ((PMQH) pbPvwMsgQ)->mqh_hmtxMsgQ,
                                            PVW_MSGQ_TIMEOUT)) != NO_ERROR)
                {
                        return(irc);
                }

                // If time stamp invalid, continue with tmStamp = 0.

                if ((irc = TMR32QueryTime_CQW(SSToDS(&tmStamp))) != NO_ERROR)
                        tmStamp.qw_ulHi = tmStamp.qw_ulLo = 0;

                pbMsgWt = ((PMQH) pbPvwMsgQ)->mqh_pbWt;
                cbMsgFree = pbPvwMsgQ + ((PMQH) pbPvwMsgQ)->mqh_cbSize - pbMsgWt;

                /* Msg buffer must be able to hold entire message (preamble +
                 * msg block) plus another preamble (to indicate queue
                 * overflow if, when the next message write is
                 * attempted, it is found that it cannot fit in buffer).
                 */

                if (cbMsgFree > (2*sizeof(PRE) + cbSize))
                {
                        ((PRE *) pbMsgWt)->pre_ulTotLen = sizeof(PRE) + cbSize;

                        if (flFlags & ET_FL_TEXT)
                        {
                            ((PRE *) pbMsgWt)->pre_bidID = ((PTBH) pbBlk)->tbh_bidID;
                        }
                        else
                        if (flFlags & ET_FL_DATA)
                        {
                            ((PRE *) pbMsgWt)->pre_bidID = ((PDBH) pbBlk)->dbh_bidID;
                        }
                        else
                        {
                            ((PRE *) pbMsgWt)->pre_bidID.bid_usInstance = \
                            ((PRE *) pbMsgWt)->pre_bidID.bid_usGroup = 0;
                        }

                        ((PRE *) pbMsgWt)->pre_ulEntityType = flFlags;
                        ((PRE *) pbMsgWt)->pre_tmStamp = tmStamp;

                        pbMsgWt += sizeof(PRE);

                        // Write message block to message buffer.

                        memcpy (pbMsgWt,pbBlk,cbSize);

                        ((PMQH) pbPvwMsgQ)->mqh_pbWt += (sizeof(PRE) + cbSize);
                }
                else
                {
                        // Message queue overflow.  Put overflow msg in Q.

                        flPvwFlags |= PVW_FL_MSGQOF;

                        ((PRE *) pbMsgWt)->pre_ulTotLen = sizeof(PRE);
                        ((PRE *) pbMsgWt)->pre_bidID.bid_usInstance = \
                        ((PRE *) pbMsgWt)->pre_bidID.bid_usGroup = 0;
                        ((PRE *) pbMsgWt)->pre_ulEntityType = ET_FL_MSGQOF;
                        ((PRE *) pbMsgWt)->pre_tmStamp = tmStamp;

                        ((PMQH) pbPvwMsgQ)->mqh_pbWt += sizeof(PRE);
                }

                // Release Msg Q mutex semaphore.

                KSEMReleaseMutex((HKSEM) ((PMQH) pbPvwMsgQ)->mqh_hmtxMsgQ);

                // Notify data collector of message in queue.

                dhw_PostEventSem(((PMQH) pbPvwMsgQ)->mqh_hevMsgQ);
        }

        return(NO_ERROR);
}


/***LP  pvwTestConfig
 *
 *      Test if an instance of an entity is configured for measurement.
 *
 *      ENTRY   pbid      -- Ptr to Block ID (BID) of entity of test.
 *              pptda     -- Ptr to PTDA for process owning this thread.
 *
 *      EXIT    NO_ERROR - success
 *              ERROR_PVW_INVALID_SPEC - if not configured
 *
 *      NOTES
 *
 */

INT pvwTestConfig( PBID   pbid,
                   ptda_t *pptd)
{
        INT     irc;
        BID     bid;
        prpd_t  prpd;

        bid.bid_usGroup = pbid->bid_usGroup;
        bid.bid_usInstance = 0;

        // Find Instance 0 of this group, and scan the configuration chain.

        if ((irc = pvwFindRPDesc(SSToDS(&bid), SSToDS(&prpd))) != NO_ERROR)
                return(irc);

        /* Exit with NO_ERROR if ALL instances of this entity are to be
         * configured.
         */

        if (prpd->rpd_pConfigHead->cfg_flFlags & CFG_FL_ALL)
        {
                return(NO_ERROR);
        }
        else
        {
// BUGBUG 28-Jul-90, DaveBe, Must add code to scan configuration chain for
// matching process or entity name; if found, must check if it is excluded
// (CFG_FL_EXCLUDE) or not.

                return(ERROR_PVW_INVALID_SPEC);
        }
}


/***LP  pvwRegKrnlInstance
 *
 *      Register an instance of a kernel transient entity.
 *
 *      ENTRY   p-- Ptr to PTDA for process owning this thread.
 *              ptcb       -- Ptr to TCB for this thread.
 *              pked       -- Ptr to kernel entity descriptor.
 *              ppbDataBlk -- Address of ptr to data block for this instance.
 * aa04705      objvalue   -- Tells if a object name should be in the txt blk
 * aa05408      handle     -- handle SFN (-1 indicates TCB is to be used)
 *
 *      EXIT    NO_ERROR - success
 *
 *      NOTES
 *
 */

INT pvwRegKrnlInstance( ptda_t *pptda,
                        tcb_t  *ptcb,
                        ked_t  *pked,
                        PBYTE  *ppbDataBlk,                            //aa04705
                        USHORT objvalue,                               //aa04705
                        USHORT handle)                                 //aa05408
{
        ULONG mftaddress;                                              //aa04705
        PBYTE pbDataBlk = *ppbDataBlk;



//aa05408  If we are registering a file instance and there is not
//aa05408  MFT associated with it, then return at this point
//
        if (objvalue) {
          mftaddress = PvwGetMFTName(ptcb,handle);
//46691   if (!mftaddress) return (NO_ERROR);
          if (!mftaddress) return (NO_MFT);    //46691
        }

        // Init. data block = 0.

        memset( pbDataBlk,
                0,
                pked->ked_cbDataSize);

        // Init. data block header (assumes hdr comes
        // first in data blk)

        ((PDBH) pbDataBlk)->dbh_bidID = ((PTBH) pked->ked_pbTextBlk)->tbh_bidID;
        ((PDBH) pbDataBlk)->dbh_ulTotLen = pked->ked_cbDataSize;
        ((PDBH) pbDataBlk)->dbh_flFlags |= (RPC_FL_32BIT | RPC_FLM_KRNL_TRAN);
        (PBYTE *) ((PDBH) pbDataBlk)->dbh_pfnTmrAdd = ppbDataBlk;

        /* Build a text block for registration using the statically allocated
         * instance text block template (pvtbh_instance).  Fill-in the
         * appropriate fields, including the members of the text block name
         * array (apvtbn_instance[]) which serve as instance descriptors
         * (Process name, PID, TID, Slot #).
         */

        pvtbh_instance.tbh_bidID = ((PTBH) pked->ked_pbTextBlk)->tbh_bidID;
        pvtbh_instance.tbh_tbnGroup.tbn_pszName = \
        pvtbh_instance.tbh_tbnInstance.tbn_pszName = \
                        ((PTBH) pked->ked_pbTextBlk)->tbh_tbnGroup.tbn_pszName;

        pvtbh_instance.tbh_atbnName[INST_PROCNAME].tbn_pszName = \
                                                pptda->ptda_pszproc;

        pvtbh_instance.tbh_atbnName[INST_PID].tbn_ulFlags = \
                                                (ULONG) pptda->ptda_infoseg.LIS_CurProcID;

//      if we are processing transient files, then thread and slot id cannot
//      be correlated, thus use 0.
        if (handle==USE_TCB) {
            pvtbh_instance.tbh_atbnName[INST_TID].tbn_ulFlags = \
                                                (ULONG) ptcb->TCBOrdinal;
            pvtbh_instance.tbh_atbnName[INST_SLOT].tbn_ulFlags = \
                                                (ULONG) ptcb->TCBNumber;
        }
        else {
            pvtbh_instance.tbh_atbnName[INST_TID].tbn_ulFlags = \
            pvtbh_instance.tbh_atbnName[INST_SLOT].tbn_ulFlags = 0L;
        }                                                               //aa04705

//aa05408  If object is a file then use mft name , else use a blank file name
//
        if (objvalue==REGISTER_FILE)
            pvtbh_instance.tbh_atbnName[INST_OBJNAME].tbn_pszName = mftaddress;
        else
         /* place blank for name of object if the object is not file */
            pvtbh_instance.tbh_atbnName[INST_OBJNAME].tbn_pszName = " ";

        // Register instance counter block.


        return(PVWRegisterPerfCtrs((PBYTE) pbDataBlk,
                                   (PBYTE) &pvtbh_instance,
                                   (RPC_FL_32BIT | RPC_FLM_KRNL_TRAN)));
}


/***EP  PVWCreateInstance_Thd
 *
 *      Create an instance of a thread entity.
 *
 *      ENTRY   pptda   -- Ptr to PTDA for process owning this thread.
 *              ptcb    -- Ptr to TCB for this thread.
 *
 *      EXIT    NO_ERROR - success
 *
 *      NOTES
 *
 */

INT PVWCreateInstance_Thd (ptda_t *pptda,
                           tcb_t  *ptcb)
{
        INT     irc;
        ked_t   *pked;
        PBID    pbid;

        KSEMRequestMutex( hKSEMPerf, -1 );      // crs-sem

//46691 ptcb->TCB_pPVDBThd = NULL;                                     //b731094
      if (ptcb->TCB_pPVDBThd == NULL)                                  //46691
        {
        if ((flPvwFlags & (PVW_FL_MSGQ | PVW_FLM_KRNL_INIT)) == \
                          (PVW_FL_MSGQ | PVW_FLM_KRNL_INIT))
        {
                /* Test if this instance of this entity is configured for
                 * measurement.  Exit if it is not configured.
                 */

                pked = &ked_tran.tran_thd;
                pbid = &((PTBH) pked->ked_pbTextBlk)->tbh_bidID;

                if ((irc = pvwTestConfig(pbid,pptda)) != NO_ERROR)
                {
                        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                        return(irc);
                }

                // Allocate a thread data block and place its address in TCB.

                if((irc = BMPGet((void *)pked->ked_pbDataBlk,
                                 (void *)&ptcb->TCB_pPVDBThd)) != NO_ERROR)
                {
                        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                        return(irc);
                }

                if((irc = pvwRegKrnlInstance(pptda,
                                             ptcb,
                                             pked,
                //aa04705                    &ptcb->TCB_pPVDBThd)) != NO_ERROR)
                                             &ptcb->TCB_pPVDBThd,
                                             REGISTER_THREAD,      //aa05408
                                             USE_TCB)) != NO_ERROR)
                {
                        // Deallocate data block.

                        BMPFree((void *)pked->ked_pbDataBlk,
                                (void *)ptcb->TCB_pPVDBThd);

                        ptcb->TCB_pPVDBThd = NULL;

                        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                        return(irc);
                }
        }

        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
        return(NO_ERROR);
        }                         //46691

   KSEMReleaseMutex( hKSEMPerf );       // crs-sem ?
   return(NO_ERROR);
}

/***LP  pvwReadPtda - Worker routine for TKScanTasks                   //aa05408
 *
 *      ulong_t pvwReadPtda (pPTDA, pArg)
 *
 *      Get all per process information
 *
 *      ENTRY   pPTDA   - Pointer to ptda
 *              pArg    - Unused
 *
 *      EXIT    ulong_t - Standard error code
 *
 *      MODIFIES
 *
 *
 */

ulong_t pvwReadPtda(pptda, pArg)
ptda_t  *pptda;
void    *pArg;
{
        ulong_t         irc;
        tcb_t  *ptcb;
        register ushort_t       *pJFN_Table;
        register int            handle;

// Get a data block for each thread in this process
//
   for (ptcb = pptda->ptda_pTCBHead; ptcb != NULL;ptcb = ptcb->TCBpTCBNext)
      {                                                                //46691

      if (ptcb->TCB_pPVDBThd == NULL)                                  //50175
        {                                                              //50175
        irc = PVWCreateInstance_Thd(pptda,ptcb);
        }                                                              //50175
      }                                                                //46691
// check if the process has grown the JFN and it is terminating        //58461
// we use 0 in the sel field of the JFN ptable in ptda to determine this//58461

   if (pptda->JFN_Length != FILPERPROC )                               //58416
     {                                                                 //58461
     if (SEGMENTOF16(pptda->JFN_pTable) == 0)                          //58461
        return(irc);                                                   //58461
     }                                                                 //58461
// If JFN table has not been grown yet, then use default table
// otherwise use grown table.
//
   if (pptda->JFN_Length == FILPERPROC )
        pJFN_Table = (ushort_t *) ((uchar_t *) pptda +
                        (OFFSETOF16(pptda->JFN_pTable) - TKOPTDA));
   else
   {                                                                    //58461
   pJFN_Table =  SELConvertToLinear
                      ((ULONG)(SEGMENTOF16(pptda->JFN_pTable)),pptda);

   if (pJFN_Table == (ulong_t)-1)   // error return                    //58461
    return(irc);                                                       //58461
  }                                                                    //58461
// Copy SFNs for each handle out and get counter data block
//
   for (handle = 0; handle < pptda->JFN_Length; handle++) {
       if (SFN_NULL != (short)*pJFN_Table )
         irc = PVWCreateInstance_Fil
                   (pptda, pptda->ptda_pTCBHead, (short)*pJFN_Table);

       pJFN_Table++;
    }
    return(irc);
}


/***EP  PVWCreateInstance_preThd                                       //aa05408
 *
 *      Create an instance for all pre existing threads
 *
 *      ENTRY   Nothing
 *
 *
 *      EXIT    NO_ERROR - success
 *
 *      NOTES
 *
 */

INT PVWCreateInstance_preThd ( VOID)

{
        INT      rc;

            /* Get info for all processes */

            /* Get Sysint's process information separately; TKScanTasks will
             * skip it since it is marked as DYING
             */
//sfb3      rc = pvwReadPtda(pPTDAFirst, (void *) NULL);

            return(TKScanTasks(TK_TG_ALL|TK_SF_DYING|TK_SF_WRETCODE, 0,
                                pvwReadPtda,(void *)NULL));

//sfb3      return(TKScanTasks(TK_TG_ALL|TK_SF_WORKER|TK_SF_WRETCODE, 0,
//sfb3                          pvwReadPtda,(void *)NULL));

}                                                                      //aa05408

/***EP  PVWKillInstance_Thd
 *
 *      Terminate an instance of a thread entity.
 *
 *      ENTRY   ptcb    -- Ptr to TCB for this thread.
 *
 *      EXIT    NO_ERROR - success
 *
 *      NOTES
 *
 */

INT PVWKillInstance_Thd (tcb_t  *ptcb)
{
        INT irc = NO_ERROR;
        PBYTE pbDataBlk;

        KSEMRequestMutex( hKSEMPerf, -1 );      // crs-sem

        pbDataBlk = (PBYTE) ptcb->TCB_pPVDBThd;

        if (pbDataBlk != NULL)
        {
           // check to see if we have a valid address                  //49168
           if (TKProbeBuff ((char *)pbDataBlk,sizeof (ulong_t *),      //49168
               (TK_UM_READ )))                                         //49168
               {                                                       //49168
                 *((ULONG *)ptcb->TCB_pPVDBThd) = NULL;                //49168
                 KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                 return(ERROR_PROTECTION_VIOLATION);                   //49168
               }                                                       //49168

                pvwStopTimers(pbDataBlk);                             //aa05233
                // De-register instance counter block.  This also clears
                // ptcb->TCB_pPVDBThd.

                if (irc = PVWRegisterPerfCtrs(pbDataBlk,              //49168
                                          NULL,
                         (RPC_FL_32BIT | RPC_FLM_KRNL_TRAN | RPC_FL_DEREG))   == NO_ERROR)
                 {                                                     //49168
                // Deallocate data block.

                 BMPFree((void *)ked_tran.tran_thd.ked_pbDataBlk,
                        (void *)pbDataBlk);
                 }
        }

        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
        return(irc);
}

/***EP  pvwDestroylink
 *
 *      It will destroy link list created in pvwDuptextBlk
 *
 *      ENTRY   pptda   -- Ptr to the process.
 *
 *      NOTES   Function added as a result of Defect # 101504
 *              Called by tkexec.c
 */

VOID PVWDestroyLink(ptda_t *pptda)
{
        PULONG pTemp;
        PULONG pNext;

       pTemp = pptda->pPvwDataBlk;

       for ( ; (pNext = (PULONG)*pTemp) != (PULONG)NULL; pTemp = pNext)
       {
          VMFreeKHB(VM_HKH_PUB_SWAPRW, (char *)pTemp);
       }
       VMFreeKHB(VM_HKH_PUB_SWAPRW, (char *)pTemp);

}

/***EP  PVWCreateInstance_Fil                                          //b722136
 *
 *      Create an instance of a file entity.
 *
 *      ENTRY   pptda   -- Ptr to PTDA for process owning this thread.
 *              ptcb    -- Ptr to TCB for this thread.
 *              usehandle -- Use the handle                            //aa05408
 *              handle    -- SFN handle                                //aa05408
 *
 *      EXIT    NO_ERROR - success
 *
 *      NOTES
 *
 */

INT PVWCreateInstance_Fil (ptda_t *pptda,
                           tcb_t  *ptcb,
                           USHORT handle)                              //aa05408
{
        INT     irc;
        ked_t   *pked;
        PBID    pbid;
        ULONG    dbaddress;
                                                                       //b731094
    KSEMRequestMutex( hKSEMPerf, -1 );      // crs-sem

//aa05408dbaddress = PvwMtFilDataBlk(pptda);  //get location of the pvwdb field in SFT
    dbaddress = PvwMtFilDataBlk(pptda,ptcb,handle);  //get location of the pvwdb field in SFT
    if (dbaddress ) {
//       *((ULONG *)dbaddress) = NULL;        //clear pbvdbfil sft field//b731094
    if ( *((ULONG *)dbaddress) == NULL)        //clear pbvdbfil sft field//b731094
    {
        if ((flPvwFlags & (PVW_FL_MSGQ | PVW_FLM_KRNL_INIT)) == \
                          (PVW_FL_MSGQ | PVW_FLM_KRNL_INIT))
        {
                /* Test if this instance of this entity is configured for
                 * measurement.  Exit if it is not configured.
                 */

                pked = &ked_tran.tran_fil;
                pbid = &((PTBH) pked->ked_pbTextBlk)->tbh_bidID;

                if ((irc = pvwTestConfig(pbid,pptda)) != NO_ERROR)
                {
                        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                        return(irc);
                }

                // Allocate a file/pipe data block and place its address in SFT.
                if((irc = BMPGet((void *)pked->ked_pbDataBlk,
                                 (void *)dbaddress)) != NO_ERROR)
                {
                        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                        return(irc);
                }

                if((irc = pvwRegKrnlInstance(pptda,
                                             ptcb,
                                             pked,
                                             dbaddress,                //aa04705
                                             REGISTER_FILE,            //aa05408
                                             handle   )) != NO_ERROR)  //aa05408
                {
                        // Deallocate data block.

                        BMPFree((void *)pked->ked_pbDataBlk,
                                (void *)*((ULONG *)dbaddress));

                        *((ULONG *)dbaddress) = NULL;
                        if (irc == NO_MFT)                              //46691
                          {
                          KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                          return (NO_ERROR);                            //46691
                          }
                        else                                            //46691
                          {
                          KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                          return(irc);                                    //46691
                          }
                }
        }
    }
        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
        return(NO_ERROR);
   }                                                                   //aa05408
   KSEMReleaseMutex( hKSEMPerf );       // crs-sem ???????????????
   return(NO_ERROR);
}                                                                      //b722136

/***EP  PVWKillInstance_Fil                                            //b722136
 *
 *      Terminate an instance of a file/pipe entity.
 *
 *      ENTRY   pptda    -- Ptr to ptda for this thread.
 *
 *      EXIT    NO_ERROR - success
 *
 *      NOTES
 *
 */

INT PVWKillInstance_Fil (ptda_t *pptda)
{
        INT irc = NO_ERROR;
        ULONG dbaddress;
        PBYTE pbDataBlk = NULL;

        KSEMRequestMutex( hKSEMPerf, -1 );      // crs-sem

//aa05408 dbaddress = PvwMtFilDataBlk(pptda);  //get location of the pvwdb field in SFT

        dbaddress = PvwMtFilDataBlk(pptda,0,USE_TCB);  //get location of the pvwdb field in SFT

        pbDataBlk = *((ULONG *)dbaddress);   //get the value of pvwdb field

        if (pbDataBlk != NULL)
        {
           // check to see if we have a valid address                  //49168
           if (TKProbeBuff ((char *)pbDataBlk,sizeof (ulong_t *),      //49168
               (TK_UM_READ )))                                         //49168
               {                                                       //49168
                 *((ULONG *)dbaddress) = NULL;                         //49168
                 KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                 return(ERROR_PROTECTION_VIOLATION);                   //49168
               }                                                       //49168

                // De-register instance counter block.  This also clears
                // ((sf_entry * ) ptcb->TCBThisSFT)->sf_fsi.pvwDB

                irc = PVWRegisterPerfCtrs(pbDataBlk,
                                          NULL,
                                          (RPC_FL_32BIT | RPC_FLM_KRNL_TRAN | RPC_FL_DEREG));

                // Deallocate data block.
                if (irc == NO_ERROR)                        //49168
                 {                                          //49168
                 BMPFree((void *)ked_tran.tran_fil.ked_pbDataBlk,
                        (void *)pbDataBlk);
                 }                                          //49168
        }

        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
        return(irc);
}                                                                      //b722136
//b734380/***EP  PVWCreateInstance_Prc
//b734380 *
//b734380 *      Create an instance of a process entity.
//b734380 *
//b734380 *      ENTRY   pptda   -- Ptr to PTDA for this process.
//b734380 *              ptcb    -- Ptr to TCB for this thread (Thread 1).
//b734380 *
//b734380 *      EXIT    NO_ERROR - success
//b734380 *
//b734380 *      NOTES
//b734380 *
//b734380 */
//b734380
//b734380INT PVWCreateInstance_Prc (ptda_t *pptda,
//b734380                           tcb_t  *ptcb)
//b734380{
//b734380        INT irc;
//b734380        ked_t   *pked;
//b734380        PBID    pbid;
//b734380
//b734380        pptda->ptda_pPVDBPrc = NULL;   //clear pPVDBPrc field          //b731094
//b734380        if ((flPvwFlags & (PVW_FL_MSGQ | PVW_FLM_KRNL_INIT)) == \
//b734380                          (PVW_FL_MSGQ | PVW_FLM_KRNL_INIT))
//b734380        {
//b734380                /* Test if this instance of this entity is configured for
//b734380                 * measurement.  Exit if it is not configured.
//b734380                 */
//b734380
//b734380               pked = &ked_tran.tran_prc;
//b734380                pbid = &((PTBH) pked->ked_pbTextBlk)->tbh_bidID;
//b734380
//b734380                if ((irc = pvwTestConfig(pbid,pptda)) != NO_ERROR)
//b734380                {
//b734380                        return(irc);
//b734380                }
//b734380
//b734380                // Allocate a process data block and place its address in PTDA.
//b734380
//b734380                if((irc = BMPGet((void *)pked->ked_pbDataBlk,
//b734380                                 (void *)&pptda->ptda_pPVDBPrc)) != NO_ERROR)
//b734380                {
//b734380                        return(irc);
//b734380                }
//b734380
//b734380                if((irc = pvwRegKrnlInstance(pptda,
//b734380                                             ptcb,
//b734380                                             pked,
//b734380              //aa04705                      &pptda->ptda_pPVDBPrc)) != NO_ERROR)
//b734380                                             &pptda->ptda_pPVDBPrc,    //aa04705
//aa05408                                             0,
//aa05408                                             0,
//b734380                                             0                    )) != NO_ERROR)
//b734380                {
//b734380                        // Deallocate data block.
//b734380
//b734380                        BMPFree((void *)pked->ked_pbDataBlk,
//b734380                                (void *)pptda->ptda_pPVDBPrc);
//b734380
//b734380                        pptda->ptda_pPVDBPrc = NULL;
//b734380
//b734380                        return(irc);
//b734380                }
//b734380        }
//b734380
//b734380        return(NO_ERROR);
//b734380}
//b734380
//b734380/***EP  PVWKillInstance_Prc
//b734380 *
//b734380 *      Terminate an instance of a process entity.
//b734380 *
//b734380 *      ENTRY   pptda   -- Ptr to PTDA for this process.
//b734380 *
//b734380 *      EXIT    NO_ERROR - success
//b734380 *
//b734380 *      NOTES
//b734380 *
//b734380 */
//b734380
//b734380INT PVWKillInstance_Prc (ptda_t *pptda)
//b734380{
//b734380        INT irc = NO_ERROR;
//b734380        PBYTE pbDataBlk = (PBYTE) pptda->ptda_pPVDBPrc;
//b734380
//b734380        if (pbDataBlk != NULL)
//b734380        {
//b734380                // De-register instance counter block.  This also clears
//b734380                // pptda->ptda_pPVDBPrc.
//b734380
//b734380                irc = PVWRegisterPerfCtrs(pbDataBlk,
//b734380                                          NULL,
//b734380                                          (RPC_FL_32BIT | RPC_FLM_KRNL_TRAN | RPC_FL_DEREG));
//b734380
//b734380                // Deallocate data block.
//b734380
//b734380                BMPFree((void *)ked_tran.tran_prc.ked_pbDataBlk,
//b734380                        (void *)pbDataBlk);
//b734380        }
//b734380
//b734380        return(irc);
//b734380}
//b734380
//b734380/***EP  PVWCreateInstance_Sem                                         //614.12
//b734380 *
//b734380 *      Create an instance of a semaphore entity.
//b734380 *
//b734380 *      ENTRY   pptda    -- Ptr to PTDA for process owning this thread.
//b734380 *              ptcb     -- Ptr to TCB for this thread.
//b734380 *              pPVDBSem -- Ptr to address in the semaphore structure to
//b734380 *                          store data block address
//b734380 *
//b734380 *      EXIT    NO_ERROR - success
//b734380 *
//b734380 *      NOTES
//b734380 *
//b734380 */
//b734380
//b734380INT PVWCreateInstance_Sem (ptda_t *pptda,
//b734380                           tcb_t  *ptcb,
//b734380                           PBYTE  *pPVDBSem)
//b734380{
//b734380
//b734380#ifdef PERFSEM
//b734380      INT     irc;
//b734380      ked_t   *pked;
//b734380      PBID    pbid;
//b734380
//b734380      *((ULONG *) pPVDBSem) = NULL;
//b734380
//b734380      if ((flPvwFlags & (PVW_FL_MSGQ | PVW_FLM_KRNL_INIT)) == \
//b734380                        (PVW_FL_MSGQ | PVW_FLM_KRNL_INIT))
//b734380       {
//b734380                /* Test if this instance of this entity is configured for
//b734380                 * measurement.  Exit if it is not configured.
//b734380                 */
//b734380
//b734380                pked = &ked_tran.tran_sem;
//b734380                pbid = &((PTBH) pked->ked_pbTextBlk)->tbh_bidID;
//b734380
//b734380                if ((irc = pvwTestConfig(pbid,pptda)) != NO_ERROR)
//b734380                {
//b734380                        return(irc);
//b734380                }
//b734380
//b734380                // Allocate a semaphore data block and place its address in xxx.
//b734380
//b734380                if((irc = BMPGet((void *)pked->ked_pbDataBlk,
//b734380                                 (void *)pPVDBSem)) != NO_ERROR)
//b734380                {
//b734380                        return(irc);
//b734380                }
//b734380
//b734380                if((irc = pvwRegKrnlInstance(pptda,
//b734380                                             ptcb,
//b734380                                             pked,
//b734380                //aa04705                    pPVDBSem)) != NO_ERROR)
//b734380                                             pPVDBSem,                 //aa04705
//aa05408                                             0,
//aa05408                                             0,
//b734380                                             0       )) != NO_ERROR)   //aa04705
//b734380                {
//b734380                        // Deallocate data block.
//b734380
//b734380                        BMPFree((void *)pked->ked_pbDataBlk,
//b734380                                (void *)*((ULONG *) pPVDBSem));
//b734380
//b734380                         *((ULONG *) pPVDBSem) = NULL;
//b734380
//b734380                        return(irc);
//b734380                }
//b734380        }
//b734380
//b734380        return(NO_ERROR);
//b734380#endif
//b734380  }                                                                     //614.12
//b734380
//b734380/***EP  PVWKillInstance_Sem                                           //614.12
//b734380 *
//b734380 *      Terminate an instance of a semaphore entity.
//b734380 *
//b734380 *      ENTRY   ptcb     -- Ptr to TCB for this semaphore.
//b734380 *              pPVDBSem -- Ptr to semaphore field for DD_
//b734380 *      EXIT    NO_ERROR - success
//b734380 *
//b734380 *      NOTES
//b734380 *
//b734380 */
//b734380
//b734380INT PVWKillInstance_Sem (tcb_t  *ptcb,
//b734380                         PBYTE  *pPVDBSem)
//b734380{
//b734380
//b734380#ifdef PERFSEM
//b734380        INT irc = NO_ERROR;
//b734380        PBYTE pbDataBlk = pPVDBSem;
//b734380
//b734380//      pbDataBlk = *((ULONG *)(pPVDBSem));
//b734380
//b734380        if (pbDataBlk != NULL)
//b734380        {
//b734380                // De-register instance counter block.  This also clears
//b734380                // pPVDBSem.
//b734380
//b734380                irc = PVWRegisterPerfCtrs(pbDataBlk,
//b734380                                          NULL,
//b734380                                          (RPC_FL_32BIT | RPC_FLM_KRNL_TRAN | RPC_FL_DEREG));
//b734380
//b734380                // Deallocate data block.
//b734380
//b734380                BMPFree((void *)ked_tran.tran_sem.ked_pbDataBlk,
//b734380                        (void *)pbDataBlk);
//b734380        }
//b734380
//b734380        return(irc);
//b734380#endif
//b734380}
                                                                      //614.12
/***LP  pvwCalCTimer
 *
 *      Calibrate C timers.
 *
 *      ENTRY   None
 *
 *      EXIT    NO_ERROR - success
 *
 *      NOTES
 *
 */

INT pvwCalCTimer( VOID )
{
        PvwCntStartTm(cal, C, 0);
        PvwEndTm(cal, C, 0);
        return(NO_ERROR);
}

/***LP  pvwGet032Addrs
 *
 *      Convert a 16:16 address to a 0:32 address, if necessary.
 *
 *      ENTRY   pBlk        -   Address of block to be converted
 *              flFlags     -   RPC_ flags:  [RPC_FL_16BIT | RPC_FL_32BIT]
 *
 *      EXIT    0:32 address if success.
 *              -1 if error.
 *
 *      NOTES
 *
 */

PVOID pvwGet032Addrs( PVOID pBlk,
                      ULONG flFlags )
{
        ULONG  pTmp;

        if (pBlk == NULL)
                return(pBlk);
        else
        if (flFlags & RPC_FL_32BIT)
                return(pBlk);
        else
                return(GET032OF1616(((ULONG)pBlk), pTmp));
}


/***LP  pvwDupTextBlk - Duplicate a registered text block for later collection
 *
 *      ENTRY   pbTextBlk   -   Address of text block (FLAT ptr)
 *              flFlags     -   RPC_ flags:
 *                                      {RPC_FL_16BIT | RPC_FL_32BIT}
 *              ppbDupBlk   -   Pointer to location which will receive address
 *                              of resultant duplicate
 *              pcbSize     -   Pointer to location which will receive size
 *                              of resultant duplicate
 *
 *      EXIT    NO_ERROR - success
 *              ERROR_INVALID_PARAMETER
 *              ERROR_PVW_INVALID_TEXT_BLK
 *
 *      NOTES   This procedure determines the size of the Text block being
 *              registered, allocates sufficient memory from the heap, and
 *              makes a copy of the Text block.  It returns a pointer to
 *              the new duplicate block and the size of the new block.
 *
 *              All the associated Text block name structures for a Text block
 *              are appended at the end of the Text block header, and all the
 *              associated ASCIIZ strings are in turn appended at the end of
 *              the Text block name structures.  Each pointer field in the
 *              Text block header and Name blocks contains not a true pointer,
 *              but rather an offset from the start of the Text block header.
 *              Since the entire structure (Text block header + Name blocks +
 *              ASCIIZs) is contiguous, an alias to the beginning of the Text
 *              block can be used directly to reconstruct the constituent
 *              pointers at the User level.
 */

INT pvwDupTextBlk ( PTBH      pbTextBlk,
                    ULONG     flFlags,
                    PBYTE     *ppbDupBlk,
                    PULONG    pcbSize)
{
        INT irc;
        USHORT i;
        ULONG  cbBlk = 0;
        PBYTE  pbBlk;
        ULONG  cszBlk;          // Count of the number of ASCIIZ strings.
        pvba_t *pAlias;         // Ptr to array of alias blocks for Name strings.
        PTBN   pName;           // 0:32 ptr to array of TBN structs.
        PBYTE  pbTemp;
        ULONG  ulTemp;

        // Determine total number of ASCIIZ strings.

        cszBlk = pbTextBlk->tbh_culName + NUM_TBH_PSZ;

        // Scan block and determine total size required.

        cbBlk = sizeof(TBH) + (pbTextBlk->tbh_culName * sizeof(TBN));

        // If interface is 16:16, must convert all string ptrs to 0:32.

        if ((pName = (PTBN) pvwGet032Addrs((PVOID)pbTextBlk->tbh_atbnName, flFlags)) == (PVOID) -1)
        {
                return(ERROR_PVW_INVALID_TEXT_BLK);
        }

        // Allocate storage for list of aliases to strings.

        if((irc = VMAllocKHB( VM_HKH_PUB_SWAPRW,
                              (ULONG)(sizeof(pvba_t) * cszBlk),
                              PVWOWNER,
                              NA,
                              (char **)SSToDS(&pAlias)) ) != NO_ERROR)
        {
                return(irc);
        }

        // Init. all aliases to NULL.
        memset(pAlias, 0 , (sizeof(pvba_t) * cszBlk));

        for (i=0; i < cszBlk; i++)
        {
                switch(i)
                {
                case 0:
                        pbTemp = (PBYTE) pbTextBlk->tbh_tbnGroup.tbn_pszName;
                        break;
                case 1:
                        pbTemp = (PBYTE) pbTextBlk->tbh_tbnInstance.tbn_pszName;
                        break;
                case 2:
                        pbTemp = (PBYTE) pbTextBlk->tbh_pszMsgFile;
                        break;
                case 3:
                        pbTemp = (PBYTE) pbTextBlk->tbh_pszHelpFile;
                        break;
                default:
                        pbTemp = (PBYTE) pName[i-NUM_TBH_PSZ].tbn_pszName;
                        break;
                }

                if ((pAlias[i].ba_pBlk = pvwGet032Addrs((PVOID)pbTemp, flFlags)) == (PVOID) -1)
                {
                        VMFreeKHB(VM_HKH_PUB_SWAPRW, (char *)pAlias);
                        return(ERROR_PVW_INVALID_TEXT_BLK);
                }

                if (pAlias[i].ba_pBlk != NULL)
                        pAlias[i].ba_cbBlk = strlen(pAlias[i].ba_pBlk);
                cbBlk += (++pAlias[i].ba_cbBlk);
        }


        if((irc = VMAllocKHB( VM_HKH_PUB_SWAPRW,
                              cbBlk+4,          //101504
                              PVWOWNER,
                              NA,
                              (char **)SSToDS(&pbBlk) )) != NO_ERROR)
        {
                VMFreeKHB(VM_HKH_PUB_SWAPRW, (char *)pAlias);
                return(irc);
        }

// 101504 begins  (linkedlist)

        if (pPTDACur->pPvwDataBlk == (PULONG)NULL)
        {
           PULONG pTemp;
           pPTDACur->pPvwDataBlk = pbBlk;
           pTemp = (PULONG)pbBlk;
           *pTemp = (ULONG)NULL;
        }
        else
        {
           PULONG pTemp = pPTDACur->pPvwDataBlk;

           for ( ; *pTemp != (ULONG)NULL; pTemp = (PULONG)*pTemp );

           *pTemp = (ULONG)pbBlk;
           pTemp = (PULONG)*pTemp;
           *pTemp = (ULONG)NULL;
        }
        pbBlk += 4;
// 101504 ends

        // Init. block = 0.
        memset(pbBlk, 0, cbBlk);

        // Move to output parameters.

        *pcbSize = cbBlk;
        *ppbDupBlk = pbTemp = pbBlk;

        // So far we've converted all PSZ to 32-bit, determined Text block
        // size required, and allocated that block.  Now, we must fill
        // the block.

        memcpy(pbTemp, pbTextBlk, sizeof(TBH));
        pbTemp += sizeof(TBH);

        memcpy(pbTemp, pName, (pbTextBlk->tbh_culName * sizeof(TBN)));
        pName = (PTBN) pbTemp;  // Now pName -> copy of Name block array.
        (ULONG) ((PTBH) pbBlk)->tbh_atbnName = (ULONG) (pbTemp - pbBlk);
        pbTemp += (pbTextBlk->tbh_culName * sizeof(TBN));

        for (i=0; i < cszBlk; i++)
        {
                // Replace each PSZ with the string's offset from the start
                // of text block.

                ulTemp = (ULONG)(pbTemp - pbBlk);

                switch(i)
                {
                case 0:
                        (ULONG) ((PTBH) pbBlk)->tbh_tbnGroup.tbn_pszName = ulTemp;
                        break;
                case 1:
                        (ULONG) ((PTBH) pbBlk)->tbh_tbnInstance.tbn_pszName = ulTemp;
                        break;
                case 2:
                        (ULONG) ((PTBH) pbBlk)->tbh_pszMsgFile = ulTemp;
                        break;
                case 3:
                        (ULONG) ((PTBH) pbBlk)->tbh_pszHelpFile = ulTemp;
                        break;
                default:
                        (ULONG) pName[i-NUM_TBH_PSZ].tbn_pszName = ulTemp;
                        break;
                }

                if (pAlias[i].ba_pBlk != NULL)
                        strcpy(pbTemp, (PCHAR) pAlias[i].ba_pBlk);
                pbTemp += pAlias[i].ba_cbBlk;
        }

        VMFreeKHB(VM_HKH_PUB_SWAPRW, (char *)pAlias);

        return(NO_ERROR);
}




/***LP  pvwFindRPDesc - Find specified Registered Perf counter Descriptor.
 *
 *      ENTRY   pbidIn    -     Ptr to Input Block ID of Registered Perf ctr
 *              pprpdOut  -     Ptr to location which will receive address
 *                              of descriptor.
 *
 *      EXIT    NO_ERROR - success
 *              ERROR_INVALID_PARAMETER - if BID not found.
 *
 *      NOTES   pbidIn->bid_usGroup = 0 is considered an invalid ID.
 *
 *              pbidIn->bid_usInstance = 0 is considered a valid ID; it is the
 *              head of the Instance descriptor chain (i.e. it is the
 *              Group descriptor).  The value of bid_usInstance in the Group
 *              descriptor is NOT = 0, however.  It is the 'last used'
 *              Instance ID for this group.  There is some special logic
 *              in this function which checks for an Input Block Instance ID
 *              of 0.  If Instance 0 is requested, the address of the Group
 *              descriptor is returned; the Instance chain is not scanned.
 */

INT pvwFindRPDesc ( PBID     pbidIn,
                    prpd_t * pprpdOut)
{
        INT irc;
        prpd_t prpdCur;         // Ptr to current descriptor.
        prpd_t prpdIHd;         // Ptr to Head of Instance desc. chain.
        BOOL fResult = FALSE;

        if (pbidIn->bid_usGroup == 0)
        {
                if (pbidIn->bid_usInstance != 0)
                {
                        return(ERROR_INVALID_PARAMETER);
                }
                else
                {
                        *pprpdOut = pPvwRPDHead;
                        return(NO_ERROR);
                }
        }

        /* Traverse chain until a match is found or
         * the end of chain is reached.
         */

        for (prpdCur = pPvwRPDHead->rpd_pNextGroup; \
             prpdCur != pPvwRPDHead; \
             prpdCur = prpdCur->rpd_pNextGroup)
        {
                if (prpdCur->rpd_bidID.bid_usGroup == pbidIn->bid_usGroup)
                {
                        fResult = TRUE;
                        break;
                }
        }

        // Error Exit if Group ID not found.
        if (fResult == FALSE)
                return(ERROR_INVALID_PARAMETER);
        else
        if (pbidIn->bid_usInstance == 0)
        {
                *pprpdOut = prpdCur;
                return(NO_ERROR);
        }

        prpdIHd = prpdCur;      // prpdIHd -> Instance Head (Group desc.)
        prpdCur = prpdIHd->rpd_pNextInstance;

        /* Beginning with first Instance in chain, traverse chain until a
         * match is found or the end of chain is reached.
         */

        while (prpdCur != prpdIHd)
        {
                if (prpdCur->rpd_bidID.bid_usInstance == pbidIn->bid_usInstance)
                {
                        *pprpdOut = prpdCur;
                        return(NO_ERROR);
                }

                prpdCur = prpdCur->rpd_pNextInstance;
        }

        // Error Exit since Instance ID not found.
        return(ERROR_INVALID_PARAMETER);
}


/***LP  pvwDeconRPDesc
 *
 *      Deconfigure Register Perf counter Descriptor, and destroy it if
 *      this is a deregistration.
 *
 *      ENTRY   prpdIn -       Address of descriptor (rpd_t)
 *
 *      EXIT    NO_ERROR - success
 *
 *      NOTES   This function will reset the configuration flag in the
 *              registrant's data block and in the descriptor.  For drivers,
 *              it will also restore the timer function pointers (in data
 *              block) to the original values.  Then if the RPC_FL_DEREG
 *              flag is set in prpdIn->rpd_flFlags, the descriptor will be
 *              unlinked from the descriptor chain and destroyed.
 */

INT pvwDeconRPDesc( prpd_t prpdIn )
{
        pcfg_t  pcfgHead;
        pcfg_t  pcfgSav;
        pcfg_t  pcfgCur;
        USHORT  smp;   //55134
        INT     irc;   //55134


        // Free all aliases (before freeing blocks).

        if (prpdIn->rpd_Data.bld_pbAlias != NULL)
        {
//56354         VMFreeMem((ULONG)prpdIn->rpd_Data.bld_pbAlias,
//56354                   HOBNULL,
//56354                   VMFM_ALLOW_HEAP);

                prpdIn->rpd_Data.bld_pbAlias = NULL;
        }

        if (prpdIn->rpd_Text.bld_pbAlias != NULL)
        {
                VMFreeMem((ULONG)prpdIn->rpd_Text.bld_pbAlias,
                          HOBNULL,
                          VMFM_ALLOW_HEAP);

                prpdIn->rpd_Text.bld_pbAlias = NULL;
        }

        if ((pcfgHead = prpdIn->rpd_pConfigHead) != NULL)
        {
                /* Traverse configuration chain, unlinking all but the first
                 * (head) record from the chain, and freeing them.
                 */

                 for (pcfgCur = pcfgHead->cfg_pNext;
                      pcfgCur != pcfgHead;
                      pcfgCur = pcfgSav)
                 {
                        pcfgSav = pcfgCur->cfg_pNext;

                        pcfgCur->cfg_pPrev->cfg_pNext = pcfgCur->cfg_pNext;
                        pcfgCur->cfg_pNext->cfg_pPrev = pcfgCur->cfg_pPrev;

                        BMPFree((void *)pbhPvwCfg,(void *)pcfgCur);
                 }

        }
// don't reset configure flag for ring 3 applications                  //55134
    if (prpdIn->rpd_flFlags & RPC_FL_APP)                             //55134
       goto decon10;                                                   //55134

        if ((prpdIn->rpd_Data.bld_pbBlk != NULL) &&
            (prpdIn->rpd_flFlags & RPC_FL_CONFIG))
        {
            /* Clear data block flags, restore original data block Tmr ptrs
             * (for drivers only) and clear desc. config. flag.
             */

//55134     ((PDBH) prpdIn->rpd_Data.bld_pbBlk)->dbh_flFlags &= ~(RPC_FL_CONFIG);
                                                        /*55134*/
          if ((irc = TKFuWord(SSToDS(&smp),
              (&((PDBH) prpdIn->rpd_Data.bld_pbBlk)->dbh_flFlags))) != NO_ERROR)
             {                                          /*55134*/
              return(irc);                              /*55134*/
             }                                          /*55134*/

          smp = smp & ~(RPC_FL_CONFIG);                  /*55134*/
                                                        /*55134*/
          if ((irc = TKSuWord(smp ,
              (&((PDBH) prpdIn->rpd_Data.bld_pbBlk)->dbh_flFlags))) != NO_ERROR)
            {                                           /*55134*/
            return(irc);                                /*55134*/
            }                                           /*55134*/


            if (prpdIn->rpd_flFlags & RPC_FLM_DRV)
            {
                ((PDBH) prpdIn->rpd_Data.bld_pbBlk)->dbh_pfnTmrAdd = prpdIn->rpd_pfnTmrAdd;
                ((PDBH) prpdIn->rpd_Data.bld_pbBlk)->dbh_pfnTmrSub = prpdIn->rpd_pfnTmrSub;
            }

            prpdIn->rpd_flFlags &= ~(RPC_FL_CONFIG);
        }

decon10:                                                               //55134

        if (prpdIn->rpd_flFlags & RPC_FL_DEREG)
        {
                /* For kernel transients, clear the data block ptr in the
                 * system control block (PTDA, TCB, etc.) of this entity.
                 * (See NOTES in PVWRegisterPerfCtrs() for details.)
                 */

                if (((prpdIn->rpd_flFlags & RPC_FLM_KRNL_TRAN) == RPC_FLM_KRNL_TRAN) &&
                    (prpdIn->rpd_pfnTmrAdd != NULL))
                {
                        *((PBYTE *) prpdIn->rpd_pfnTmrAdd) = NULL;
                }

                // Unlink descriptor from chain and free it.

                prpdIn->rpd_pPrevGroup->rpd_pNextGroup = prpdIn->rpd_pNextGroup;
                prpdIn->rpd_pNextGroup->rpd_pPrevGroup = prpdIn->rpd_pPrevGroup;
                prpdIn->rpd_pPrevInstance->rpd_pNextInstance = prpdIn->rpd_pNextInstance;
                prpdIn->rpd_pNextInstance->rpd_pPrevInstance = prpdIn->rpd_pPrevInstance;

                // 73545 - fix memory leakage reported by J. Summers in
                //         Austin.
                if ( prpdIn->rpd_pConfigHead != NULL )
                   BMPFree((void *)pbhPvwCfg,(void *)prpdIn->rpd_pConfigHead);

                BMPFree((void *)pbhPvwRPD,(void *)prpdIn);
        }

        return(NO_ERROR);
}


/***EP  PVWRegisterPerfCtrs - Register a performance data block for collection
 *
 *      This is the worker for dh_RegisterPerfCtrs, fsh_RegisterPerfCtrs,
 *      and DosRegisterPerfCtrs.
 *
 *      ENTRY   pbDataBlk   -   Address of data block
 *              pbTextBlk   -   Address of text block
 *              flFlags     -   RPC flags (see pvwxport.h):
 *                               {RPC_FL_16BIT | RPC_FL_32BIT} |
 *                               {RPC_FL_PERS | RPC_FL_TRAN} |
 *                               {RPC_FL_KRNL | RPC_FL_DD | RPC_FL_FSD | RPC_FL_VDD | RPC_FL_APP}
 *                               {RPC_FL_DEREG}
 *
 *      EXIT    NO_ERROR - success
 *              ERROR_INVALID_PARAMETER
 *              ERROR_PVW_INVALID_COUNTER_BLK
 *              ERROR_PVW_INVALID_TEXT_BLK
 *
 *      NOTES   Since this is a common worker for all flavors of
 *              RegisterPerfCtrs, it use TkSu/FuBuff() since that is
 *              required for DosRegisterPerfCtrs() which fields its requests
 *              from User space.
 *
 *              This procedure determines the size of the Text block being
 *              registered, allocates sufficient memory from the heap, and
 *              makes a copy of the driver's Text block.  The associated
 *              driver may therefore free the memory occupied by its own
 *              copy of the Text block, once it has been registered.
 *
 *              Deregistration: pbDataBlk = Address of data block (a copy
 *                                          will be put in message queue)
 *                              pbTextBlk = NULL
 *                              flFlags   = (... | RPC_FL_DEREG)
 *
 *              Registration of Transient Entities:
 *
 *                      Instance 0:
 *                              pbDataBlk = Address of data block HEADER (the
 *                                          data block counters are not needed)
 *                              pbTextBlk = Address of text block (full text
 *                                          block with per-counter name blks)
 *                              flFlags   = (... | RPC_FL_TRAN)
 *
 *                      Instance 1-n:
 *                              pbDataBlk = Address of data block (header
 *                                          followed by counters)
 *                              pbTextBlk = Address of text block header (the
 *                                          per-counter name blocks are not
 *                                          needed.  Kernel counters may
 *                                          use name blocks as instance
 *                                          descriptors).
 *                              flFlags   = (... | RPC_FL_TRAN)
 *
 *                      Kernel transient entities:  The dbh_pfnTmrAdd
 *                      member of the data block header (DBH) is used by
 *                      kernel transient entities to store the address of
 *                      the system control structure member in which the
 *                      kernel transient entity has stored the data block
 *                      pointer (&pptda->ptda_pPVDBPrc,
 *                      &ptcb->TCB_pPVDBThd, etc.).  This is so that if
 *                      the entity still exists when Perfview dies, the
 *                      data block ptrs in the system control structures can
 *                      be cleared.  Otherwise, they would point at non-
 *                      existent memory, since the kernel transient data
 *                      memory objects are all destroyed.  This could result
 *                      in a GP Fault if the Perfview hooks were subsequently
 *                      activated.  (The dbh_pfnTmrAdd member is normally
 *                      unused for kernel entities.)
 */

INT PVWRegisterPerfCtrs ( PBYTE    pbDataBlk,
                          PBYTE    pbTextBlk,
                          ULONG    flFlags )
{
        INT i, irc;
        PVOID pData, pText;
        prpd_t prpdCur;                 // Ptr to Current descriptor.
        prpd_t prpdNew;                 // Ptr to New descriptor.
        prpd_t prpdIHd;                 // Ptr to Head of Instance desc. chain.
        BID bidNew = {0,0};             // New block ID for registrant.
        BOOL fResult;

                                          // crs-sem
        if ( hKSEMPerf == 0 )
           {   // create the mutex sem
           KSEMCreate( &hKSEMPerf, KRNL_MUTEXSEM );
           }

        KSEMRequestMutex( hKSEMPerf, -1 );  // crs-sem

#ifdef KERNEL_ONLY      // Convenient way to prevent registration by Drivers.
        if ((flFlags & RPC_FL_KRNL) == 0)
                {
                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                return(ERROR_CALL_NOT_IMPLEMENTED);
                }
#endif //KERNEL_ONLY

        /* Validate data block ptr; if ptr is 16:16, convert it to 0:32.
         */

        if (pbDataBlk == NULL)
                {
                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                return(ERROR_INVALID_PARAMETER);
                }
        else
        if ((pData = pvwGet032Addrs((PVOID)pbDataBlk, flFlags)) == (PVOID) -1)
                {
                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                return(ERROR_PVW_INVALID_COUNTER_BLK);
                }

        if (flFlags & RPC_FL_DEREG)
        {
                                                                       //49168
           // check to see if we have a valid address                  //49168
           if (TKProbeBuff ((char *)pData,sizeof (ulong_t *),          //49168
               (TK_UM_READ )))                                         //49168
               {                                                       //49168
                 *((ULONG *)pData)           = NULL;                   //49168
                 KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                 return(ERROR_PROTECTION_VIOLATION);                   //49168
               }                                                       //49168
                /* Use the dbh_bidID to find and deallocate the corresponding
                 * Registered Perf ctr Descriptor (RPD).  If the counter
                 * block was indeed registered, then place an "entity death"
                 * message in the message queue along with a copy of the data
                 * block which is being deregistered.
                 */
                /* check for two deregistered from the same bid, since */
                /* the bid is set to zero after deregister */

                if ((((PDBH) pData)->dbh_bidID.bid_usInstance == 0) &&                 //aa04740
                    (((PDBH) pData)->dbh_bidID.bid_usGroup == 0))      //aa04740
                {                                                      //aa05446
                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                return(ERROR_PVW_INVALID_COUNTER_BLK);                 //aa05446
                }                                                      //aa05446
                if ((irc = pvwFindRPDesc(&((PDBH) pData)->dbh_bidID,
                                         SSToDS(&prpdNew))) != NO_ERROR)
                {
                        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                        return(ERROR_PVW_INVALID_COUNTER_BLK);
                }

                /* Remove descriptor from the chain, free all associated
                 * memory and aliases, and free the the descriptor itself.
                 */

                // Deconfigure registrant and destroy descriptor.

                prpdNew->rpd_flFlags |= RPC_FL_DEREG;
                pvwDeconRPDesc(prpdNew);

                // Put "death" message in queue along with copy of data block.

                pvwWriteMsg( (PBYTE) pData,
                             ((PDBH) pData)->dbh_ulTotLen,
                             (ET_FLM_BID_DATA | ET_FL_DEATH));

                ((PDBH) pData)->dbh_bidID = bidNew;                    //aa04740

                // If copy of text block exists, free it.


//101504 begins if (prpdNew->rpd_Text.bld_pbBlk != NULL)               // 71695
//                   VMFreeKHB(VM_HKH_PUB_SWAPRW, (char *)(prpdNew->rpd_Text.bld_pbBlk - 4));
//101504 ends   Memory is freed when process ends.


                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                return(NO_ERROR);
        }


        /* If we get here, we know this is a registration, not a
         * de-registration.
         */

        // Validate text block ptr; if ptr is 16:16, convert it to 0:32.

        if (pbTextBlk == NULL)
                {
                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                return(ERROR_INVALID_PARAMETER);
                }
        else
        if ((pText = pvwGet032Addrs((PVOID)pbTextBlk, flFlags)) == (PVOID) -1)
                {
                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                return(ERROR_PVW_INVALID_TEXT_BLK);
                }

        /* Validate block contents:
         *      Verify version number.
         */

        if (((PTBH) pText)->tbh_ulVersion != TBH_VER_CURRENT)
                {
                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                return(ERROR_PVW_INVALID_TEXT_BLK);
                }

// BUGBUG 30-Jan-90, DaveBe, <Need code to further validate contents>

        // Initialize descriptor table if none yet exists.

        if (pPvwRPDHead == NULL)
        {
                if((irc = BMPInit(RPC_MAX_CONFIG,
                                  sizeof (cfg_t),
                                  (PG_SWAPPABLE),
                                  PVWOWNER,
                                  NULL,
                                  (void *)&pbhPvwCfg) ) != NO_ERROR)
                {
                        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                        return(irc);
                }
                // perf - since this allocation is swappable and greater than
                //        4 meg use page directory alignment to make the system
                //        page table also swappable
                if((irc = BMPInit(RPC_MAX_DESC,
                                  sizeof (rpd_t),
                                  // PERF - defect 98488 ,add PAGEDIRALIGN
                                  // align memory on 4 meg boundary so that
                                  // the system page table can be swapped out
                                  (PG_SWAPPABLE | VMBMP_PAGEDIRALIGN),
                                  PVWOWNER,
                                  NULL,
                                  (void *)&pbhPvwRPD) ) != NO_ERROR)
                {
                        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                        return(irc);
                }

                // Get HEAD record.
                if((irc = BMPGet((void *)pbhPvwRPD,(void *)&pPvwRPDHead)) != NO_ERROR)
                {
                        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                        return(irc);
                }

                // Init. HEAD descriptor = 0.
                memset(pPvwRPDHead, 0, sizeof(rpd_t));

                // Init. HEAD links to point at itself.
                pPvwRPDHead->rpd_pPrevGroup = \
                pPvwRPDHead->rpd_pNextGroup = \
                pPvwRPDHead->rpd_pPrevInstance = \
                pPvwRPDHead->rpd_pNextInstance = pPvwRPDHead;
        }

        // Assign appropriate Block ID; both the Group and Instance IDs
        // wrap through 2^16, to minimize frequency of reuse, and a Group ID
        // of 0 is considered invalid.  The Text block for Instance 0 of all
        // Groups (Persistent and Transient) contains the full counter block
        // description (one TBN structure for every counter in the block).
        // For Persistent Entities, there is only one Instance allowed per
        // Group; this is Instance 0.  For Transient entities, mulitple
        // instances may be registered, but Instance 0 is always reserved for
        // the full counter block description.  Block IDs are unique; if an
        // unused BID is not available, an error is returned.  Once an BID is
        // determined, a BMP record is committed and pointers to the
        // registered Data and Text blocks are stored.

        if (((PTBH) pText)->tbh_bidID.bid_usGroup == 0)
        {
                /* Register a new group. Scan pPvwRPDHead chain for unused Group
                 * ID, beginning with bidPvwLastRPD.bid_usGroup; quit after
                 * RPC_MAX_ID iterations.
                 */

                bidNew.bid_usGroup = bidPvwLastRPD.bid_usGroup;

                do
                {
                        fResult = FALSE;

                        // 1 <= bid_usGroup <= RPC_MAX_ID; (0 is invalid ID).

                        if (bidNew.bid_usGroup++ >= RPC_MAX_ID)
                                bidNew.bid_usGroup = 1;

                        /* Traverse chain until a match is found or
                         * the end of chain is reached.
                         */

                        for (prpdCur = pPvwRPDHead->rpd_pNextGroup; \
                             prpdCur != pPvwRPDHead; \
                             prpdCur = prpdCur->rpd_pNextGroup)
                        {
                                if (prpdCur->rpd_bidID.bid_usGroup == \
                                                        bidNew.bid_usGroup)
                                {
                                        fResult = TRUE;
                                        break;
                                }
                        }
                }
                while(fResult == TRUE && bidNew.bid_usGroup != bidPvwLastRPD.bid_usGroup);

                // Exit if no ordinals available.
                if (fResult == TRUE)
                {
// BUGBUG 22-Jun-90, DaveBe, This error message is obscure: it should be
// ERROR_NO_MORE_ORDINALS.
                        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                        return(ERROR_NO_MORE_ITEMS);
                }

                bidPvwLastRPD.bid_usGroup = bidNew.bid_usGroup;

                if((irc = BMPGet((void *)pbhPvwRPD,
                                 (void *)SSToDS(&prpdNew))) != NO_ERROR)
                {
                        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                        return(irc);
                }

                // Init. descriptor = 0.
                memset(prpdNew, 0, sizeof(rpd_t));

                // Insert New descriptor at beginning of chain.

                prpdNew->rpd_pPrevGroup = pPvwRPDHead;
                prpdNew->rpd_pNextGroup = pPvwRPDHead->rpd_pNextGroup;
                (prpdNew->rpd_pNextGroup)->rpd_pPrevGroup = prpdNew;
                pPvwRPDHead->rpd_pNextGroup = prpdNew;

                // Instance ptrs link to itself.
                prpdNew->rpd_pPrevInstance = prpdNew;
                prpdNew->rpd_pNextInstance = prpdNew;

                // Create Configuration Specifier for this Group.

                if((irc = BMPGet((void *)pbhPvwCfg,
                                 (void *)&prpdNew->rpd_pConfigHead)) != NO_ERROR)
                {
                        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                        return(irc);
                }

                // Init. specifier = 0.
                memset(prpdNew->rpd_pConfigHead, 0, sizeof(cfg_t));

                // First Config. Spec. ptrs link to itself.
                prpdNew->rpd_pConfigHead->cfg_pPrev = prpdNew->rpd_pConfigHead;
                prpdNew->rpd_pConfigHead->cfg_pNext = prpdNew->rpd_pConfigHead;

                // Init. Config. Spec. BID and Flags.
                prpdNew->rpd_pConfigHead->cfg_bidID = bidNew;

                if (flFlags & RPC_FL_PERS)
                {
                        prpdNew->rpd_pConfigHead->cfg_flFlags |= CFG_FL_ALL;
                }
                else
                {
// BUGBUG 09-Jul-90,DaveBe, Currently, ALL instances of ALL transient groups
// are configured for measurement.  This will eventually change such that
// cfg_flFlags = 0 for the first CONFIG record of transient groups (no
// instances configured initially.)

                        prpdNew->rpd_pConfigHead->cfg_flFlags |= CFG_FL_ALL;
                }

                // Clear Group IDs in Data and Text blocks.
                ((PDBH) pData)->dbh_bidID.bid_usGroup = \
                ((PTBH) pText)->tbh_bidID.bid_usGroup = 0;
        }
        else
        if ((((PTBH) pText)->tbh_tbnGroup.tbn_ulFlags & TBN_FL_TRAN) &&
            (flFlags & RPC_FL_TRAN))
        {
                /* Register a new instance.  Scan pPvwRPDHead chain for the given
                 * Group ID.
                 */

                bidNew.bid_usGroup = ((PTBH) pText)->tbh_bidID.bid_usGroup;
                bidNew.bid_usInstance = 0;

                if (pvwFindRPDesc(SSToDS(&bidNew),SSToDS(&prpdCur)) != NO_ERROR)
                {
                        // Exit since Group ID not found.
                        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                        return(ERROR_PVW_INVALID_TEXT_BLK);
                }


                /* Group ID found, so scan for unused Instance ID, beginning
                 * with rpd_bidID.bid_usInstance; quit after RPC_MAX_ID
                 * iterations.
                 */

                prpdIHd = prpdCur;
                bidNew.bid_usInstance = prpdIHd->rpd_bidID.bid_usInstance;

                do
                {
                        fResult = FALSE;

                        // 1 <= bid_usInstance <= RPC_MAX_ID; (0 is invalid ID).

                        if (bidNew.bid_usInstance++ >= RPC_MAX_ID)
                                bidNew.bid_usInstance = 1;

                        /* Traverse chain until a match is found or
                         * the end of chain is reached.
                         */

                        for (prpdCur = prpdIHd->rpd_pNextInstance; \
                             prpdCur != prpdIHd; \
                             prpdCur = prpdCur->rpd_pNextInstance)
                        {
                                if (prpdCur->rpd_bidID.bid_usInstance == \
                                                        bidNew.bid_usInstance)
                                {
                                        fResult = TRUE;
                                        break;
                                }
                        }
                }
                while(fResult == TRUE && \
                      bidNew.bid_usInstance != prpdIHd->rpd_bidID.bid_usInstance);

                // Exit if no ordinals available.
                if (fResult == TRUE)
                {
// BUGBUG 22-Jun-90, DaveBe, This error message is obscure: it should be
// ERROR_NO_MORE_ORDINALS.
                        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                        return(ERROR_NO_MORE_ITEMS);
                }

                // Record this new Instance ID as the 'last used' Instance ID.

                prpdIHd->rpd_bidID.bid_usInstance = bidNew.bid_usInstance;

                if((irc = BMPGet((void *)pbhPvwRPD,(void *)SSToDS(&prpdNew))) != NO_ERROR)
                {
                        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                        return(irc);
                }

                // Init. descriptor = 0.
                memset(prpdNew, 0, sizeof(rpd_t));

                // Insert New descriptor at beginning of chain.

                prpdNew->rpd_pPrevInstance = prpdIHd;
                prpdNew->rpd_pNextInstance = prpdIHd->rpd_pNextInstance;
                (prpdNew->rpd_pNextInstance)->rpd_pPrevInstance = prpdNew;
                prpdIHd->rpd_pNextInstance = prpdNew;

                // Group ptrs link to itself.
                prpdNew->rpd_pPrevGroup = prpdNew;
                prpdNew->rpd_pNextGroup = prpdNew;
        }
        else
        {
                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                return(ERROR_PVW_INVALID_TEXT_BLK);
        }

        // Init. BID and Flags.
        prpdNew->rpd_bidID = bidNew;
        prpdNew->rpd_flFlags = (flFlags & ~RPC_FLM_PRIVATE);

        // Init. Data Block pointer and size.
        prpdNew->rpd_Data.bld_pbBlk = (PBYTE) pData;

// BUGBUG 18-May-90, DaveBe, <Should verify that Data block size correlates
//                            with Text block information.>

        prpdNew->rpd_Data.bld_cbSize = ((PDBH) pData)->dbh_ulTotLen;

        // Init. context of registrant. (Text block is in Kernel heap).

//pmr7x112 prpdNew->rpd_Data.bld_hptda = pPTDACur->ptda_handle;

        if (flFlags & RPC_FL_APP)                                //pmr7x112
           prpdNew->rpd_Data.bld_hptda = pPTDACur->ptda_handle;  //pmr7x112
        else                                                     //pmr7x112
           prpdNew->rpd_Data.bld_hptda = HOBNULL;                //pmr7x112

        prpdNew->rpd_Text.bld_hptda = HOBNULL;


        /* Save original data block ptrs in descriptor.  For kernel transient
         * entities, dbh_pfnTmrAdd has special meaning; see above NOTES for
         * PVWRegisterPerfCtrs().
         */

        prpdNew->rpd_pfnTmrAdd = ((PDBH) pData)->dbh_pfnTmrAdd;
        prpdNew->rpd_pfnTmrSub = ((PDBH) pData)->dbh_pfnTmrSub;

        // Clear Instance IDs in Data and Text blocks.

        ((PDBH) pData)->dbh_bidID.bid_usInstance = \
        ((PTBH) pText)->tbh_bidID.bid_usInstance = 0;

        // Duplicate Text block and save its address and
        // size in descriptor.

        if ((irc = pvwDupTextBlk(pText,
                                 flFlags,
                                 &prpdNew->rpd_Text.bld_pbBlk,
                                 &prpdNew->rpd_Text.bld_cbSize)) != NO_ERROR)
        {
                // Free descriptor for reuse since Text block is invalid.

                BMPFree((void *)pbhPvwRPD,(void *)prpdNew);
                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                return(irc);
        }

        // Write back BID.

        ((PDBH) pData)->dbh_bidID = bidNew;
        ((PTBH) pText)->tbh_bidID = bidNew;

        // Also store ordinal in the duplicated text block.

        ((PTBH) prpdNew->rpd_Text.bld_pbBlk)->tbh_bidID = bidNew;

        // If requested, initialize Tmr function ptrs to
        // the Tmr functions now at Registration time, rather
        // than waiting till Configuration time.

        if (flFlags & RPC_FL_TMR_INIT)
        {
                if (flFlags & RPC_FL_32BIT)
                {
                        ((PDBH) pData)->dbh_pfnTmrAdd = &TMR32AddTime;
                        ((PDBH) pData)->dbh_pfnTmrSub = &TMR32SubTime;
                }
                else
                {
                        ((PDBH) pData)->dbh_pfnTmrAdd = pfnTMR16AddTime;
                        ((PDBH) pData)->dbh_pfnTmrSub = pfnTMR16SubTime;
                }
        }


/* BUGBUG 28-Jul-90, DaveBe, This is a good place to test for configuration
 * of the bidNew entity, and to configure it if necessary.  This should
 * work for both transient AND persistent entities. When an instance of a
 * (non-kernel) transient entity is registered, its configuration chain
 * should be scanned.  If it is to be configured, and is not already (kernel
 * transients will ONLY be registered if they are configured), then the
 * instance should be CONFIGURED as well as REGISTERED.
 */

        /* Place "new registrant" message in message queue, along
         * with copy of text block.
         */

        pvwWriteMsg( prpdNew->rpd_Text.bld_pbBlk,
                     prpdNew->rpd_Text.bld_cbSize,
                     (ET_FLM_BID_TEXT | ET_FL_BIRTH));

        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
        return(NO_ERROR);
}


/***LP  pvwInitPersist
 *
 *      Initialize persistent performance entities for measurement
 *
 *      ENTRY   None
 *
 *      EXIT    NO_ERROR - success
 *              ERROR_ALREADY_EXISTS
 *
 *      NOTES
 *
 */

INT pvwInitPersist( VOID )
{
        INT irc, i;
        ULONG uli;
        ked_t * pkedPers = (ked_t *) &ked_pers;

        if ((flPvwFlags & PVW_FL_KRNL_PERS_INIT) == 0)
        {
                for(i=0; i < PVW_NUM_ENT_KRNL_PERS; i++)
                {
                        // Allocate Data block.

                        if((irc = VMAllocKHB(
                                        VM_HKH_PUB_RESRW,
                                        (ULONG) pkedPers[i].ked_cbDataSize,
                                        PVWOWNER,
                                        NA,
                                        (char **)&pkedPers[i].ked_pbDataBlk)) != NO_ERROR)
                        {
                                return(irc);
                        }

                        // Init. entire Data block = 0.

                        memset( pkedPers[i].ked_pbDataBlk,
                                0,
                                pkedPers[i].ked_cbDataSize);

                        // Init. header (assumes hdr comes first in data blk)

                        ((PDBH) pkedPers[i].ked_pbDataBlk)->dbh_ulTotLen = \
                                                pkedPers[i].ked_cbDataSize;
                        ((PDBH) pkedPers[i].ked_pbDataBlk)->dbh_flFlags |= \
                                                (RPC_FL_32BIT | RPC_FLM_KRNL_PERS);

                        /* Init. Text block BID = 0.  (This is the only member
                         * of the Text block which gets changed during
                         * registration; it may still be set from a previous
                         * registration since it is a static structure.)
                         */

                        ((PTBH) pkedPers[i].ked_pbTextBlk)->tbh_bidID.bid_usGroup = \
                        ((PTBH) pkedPers[i].ked_pbTextBlk)->tbh_bidID.bid_usInstance = 0;

                        // Register counter block.

                        if ((irc = PVWRegisterPerfCtrs(
                                        pkedPers[i].ked_pbDataBlk,
                                        pkedPers[i].ked_pbTextBlk,
                                        (RPC_FL_32BIT | RPC_FLM_KRNL_PERS))) != NO_ERROR)
                        {
                                return(irc);
                        }
                }

                // Indicate that Perfview Persistent entities initialized.
                flPvwFlags |= PVW_FL_KRNL_PERS_INIT;

                return(NO_ERROR);
        }
        else
                return(ERROR_ALREADY_EXISTS);
}


/***LP  pvwInitTransient
 *
 *      Initialize transient performance entities for measurement
 *
 *      ENTRY   None
 *
 *      EXIT    NO_ERROR - success
 *              ERROR_ALREADY_EXISTS
 *
 *      NOTES
 *
 */

INT pvwInitTransient( VOID )
{
        INT irc, i;
        ULONG uli;
        ked_t * pkedTran = (ked_t *) &ked_tran;
        PDBH pdbhInstance0;

        if ((flPvwFlags & PVW_FL_KRNL_TRAN_INIT) == 0)
        {
                for(i=0; i < PVW_NUM_ENT_KRNL_TRAN; i++)
                {
                        // Allocate BMP object for Transient Data blocks.

                        if((irc = BMPInit(
                                        (ULONG) pkedTran[i].ked_cbDataBlks,
                                        (ULONG) pkedTran[i].ked_cbDataSize,
                                        (PG_RESIDENT),
                                        PVWOWNER,
                                        NULL,
                                        (void *)&pkedTran[i].ked_pbDataBlk)) != NO_ERROR)
                        {
                                return(irc);
                        }

// BUGBUG 19-Jun-90, DaveBe, This is really a waste of a BMP record, since it
// isn't utilized as a true Transient Instance Data block (only its header
// is used at registration time), but it simplifies the registration logic.

                        // Reserve first Data block for registration purposes.

                        if((irc = BMPGet((void *)pkedTran[i].ked_pbDataBlk,
                                         (void *)SSToDS(&pdbhInstance0))) != NO_ERROR)
                        {
                                return(irc);
                        }

                        // Init. first Data block = 0.

                        memset( pdbhInstance0,
                                0,
                                pkedTran[i].ked_cbDataSize);

                        // Init. first Data block header (assumes hdr comes
                        // first in data blk)

                        pdbhInstance0->dbh_ulTotLen = \
                                        pkedTran[i].ked_cbDataSize;
                        pdbhInstance0->dbh_flFlags |= \
                                        (RPC_FL_32BIT | RPC_FLM_KRNL_TRAN);


                        /* Init. Text block BID = 0.  (This is the only member
                         * of the Text block which gets changed during
                         * registration; it may still be set from a previous
                         * registration since it is a static structure.)
                         */

                        ((PTBH) pkedTran[i].ked_pbTextBlk)->tbh_bidID.bid_usGroup = \
                        ((PTBH) pkedTran[i].ked_pbTextBlk)->tbh_bidID.bid_usInstance = 0;

                        // Register counter block.

                        if ((irc = PVWRegisterPerfCtrs(
                                        (PBYTE) pdbhInstance0,
                                        pkedTran[i].ked_pbTextBlk,
                                        (RPC_FL_32BIT | RPC_FLM_KRNL_TRAN))) != NO_ERROR)
                        {
                                return(irc);
                        }
                }

                // Indicate that Perfview Transient entities initialized.
                flPvwFlags |= PVW_FL_KRNL_TRAN_INIT;

                return(NO_ERROR);
        }
        else
                return(ERROR_ALREADY_EXISTS);
}


/***LP  pvwAliasPerfBlk -- get Alias to Perf counter Block
 *
 *      This function encapsulates block aliasing for _VR32AliasPerfCtrs.
 *
 *      ENTRY   pBlk            Ptr to block descriptor (bld_t)
 *              flBlk           Flags -- PG_W or NA
 *              ppbBlkAlias     Ptr to destination for Block Alias
 *              pulBlkSize      Ptr to destination for Block size (can be NULL)
 *
 *      EXIT    ppbBlkAlias = alias to pBlk in current context
 *              pulBlkSize  = cbBlk
 *
 *              NO_ERROR - success
 *              ERROR_ALREADY_EXISTS
 *
 *      NOTES   Only 1 alias is allowed per block; subsequent attempts at
 *              aliasing will fail.
 */

INT pvwAliasPerfBlk ( bld_t     *pBlk,
                      ULONG     flBlk,
                      PBYTE     *ppbBlkAlias,
                      PULONG    pulBlkSize )
{
        ULONG           cbalias;        // Size of region to alias
        ULONG           palias;         // Address of region to alias
        INT             irc;            // Return code
        VMAC            ac;             // VMMapAlias() return info buffer

        // Only provide 1 alias per block.

        if(pBlk->bld_pbAlias != NULL)
                return(ERROR_ALREADY_EXISTS);

        // Start alias on page boundary
        palias = VMRegionBase((ULONG)pBlk->bld_pbBlk);

        // Size must be page multiple
        cbalias = VMRegionSize((ULONG)pBlk->bld_pbBlk, pBlk->bld_cbSize);

        if((irc = VMMapAlias(
                                // VM flags
                flBlk | (VMAC_ARENAPRV | VMAC_LOCANY | PG_R | PG_U),
                NA,             // SEL flags (if 16:16 ptr)
                0,              // Alias type flags
                NULL,           // Alias address (don't care)
                HOBNULL,        // Map alias in current context
                palias,         // Address of region to alias
                pBlk->bld_hptda, // Context of region to alias
                cbalias,        // Size of region to alias
                SSToDS(&ac)     // Address of return info buffer
                )) != NO_ERROR)
                return(irc);    // Return error if alias fail

        // Store alias in block descriptor.

        pBlk->bld_pbAlias = (PBYTE)(ac.ac_va+((ULONG)pBlk->bld_pbBlk & PAGEMASK));

        // Send alias address to destination
        if ((irc = TKSuDWord((ULONG)(ac.ac_va+((ULONG)pBlk->bld_pbBlk & PAGEMASK)),
                             (PULONG)ppbBlkAlias)) != NO_ERROR)
                return(irc);

        // Send alias region size to dest.
        if (pulBlkSize != NULL)
        {
                if ((irc = TKSuDWord(pBlk->bld_cbSize, (PULONG)pulBlkSize)) != NO_ERROR)
                        return(irc);
        }
        return(NO_ERROR);
}
#endif

/********************************* APIs ************************************/


/***EP  VR32ConfigurePerf - Configure performance entity for measurement
 *
 *      This is the worker for the Dos32ConfigurePerf API.
 *
 *      ENTRY   ulEntityType -  Type of measurement entity (e.g. Thread
 *                              Run Time)
 *              ulConfigType -  Describes the form of the next parameters
 *                              (e.g. if thread, would be pid and tid; if
 *                              pipe might be process name of pipe OR name of
 *                              program (meaning all pipes opened by that
 *                              program).
 *              ulInfo1      -  Information which may need to be matched for
 *                              the element to be measured, depending on
 *                              entity and config. types (e.g. pid).
 *              ulInfo2      -  Information which may need to be matched for
 *                              the element to be measured, depending on
 *                              entity and config. types (e.g. tid).
 *              pszConfigSpec-  The configuration string which must be matched
 *                              by the element for measurement to commence.
 *              fExclude     -  TRUE if the specified set of elements is to be
 *                              excluded from the measurement.  (Even if an
 *                              element is configured for measurement, it is
 *                              not measured if it is excluded:  as in
 *                              "measure processes *.exe and exclude
 *                              cmd.exe.")
 *                              FALSE otherwise.
 *
 *      EXIT    NO_ERROR - success
 *              ERROR_PVW_INVALID_ENTITY
 *              ERROR_PVW_INVALID_ENTITY_TYPE
 *              ERROR_PVW_INVALID_SPEC
 *              ERROR_ALREADY_EXISTS
 *
 *      NOTES   If (ulEntityType & ET_FL_BID), then ulInfo1 == Block ID (BID).
 *
 *              If ((ulEntityType & ET_FL_MSGQ) && (ulConfigType == CC_INIT)),
 *              then ulInfo1 == Shared Event Semaphore Handle.
 *
 */

INT cdecl VR32ConfigurePerf ( THUNKSEF sef,
                              ULONG    ulEntityType,
                              ULONG    ulConfigCode,
                              ULONG    ulInfo1,
                              ULONG    ulInfo2,
                              PSZ      pszConfigSpec,
                              BOOL     fExclude )
{

#ifdef PERFVIEW

        INT     irc,i;
        ULONG   uli, flFlags;
        ULONG   ulPostCt;       /* Returned by dhw_ResetEventSem */
        PBYTE   pbBlk;
        bld_t * pData;
        bld_t * pText;
        PBID    pbidIn;
        prpd_t  prpdIn;
        prpd_t  prpdGrp;
        prpd_t  prpdSav;
        ked_t * pked;
        USHORT  usTemp;
        USHORT  smp;           //51652

                                          // crs-sem
        if ( hKSEMPerf == 0 )
           {   // create the mutex sem
           KSEMCreate( &hKSEMPerf, KRNL_MUTEXSEM );
           }

        KSEMRequestMutex( hKSEMPerf, -1 );  // crs-sem

        switch(ulConfigCode)
        {

        case CC_INIT:

                // Lock down the measurement function routines; we can't
                // can't allow a page fault when servicing many of the
                // measurement functions.

                if ((flPvwFlags & PVW_FL_MT_LOCK) == 0)
                {
                        if((irc = VMLockMem((ulong_t)&PvwMtStartLock,
                                        ((PCHAR)&PvwMtEndLock - (PCHAR)&PvwMtStartLock),
                                        VML_LONG,
                                        &hPvwMtLock,
                                        (struct pagelist_s *) VM_NO_ADDR,
                                        (ulong_t *) VM_NO_ADDR)) != NO_ERROR)

                        {
                                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                                return(irc);
                        }

                        flPvwFlags |= PVW_FL_MT_LOCK;
                }

// BUGBUG DaveBe, 02-May-90, Lock ked_pers and ked_tran memory.

                if(ulEntityType & ET_FL_KRNL)
                {
                    if ((irc = pvwInitPersist()) != NO_ERROR)
                                {
                                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                                return(irc);
                                }

                    if ((irc = pvwInitTransient()) != NO_ERROR)
                                {
                                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                                return(irc);
                                }

                    if((flPvwFlags & PVW_FLM_KRNL_INIT) == PVW_FLM_KRNL_INIT)
                    {
                        // Increment Kernel Init. reference count.

                        cusPvwKInitRef++;

                        // Perfview is now active.
                        fPerfview = TRUE;

                        // Activate all kernel measurement functions.  This
                        // activates transient as well as persistent
                        // measurement functions.

                        for (uli=0; uli<=LAST_PVMRT_INDEX; uli++)
                        {
                                if ((irc = PVWSetHook(uli, (PFN) PvwMtRouterTable[uli])) != NO_ERROR)
                                        {
                                        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                                        return(irc);
                                        }
                        }

                        // Kernel hooks activated.
                        flPvwFlags |= PVW_FLM_KRNL_HOOKS;

                        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                        return(NO_ERROR);
                    }
                }
                else
                if(ulEntityType & ET_FL_MSGQ)
                {
                    if (pbPvwMsgQ == NULL)
                    {
                        /* Create the kernel Message Queue, and initialize
                         * its header.
                         */

                        if((irc = VMAllocKHB( VM_HKH_PUB_RESRW,
                                              PVW_MSGQ_SIZE,
                                              PVWOWNER,
                                              NA,
                                              (char **)&pbPvwMsgQ) ) != NO_ERROR)
                        {
                                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                                return(irc);
                        }

                        ((PMQH) pbPvwMsgQ)->mqh_cbSize = PVW_MSGQ_SIZE;
                        ((PMQH) pbPvwMsgQ)->mqh_pbRd = \
                        ((PMQH) pbPvwMsgQ)->mqh_pbWt = pbPvwMsgQ + sizeof(MQH);

                        if((irc = dhw_OpenEventSem(ulInfo1)) != NO_ERROR)
                                {
                                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                                return(irc);
                                }

                        ((PMQH) pbPvwMsgQ)->mqh_hevMsgQ = ulInfo1;

                        if((irc = KSEMCreate( (PHKSEM) &((PMQH) pbPvwMsgQ)->mqh_hmtxMsgQ,
                                              KRNL_MUTEXSEM)) != NO_ERROR)
                        {
                                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                                return(irc);
                        }

                        /* The data collector will now wait until the next
                         * dhw_PostEventSem(pbPvwMsgQ->mqh_hevMsgQ).
                         */

                        dhw_ResetEventSem(((PMQH) pbPvwMsgQ)->mqh_hevMsgQ,
                                          (PULONG) SSToDS(&ulPostCt));

                        // 73934 - make sure overflow indicator is off
                        flPvwFlags &= ~PVW_FL_MSGQOF;

                        // Message Queue initialized.
                        flPvwFlags |= PVW_FL_MSGQ;

                        // get pre existing threads                    //aa05408
                        irc = PVWCreateInstance_preThd( );                //aa05408
                        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                        return (irc);                                  //aa05408
                        return(NO_ERROR);
                    }
                    else
                    {
                        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                        return(ERROR_ALREADY_EXISTS);
                    }
                }

                break;

        case CC_CALIBRATE:

                if (ulEntityType == ET_FLM_ALL)
                {
                       irc = pvwCalibrateTimers();
                       KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                       return( irc );
                }

                break;

        case CC_CONFIG_ET:

                if ((ulEntityType & ET_FL_BID) && (pPvwRPDHead != NULL))
                {
                    pbidIn = (PBID) SSToDS(&ulInfo1);

                    if (pvwFindRPDesc(pbidIn,SSToDS(&prpdIn)) != NO_ERROR)
                    {
                        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                        return(ERROR_PVW_INVALID_SPEC);
                    }

                    if ((pbBlk = prpdIn->rpd_Data.bld_pbBlk) == NULL)
                    {
                        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                        return(ERROR_PVW_INVALID_COUNTER_BLK);
                    }

                    if(prpdIn->rpd_flFlags & RPC_FLM_DRV)
                    {
                                // Give Tmr functions to the data block.

                                if (prpdIn->rpd_flFlags & RPC_FL_32BIT)
                                {
                                        ((PDBH) pbBlk)->dbh_pfnTmrAdd = &TMR32AddTime;
                                        ((PDBH) pbBlk)->dbh_pfnTmrSub = &TMR32SubTime;
                                }
                                else
                                {
                                        ((PDBH) pbBlk)->dbh_pfnTmrAdd = pfnTMR16AddTime;
                                        ((PDBH) pbBlk)->dbh_pfnTmrSub = pfnTMR16SubTime;
                                }
                    }
                    else
                    if (((prpdIn->rpd_flFlags & RPC_FLM_KRNL_PERS) != RPC_FLM_KRNL_PERS) &&
                        !(prpdIn->rpd_flFlags & RPC_FL_APP))
                    {
                        // Return error since not a valid entity type.

                        break;
                    }

                    // Perfview is now active. (This is done here in case
                    // kernel entities are not first initialized; they ARE
                    // normally initialized first.)
// BUGBUG 21-Aug-90, DaveBe, Is this OK?
                    fPerfview = TRUE;

                    // Increment reference count.

                    prpdIn->rpd_culRef++;

                    // Indicate "all instances" in first Config. specifier for
                    // this group.

                    prpdIn->rpd_pConfigHead->cfg_flFlags |= CFG_FL_ALL;

// don't set configure flag for ring 3 applications                    //55134
             if (prpdIn->rpd_flFlags & RPC_FL_APP)                     //55134
                goto config10;                                         //55134

                    // Set "configured" flag in descriptor.
                    prpdIn->rpd_flFlags |= RPC_FL_CONFIG;

                    // Set "configured" flag in data block


//51652        ((PDBH) prpdIn->rpd_Data.bld_pbBlk)->dbh_flFlags |= RPC_FL_CONFIG;

                                                        /*51652*/
          if ((irc = TKFuWord(SSToDS(&smp),
              (&((PDBH) prpdIn->rpd_Data.bld_pbBlk)->dbh_flFlags))) != NO_ERROR)
             {                                          /*51652*/
              KSEMReleaseMutex( hKSEMPerf );       // crs-sem
              return(irc);                              /*51652*/
             }                                          /*51652*/

          smp = smp | RPC_FL_CONFIG;                    /*51652*/
                                                        /*51652*/
          if ((irc = TKSuWord(smp ,
              (&((PDBH) prpdIn->rpd_Data.bld_pbBlk)->dbh_flFlags))) != NO_ERROR)
            {                                           /*51652*/
            KSEMReleaseMutex( hKSEMPerf );       // crs-sem
            return(irc);                                /*51652*/
            }                                           /*51652*/

  config10:                                                            //55134

/* BUGBUG 28-Jul-90, DaveBe, Must check system for ALL (un-registered)
 * instances of this group (since kernel transients are not registered unless
 * they are configured) and simulate their creation (which will allocate
 * their per-instance data blocks and register them).
 */
                    KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                    return(NO_ERROR);
                }

                break;

        case CC_CONFIG_ETNAME:

                if ((ulEntityType & ET_FL_BID) && (pPvwRPDHead != NULL))
                {
                        pbidIn = (PBID) SSToDS(&ulInfo1);

                        if (pvwFindRPDesc(pbidIn,SSToDS(&prpdIn)) != NO_ERROR)
                        {
                                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                                return(ERROR_PVW_INVALID_SPEC);
                        }

                        if(((prpdIn->rpd_flFlags & RPC_FLM_KRNL_TRAN) == RPC_FLM_KRNL_TRAN) &&
                           ((flPvwFlags & PVW_FLM_KRNL_HOOKS) == PVW_FLM_KRNL_HOOKS))
                        {

// BUGBUG DaveBe, 30-Apr-90, Must be implemented.

/* BUGBUG 28-Jul-90, DaveBe, Must check system for all (un-registered)
 * instances of this group with this entity name (since kernel transients are
 * not registered unless they are configured) and simulate their creation
 * (which will allocate their per-instance data blocks and register them).
 */

                                // Increment reference count.
                                prpdIn->rpd_culRef++;

                                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                                return(ERROR_CALL_NOT_IMPLEMENTED);
                        }
                }

                break;


        case CC_CONFIG_PROCNAME:

                if ((ulEntityType & ET_FL_BID) && (pPvwRPDHead != NULL))
                {
                        pbidIn = (PBID) SSToDS(&ulInfo1);

                        if (pvwFindRPDesc(pbidIn,SSToDS(&prpdIn)) != NO_ERROR)
                        {
                                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                                return(ERROR_PVW_INVALID_SPEC);
                        }

                        if(((prpdIn->rpd_flFlags & RPC_FLM_KRNL_TRAN) == RPC_FLM_KRNL_TRAN) &&
                           ((flPvwFlags & PVW_FLM_KRNL_HOOKS) == PVW_FLM_KRNL_HOOKS))
                        {

// BUGBUG DaveBe, 30-Apr-90, Must be implemented.

/* BUGBUG 28-Jul-90, DaveBe, Must check system for all (un-registered)
 * instances of this group belonging to this process (since kernel transients
 * are not registered unless they are configured) and simulate their creation
 * (which will allocate their per-instance data blocks and register them).
 */

                                // Increment reference count.
                                prpdIn->rpd_culRef++;

                                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                                return(ERROR_CALL_NOT_IMPLEMENTED);
                        }
                }

                break;


        case CC_DECON:

// BUGBUG DaveBe, 02-May-90, Eventually, when selective deconfiguration is
// implemented, we will decrement the reference count (rpd_culRef) and
// only deconfigure when the count reaches 0; currently we just deconfigure
// everything regardless of reference count.

            if(ulEntityType == ET_FLM_ALL)
            {
                Pvwptcblast = 0;     // clear address of last ptcb    //53160
                fPerfview = FALSE;
                // Destroy Message Queue.

                if(flPvwFlags & PVW_FL_MSGQ)
                {
////                    flPvwFlags &= ~PVW_FL_MSGQ;   // crs-sem

                        if((irc = dhw_CloseEventSem(((PMQH) pbPvwMsgQ)->mqh_hevMsgQ)) != NO_ERROR)
                                pvwAssert(FALSE);

                        /* Make sure Msg Q mutex semaphore is released before
                         * destroying it.
                         */

                        usTemp = 0;

                        KSEMQueryMutex((HKSEM) ((PMQH) pbPvwMsgQ)->mqh_hmtxMsgQ,
                                       (USHORT *) SSToDS(&usTemp));

                        if(usTemp != 0)
                        {
                                KSEMReleaseMutex((HKSEM) ((PMQH) pbPvwMsgQ)->mqh_hmtxMsgQ);
                        }

                        KSEMDestroy((HKSEM) ((PMQH) pbPvwMsgQ)->mqh_hmtxMsgQ);

                        VMFreeKHB(VM_HKH_PUB_RESRW, (char *)pbPvwMsgQ);
                        pbPvwMsgQ = NULL;

                        flPvwFlags &= ~PVW_FL_MSGQ;
                }

                // De-activate all measurement functions.

                if(flPvwFlags & PVW_FLM_KRNL_HOOKS)
                {
                        for (uli=0; uli<=LAST_PVMRT_INDEX; uli++)
                        {
                                if ((irc = PVWUnSetHook(uli)) != NO_ERROR)
                                        pvwAssert(FALSE);
                        }

                        flPvwFlags &= ~PVW_FLM_KRNL_HOOKS;
                }

                // Unlock Measurement function memory.

                if(flPvwFlags & PVW_FL_MT_LOCK)
                {
                        if((irc = VMUnlock(&hPvwMtLock)) != NO_ERROR)
                        {
                                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                                return(irc);
                        }

                        flPvwFlags &= ~PVW_FL_MT_LOCK;
                }

// BUGBUG DaveBe, 02-May-90, Unlock ked_pers and ked_tran memory.

                /* Release all aliases to registered blocks and all persistent
                 * and transient memory allocations.
                 */

                /* Note:  This does not clear the Data Block ptrs in the
                 * Kernel Entity Descriptors (ked_pbDataBlk of
                 * ked_pers[] & ked_tran[]), but since these are
                 * initialized before Registration in pvwInitPersist(), it
                 * doesn't hurt to leave them; they will get re-initialized
                 * the next time Perfview is started.
                 */

                if (pPvwRPDHead != NULL)
                {
                        /* Traverse the descriptor chain, deconfiguring as we
                         * go.  Note that we save the pointer to the "Next"
                         * desc. before calling pvwDeconRPDesc.  This is
                         * because the current desc. may be freed by
                         * pvwDeconRPDesc which would invalidate its contents.
                         * Note that all Kernel entities are de-registered as
                         * well as deconfigured (i.e. their descriptors are
                         * destroyed).
                         */

                        for (prpdGrp = pPvwRPDHead->rpd_pNextGroup; \
                             prpdGrp != pPvwRPDHead; \
                             prpdGrp = prpdSav)
                        {
                                for (prpdIn = prpdGrp->rpd_pNextInstance; \
                                     prpdIn != prpdGrp; \
                                     prpdIn = prpdSav)
                                {
                                        prpdSav = prpdIn->rpd_pNextInstance;
                                        if (prpdIn->rpd_flFlags & RPC_FL_KRNL)
                                            prpdIn->rpd_flFlags |= RPC_FL_DEREG;
                                        pvwDeconRPDesc(prpdIn);
                                }

                                prpdSav = prpdGrp->rpd_pNextGroup;
                                if (prpdGrp->rpd_flFlags & RPC_FL_KRNL)
                                        prpdGrp->rpd_flFlags |= RPC_FL_DEREG;
                                pvwDeconRPDesc(prpdGrp);
                        }
                }

                if (flPvwFlags & PVW_FL_KRNL_PERS_INIT)
                {
                        pked = (ked_t *) &ked_pers;

                        for(i=0; i < PVW_NUM_ENT_KRNL_PERS; i++)
                        {
                                // Free Persistent Data block.

                                VMFreeKHB(VM_HKH_PUB_RESRW,
                                          (char *)pked[i].ked_pbDataBlk);

                                pked[i].ked_pbDataBlk = NULL;
                        }
                }

                if (flPvwFlags & PVW_FL_KRNL_TRAN_INIT)
                {
                        pked = (ked_t *) &ked_tran;

                        for(i=0; i < PVW_NUM_ENT_KRNL_TRAN; i++)
                        {
                                /* Destroy BMP object for Transient Data
                                 * blocks; do not bother to first free the
                                 * component BMP records.
                                 */

                                BMPDestroy((void *)pked[i].ked_pbDataBlk);

                                pked[i].ked_pbDataBlk = NULL;
                        }
                }

                flPvwFlags &= ~(PVW_FLM_KRNL_INIT);
                cusPvwKInitRef = 0;

                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                return(NO_ERROR);
            }
            else
            {
                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                return(ERROR_CALL_NOT_IMPLEMENTED);
            }

            break;

        default:
                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                return(ERROR_PVW_INVALID_ENTITY_TYPE);
                break;
        }

        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
        return(ERROR_PVW_INVALID_ENTITY_TYPE);
#else
        return(NO_ERROR);
#endif

}


/***EP  VR32DeconPerf - Deconfigure performance entity from measurement
 *
 *      This is the worker for the Dos32DeconPerf API
 *
 *      ENTRY   No parameters
 *
 *      EXIT    ERROR_CALL_NOT_IMPLEMENTED
 *
 *      NOTES   This API is obsolete.  Use Dos32ConfigurePerf() instead.
 *
 */

INT cdecl VR32DeconPerf ( THUNKSEF sef )
{
        return(ERROR_CALL_NOT_IMPLEMENTED);
}



/***EP  VR32AliasPerfCtrs - Alias the address space of performance counters
 *
 *      This is the worker for the Dos32AliasPerfCtrs API.
 *
 *      ENTRY   ulRangeType   - A number identifying the particular
 *                              performance data block which is to be mapped
 *                              into current context.
 *              ulInfo        - Information used to specify block to alias
 *                              (e.g. Block ID (BID)).
 *              ppbRangeStart - Pointer to a pointer (PBYTE) which will
 *                              receive the alias address.  (Note: This is
 *                              treated differently when ulRangeType ==
 *                              RT_FL_MSGQ -- see NOTES below.)
 *              pulRangeSize  - Pointer to a location (ULONG) which will
 *                              receive the size of the aliased data block.
 *
 *      EXIT    NO_ERROR - success
 *              ERROR_PVW_INVALID_RANGE_TYPE
 *              ERROR_PVW_INVALID_SPEC
 *              ERROR_ALREADY_EXISTS
 *              ERROR_NO_MORE_ITEMS (for RT_FL_NEXT)
 *              ERROR_INSUFFICIENT_BUFFER (for RT_FL_MSGQ)
 *
 *      FLAGS   Range Type Flags (RT_FL) for 'ulRangeType' parameter -- valid
 *              values are:
 *
 *              RT_FL_BID |
 *              {RT_FL_NEXT_GROUP | RT_FL_NEXT_INSTANCE} |
 *              {RT_FL_TEXT | RT_FL_DATA} |
 *              {RT_FL_MSGQ}
 *
 *
 *      NOTES   Only 1 alias is allowed per block; subsequent attempts at
 *              aliasing will fail.
 *
 *              If ulRangeType == RT_FL_BID, then the alias for the specified
 *              BID (ulInfo) is returned.
 *
 *              If ulRangeType == (RT_FL_BID |
 *                                 {RT_FL_NEXT_GROUP | RT_FL_NEXT_INSTANCE}),
 *              then the alias for the next BID after the specified BID
 *              (ulInfo) in the Registered Perf Counter chain is returned.
 *
 *
 *              Perfview Message Queue:
 *
 *              The Message Perfview Message Queue is used to relay
 *              information about transient entities from the ring 0 kernel
 *              component of Perfview to the ring 3 data collector.  The data
 *              collector is the only client of this queue.
 *
 *              If ulRangeType == RT_FL_MSGQ, then *ppbRangeStart is treated
 *              as an input parameter rather than an output parameter as
 *              follows:
 *                      ulInfo = Size (in bytes) of destination buffer for
 *                               current messages.
 *                      ppbRangeStart = Pointer to a pointer (PBYTE) which
 *                                      contains address of the destination
 *                                      buffer for current messages.
 *                      pulRangeSize = Pointer to ULONG which will receive
 *                                     the actual number of bytes copied from
 *                                     Message Queue to destination buffer.
 *              If ulInfo < (message bytes available), then the error
 *              ERROR_INSUFFICIENT_BUFFER is returned.  In this case, if
 *              ulInfo==0, then PVW_MSGQ_SIZE (maximum message queue size) is
 *              returned in *pulRangeSize.  Otherwise in this case, the number
 *              of message bytes available is returned in *pulRangeSize.
 *
 *              The Message Queue is a contiguous block of memory which is
 *              preceded by a Header (MQH).  It is not a circular buffer.
 *              When the queue is flushed to ring 3, as a result of a request
 *              from the data collector, all messages which are copied to the
 *              data collector buffer are deleted from the queue, and any
 *              messages which are left in the message queue are copied to
 *              the beginning of the queue.
 *
 *              If the Msg queue becomes so full that it cannot accept further
 *              messages, a queue overflow message (ET_FL_MSGQOF) is placed at
 *              the end of the queue and no further messages are added to the
 *              queue until the queue is flushed by the data collector.
 *
 *              The shared event semaphore in the Message Queue header is
 *              created by the ring 3 data collector application.  The
 *              ring 0 kernel component of Perfview will post this semaphore
 *              whenever new messages enter the queue.
 *
 *              The mutex semaphore in the Message Queue header is used
 *              solely by the ring 0 component of Perfview.  Its purpose is
 *              to prevent concurrent and possibly conflicting operations on
 *              the Message Queue.
 */

INT cdecl VR32AliasPerfCtrs ( THUNKSEF sef,
                              ULONG    ulRangeType,
                              ULONG    ulInfo,
                              PBYTE    *ppbRangeStart,
                              PULONG   pulRangeSize )
{

#ifdef PERFVIEW

        INT             irc;
        bld_t           * pBlk;
        PBID            pbidIn;
        prpd_t          prpdIn;
        ULONG           ulSize;
        char *          pdstUsr;
        ULONG           ulPostCt;       /* Returned by dhw_ResetEventSem */

        KSEMRequestMutex( hKSEMPerf, -1 );      // crs-sem

        if (ulRangeType & RT_FL_BID)
        {
                if (pPvwRPDHead == NULL)
                        {
                        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                        return(ERROR_NO_MORE_ITEMS);
                        }

                pbidIn = (PBID) SSToDS(&ulInfo);

                if (ulRangeType & RT_FL_NEXT_GROUP)
                {
                        // Assume Instance 0.
                        pbidIn->bid_usInstance = 0;
                }

                if (pvwFindRPDesc(pbidIn,SSToDS(&prpdIn)) != NO_ERROR)
                {
                        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                        return(ERROR_PVW_INVALID_SPEC);
                }

                if (ulRangeType & RT_FL_NEXT_GROUP)
                {
                        if ((prpdIn = prpdIn->rpd_pNextGroup) == pPvwRPDHead)
                        {
                                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                                return(ERROR_NO_MORE_ITEMS);
                        }
                }
                else
                if (ulRangeType & RT_FL_NEXT_INSTANCE)
                {
                        prpdIn = prpdIn->rpd_pNextInstance;

                        if (prpdIn->rpd_bidID.bid_usInstance == 0)
                        {
                                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                                return(ERROR_NO_MORE_ITEMS);
                        }
                }


                if (ulRangeType & RT_FL_TEXT)
                {
                        pBlk = &prpdIn->rpd_Text;
                }
                else
                if (ulRangeType & RT_FL_DATA)
                {
                        pBlk = &prpdIn->rpd_Data;
                }
                else
                {
                        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                        return(ERROR_PVW_INVALID_RANGE_TYPE);
                }

                if (pBlk->bld_pbBlk != NULL)
                {
                        irc = pvwAliasPerfBlk( pBlk,
                                                NA,
                                                ppbRangeStart,
                                                pulRangeSize );
                        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                        return( irc );
                }

        }
        else
        if (ulRangeType & RT_FL_MSGQ)
        {
           if(pbPvwMsgQ == NULL) { // Not initialized or after DECON      //108584
              KSEMReleaseMutex(hKSEMPerf );                               //108584
              return ERROR_PRF_NOT_INITIALIZED;                           //108584
           }                                                              //108584
                //Take Message Queue Mutex semaphore while flushing Q.

                if ((irc = KSEMRequestMutex((HKSEM) ((PMQH) pbPvwMsgQ)->mqh_hmtxMsgQ,
                                            PVW_MSGQ_TIMEOUT)) != NO_ERROR)
                {
                        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                        return(irc);
                }

                ulSize = ((PMQH) pbPvwMsgQ)->mqh_pbWt - ((PMQH) pbPvwMsgQ)->mqh_pbRd;

#ifdef TESTONLY
                // Monitor maximum size of Message Queue.

                if (ulSize > ulMaxMsgQSize)
                        ulMaxMsgQSize = ulSize;
#endif //TESTONLY

                if (ulInfo == 0 || ulInfo < ulSize)
                {
                        KSEMReleaseMutex((HKSEM) ((PMQH) pbPvwMsgQ)->mqh_hmtxMsgQ);

                        if (ulInfo == 0)
                            ulSize = PVW_MSGQ_SIZE;

                        if((irc = TKSuDWord(ulSize,pulRangeSize)) != NO_ERROR)
                                {
                                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                                return(irc);
                                }
                        else
                                {
                                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                                return(ERROR_INSUFFICIENT_BUFFER);
                                }
                }
                else
                {
                        if ((irc = TKFuDWord(SSToDS(&pdstUsr), (char *) ppbRangeStart)) != NO_ERROR)
                                {
                                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                                return(irc);
                                }

                        if ((irc = TKSuBuff(pdstUsr,
                                            ((PMQH) pbPvwMsgQ)->mqh_pbRd,
                                            ulSize,
                                            TK_UM_FATAL)) != NO_ERROR)
                        {
                                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                                return(irc);
                        }

                        if ((irc = TKSuDWord(ulSize,pulRangeSize)) != NO_ERROR)
                                {
                                KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                                return(irc);
                                }

                        ((PMQH) pbPvwMsgQ)->mqh_pbRd = \
                        ((PMQH) pbPvwMsgQ)->mqh_pbWt = pbPvwMsgQ + sizeof(MQH);

                        /* Clear Msg Q event semaphore, since data transfer
                         * was successful.
                         */

                        dhw_ResetEventSem(((PMQH) pbPvwMsgQ)->mqh_hevMsgQ,
                                          (PULONG) SSToDS(&ulPostCt));

                        // Remove queue overflow flag, if it exists.

                        flPvwFlags &= ~PVW_FL_MSGQOF;

                        // Release Msg Q mutex semaphore.

                        KSEMReleaseMutex((HKSEM) ((PMQH) pbPvwMsgQ)->mqh_hmtxMsgQ);

                        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
                        return(NO_ERROR);
                }
        }

        KSEMReleaseMutex( hKSEMPerf );       // crs-sem
        return(ERROR_PVW_INVALID_RANGE_TYPE);
#else
        return(NO_ERROR);
#endif

}



/***EP  VR32RegisterPerfCtrs - Register a performance data block for collection
 *
 *      This is the worker for the Dos32RegisterPerfCtrs API.
 *
 *      ENTRY   pbDataBlk   -   Address of data block.
 *              pbTextBlk   -   Address of text block which identifies the
 *                              counters which constitute the data block.
 *              flFlags     -   Flags.
 *
 *      EXIT    NO_ERROR - success
 *              ERROR_INVALID_PARAMETER
 *              ERROR_PVW_INVALID_COUNTER_BLK
 *              ERROR_PVW_INVALID_TEXT_BLK
 *
 *
 *      NOTES   See also the worker function PVWRegisterPerfCtrs().
 *
 *              Perfview will assign the block identifier (BID), bidID, at
 *              registration time, and will place the BID in the appropriate
 *              fields of the application's data and text blocks.  This BID
 *              will be used at data analysis time to correlate application
 *              performance data with registered text block information.  The
 *              counters and timers within the data block should be grouped
 *              together on a per-entity basis; that is, all counters and
 *              timers for a particular entity should be adjacent.  The order
 *              of the Name blocks in the Text block must correspond exactly
 *              with the order of the counters in the Data block.  A
 *              semaphore, with handle hSem, will prevent Perfview from
 *              collecting the data block while it is unstable; that is,
 *              while the device driver is updating its counters.  It is the
 *              Application's responsibility to create the semaphore.
 *
 *              Since an application can terminate at any time, the data
 *              collector will register an exception handler to intercept
 *              page faults due to the data block disappearing during data
 *              collection.  When an application terminates, the kernel
 *              component of Perfview will test if the application was
 *              registered.  If it was registered AND it was configured for
 *              measurement, a termination message is sent to the data
 *              collector, who will then deconfigure the application.
 *              Once the application has been deconfigured, the copy of the
 *              text block which was made at registration time, and any
 *              associated block aliases are freed.
 *
 *              Note that the format of the block structures for the
 *              Dos32RegisterPerfCtrs, dh_RegisterPerfCtrs, and
 *              fsh_RegisterPerfCtrs functions are all identical.
 *
 *              DATA BLOCK: The Data Block is where the actual counters
 *              reside.  This is collected periodically by Perfview:
 *
 *              ULONG -- ulTotLen:   Data Block Total Length (in bytes)
 *              BID   -- bidID:      Block IDentifier
 *              ULONG -- flFlags:    Flags
 *              ULONG -- hSem:       Semaphore handle
 *              PFN   -- pfnTmrAdd:  Currently unused for Dos32RegisterPerfCtrs
 *              PFN   -- pfnTmrSub:  Currently unused for Dos32RegisterPerfCtrs
 *              DWORD -- ulCtr:      Counter 1
 *                |        |            |
 *              QWORD -- qwTmr:      Timer 1
 *                |        |            |
 *                |        |            |
 *              DWORD -- ulCtr:      Counter M
 *                |        |            |
 *              QWORD -- qwTmr:      Timer N
 *
 *              TEXT BLOCK:  The Text block is associated the Data block at
 *              registration time.  It is copied by Perfview during
 *              registration and saved for later collection when Perfview is
 *              enabled.  The Text block allows specification of Group and
 *              Instance name strings, along with Name blocks for each
 *              individual counter:
 *
 *              ULONG -- ulVersion   Version number
 *              BID   -- bidID:      Block IDentifier
 *              TBN   -- tbnGroup    Group Name Block.
 *              TBN   -- tbnInstance Instance Name Block.
 *              PSZ   -- pszMsgFile  Message File name
 *              PSZ   -- pszHelpFile Help File name
 *              ULONG -- culName:    Number of Ctr and Tmr Name blocks (N+M)
 *              PTBN  -- pName:      Pointer to an array of Name blocks.
 *
 *              NAME BLOCK:  Each Name block  consists of  a Flag word which
 *              denotes the type of counter and its use, the counter size
 *              (this is currently optional for known counter types), a
 *              message number for the associated Message and Help files, and
 *              a pointer to an ASCIIZ string which is the default name of
 *              the counter (if the Message and/or Help files are not
 *              present):
 *
 *              ULONG  -- ulFlags:      Flags for Counter 1
 *              USHORT -- usSize:       Size of Counter 1
 *              USHORT -- usMsgID:      Message ID (name)
 *              PSZ    -- pszName:      Pointer to ASCIIZ Name of Counter 1
 *                |          |              |
 *                |          |              |
 *              ULONG  -- usFlags:      Flags for Counter (N+M)
 *              USHORT -- usSize:       Size of Counter 1
 *              USHORT -- usMsgID:      Message ID (name)
 *              PSZ    -- pszName:      Pointer to ASCIIZ Name of Counter (N+M)
 *
 *              All strings in the text block will be zero-terminated.
 *              Though there is no actual limit to the length of these
 *              strings, we suggest that the first 10 characters be unique,
 *              because of the practical limit on column width during tabular
 *              data analysis display.  Each counter and timer will also have
 *              a flag word associated with it in the text block.  Their
 *              purpose is to provide a means for identifying the particular
 *              counter type.  Counter type values are:
 *
 *                      TBN_FL_CT_CNT    Counter
 *                      TBN_FL_CT_TIMR   Timer
 *                      TBN_FL_CT_QLEN   Queue length
 *                      TBN_FL_CT_SUMSQ  Sum of Squares
 *                      TBN_FL_CT_BULKCT Bulk Counter
 *
 *              Other bit assignments are T.B.D.
 */

INT cdecl VR32RegisterPerfCtrs (  THUNKSEF sef,
                                  PBYTE    pbDataBlk,
                                  PBYTE    pbTextBlk,
                                  ULONG    flFlags )
{

#ifdef PERFVIEW

//BUGBUG, 04-Jun-90, DaveBe, <This currently does NOT use TKSu/FuBuff().>

        return(PVWRegisterPerfCtrs(pbDataBlk,
                                   pbTextBlk,
                                   (flFlags | RPC_FL_APP)));
#else
        return(NO_ERROR);
#endif
}
