Extensible Counter Loading & Unloading Utilities

                Design Specification and Overview



                                                       created: 15 Feb 93
                                                                a-robw
                                                                russbl

                                                       updated: 16 Nov 95
                                                                a-robw

Overview
                                   
Device driver, service and application developers that wish to provide 
performance measuring capability in their software must have a way to 
incorporate the names of the performance counters and counter objects 
into the registry. Currently the only methods available are the manual 
installation of the names into the registry or for each developer to 
devise a scheme to do it programmatically. Since the "correct" way to 
do this is poorly documented and difficult to explain, the following 
utilities and library functions are provided to make the installation
and removal of these extensible counters much simpler and less prone 
to error and confusion.

The two command line utilities provided with Windows NT are shown below.

    > LodCtr MyDriver.INI

    > UnLodCtr MyDriver


LoadPerf.DLL is provided to the software developer to provide access 
to these same functions (in both Unicode & ANSI formats) from within 
a setup program.

    LoadPerfCounterTextStrings (
        LPTSTR  szCommandLine
        BOOL    bQuietModeFlag)

    UnloadPerfCounterTextStrings (
        LPTSTR  szCommandLine,
        BOOL    bQuietMode)

The contents of the string arguments in the above functions are the
same as those for the command line.
 

The installation utility (LodCtr) accepts as an argument the name of 
the device or application's counter .INI file. The format of the .INI 
file is described in detail later in this document. This utility will 
enter the counter names and explain text stored in the .INI file into 
the corresponding data file and update the necessary keys and values 
for the extensible performance counter DLL.

The removal utility UnLodCtr accepts as an argument, the name of the 
regsistry key which is to have its names removed from the data files.
This key is the registry key that the application, service or device 
driver us using under the ...\Services key and has the Performance
subkey.
 
The extensible performance counter DLL must be written to look up 
the base values of the counter names and explain text during 
initialization for this to function properly.

.INI file format

The .ini file for the extensible performance counter will consist 
of keys and values in a format similar to that of a MS-Windows 
.INI file (e.g. WIN.INI) This will allow a format that is somewhat 
self-documenting as well as allow current Win32 utilities to process 
it and parse the data (e.g. GetPrivateProfileString). A single file 
was selected to minimize the development and maintenance overhead of 
adding or modifying counters and adding foreign language support. 
The contents of the .INI file are described below:


Usage Notes:

The following assumptions are made in the use of counter names and
explain text and should be followed in order to insure predictable and
reliable operation.

    - Index number ranges must not be overlapping between drivers

        The range of index numbers used by an extensible counter 
        must fall between the first and last values (see below).
        (gaps are allowed within the range used). If LodCtr is used
        then this won't be a problem.

    - Names must be assigned to EVEN numbers and Explain text assigned
        to ODD numbers.

        If the convention is followed as shown in the examples below,
        where each item is given an offset of an even number starting 
        from 0, then LodCtr will do the right thing and make this 
        assignment automatically. For this to work, however, the offest 
        values MUST ALWAYS BE EVEN NUMBERS.

    - Manual assignment of counter index values is not recommended.
        
        Failure to follow all the assumptions or manually modifying
        or "hard-coding" index values may result in counter name 
        text corruption or erroneous display of names.

    - Symbol file format must conform to the following:
        
        #define NAME    decimal_number

        The symbol file processor is pretty dumb and can read .H header
        files but will only understand lines that conform to the above
        format. (in line comments after the number are OK) see the
        example below for more information.


// Begin .INI file format

[info]
drivername=<name of device found under the CurrentControlSet\Services key>
symbolfile=<.h file containing symbolic offsets of counters>

[languages] // one key (value optional) for each language supported in file
009=
 .
 .
 .
 .


[text]  // counter & explain text for customer-defined counters
offset_langid_NAME=text
offset_langid_HELP=text

// offset must be a symbolic constant (from symbolfile)
// offset value must be an even number (see code example for why)
// NAME and HELP are literal text and identify counter names or 
// explain text
// langid must be listed as a key under [languages]
// text must be entered on a single line (though it can be a long one)

// end .INI file format


The .ini file must be loaded into the registry before the extensible
performance counter DLL is initialized (e.g. during or immediately 
after the driver is loaded for the first time. Once the counter names 
are loaded, however, they will remain until they are removed or NT is 
reinstalled.


Following is an example of how the various components of an extensible 
counter would incorporate the definitions of the .INI file and the use
of the LodCtr and UnLodCtr utilities.  This example has one object and
two counters.

// begin devdef.H file

// legal constant definitions

#define OBJECT_1    0
#define DEVICE_COUNTER_1    2
#define DEVICE_COUNTER_2    4


// end devdef.H file

// BEGIN: Object & Counter structure initialization file

// defines static structures used to build the perf data that is
// returned by the extensible counter routines

#include "devdef.h"
        
MY_DEVICE_CTR_DEFINITION MyDeviceCtrDefinition = {

    {   sizeof(MY_DEVICE_CTR_DEFINITION) + SIZE_OF_CTR_DATA,
        sizeof(MY_DEVICE_CTR_DEFINITION),
        sizeof(PERF_OBJECT_TYPE),
        OBJECT_1,
        0,
        OBJECT_1,
        0,
        PERF_DETAIL_ADVANCED,
        (sizeof(MY_DEVICE_CTR_DEFINITION-sizeof(PERF_OBJECT_TYPE))/
        sizeof(PERF_COUNTER_DEFINITION),
        1,
        0,
        0
    },
    {   sizeof(PERF_COUNTER_DEFINITION),
        DEVICE_COUNTER_1,
        0,
        DEVICE_COUNTER_1,
        0,
        0,
        PERF_DETAIL_ADVANCED, 
        PERF_COUNTER_COUNTER, 
        sizeof(DWORD),
        DEVICE_COUNTER_1_DATA_OFFSET
    },
    {   sizeof(PERF_COUNTER_DEFINITION),
        DEVICE_COUNTER_2,
        0,
        DEVICE_COUNTER_2,
        0,
        0,
        PERF_DETAIL_ADVANCED,
        PERF_COUNTER_COUNTER,
        sizeof(DWORD),
        DEVICE_COUNTER_2_DATA_OFFSET,
    }
};

// END: Object & Counter structure initialization file

// begin .INI file example
[info]
drivername=DriverName
symbolfile=devdef.h

[languages] 
009=English
00C=OtherLanguage

[text]  
OBJECT_1_009_NAME=Device Name
OBJECT_1_009_HELP=Displays performance statistics on Device Name
OBJECT_1_00C_NAME=Device Name in other language
OBJECT_1_00C_HELP=Displays performance of Device Name in other language

DEVICE_COUNTER_1_009_NAME=Counter A
DEVICE_COUNTER_1_009_HELP=Displays the current value of Counter A
DEVICE_COUNTER_1_00C_NAME=Counter A in other language
DEVICE_COUNTER_1_00C_HELP=Displays the value of Counter A in other language

DEVICE_COUNTER_2_009_NAME=Counter B
DEVICE_COUNTER_2_009_HELP=Displays the current rate of Devices B
DEVICE_COUNTER_2_00C_NAME=Counter B in other language
DEVICE_COUNTER_2_00C_HELP=Displays the rate of Device B in other language

// end .INI file


OpenPerformanceData ( ... args ... ) 
{

        .
        .
        .

    // execute this code before accessing or passing any perf. data
    // objects.

    status = RegOpenKeyEx (
        HKEY_LOCAL_MACHINE,
        "\\System\\CurrentControlSet\\Service\\DriverName\\Performance",
        NULL,
        SAM, 
        &hKeyDriverPerf);

    size = sizeof (DWORD);
    Status = RegQueryValueEx (
                hKeyDriverPerf, 
                "First Counter"
                0L,
                &type,
                (LPBYTE)&dwFirstCounter,
                &size);

    size = sizeof (DWORD);
    Status = RegQueryValueEx(
                hKeyDriverPerf, 
                "First Help"
                0L,
                &type,
                (LPBYTE)&dwFirstHelp,
                &size);

    //
    //  NOTE: the initialization program could also retrieve
    //      LastCounter and LastHelp if they wanted to do 
    //      bounds checking on the new number. e.g.
    //
    //      counter->CounterNameTitleIndex += dwFirstCounter;
    //      if (counter->CounterNameTitleIndex > dwLastCounter) {
    //          LogErrorToEventLog (INDEX_OUT_OF_BOUNDS);
    //      }

    For each counter object {
        Object->ObjectNameTitleIndex += dwFirstCounter;
        Object->ObjectHelpTitleIndex += dwFirstHelp;

        for each counter definition in the object {
            counter->CounterNameTitleIndex += dwFirstCounter;
            counter->CounterHelpTitleIndex += dwFirstHelp;

        }
    }

    RegCloseKey (hKeyDriverPerf);
        .
        .
        .

}


When LodCtr has loaded the contents of the .INI file the following 
registry keys will have been updated.  The ":" indicates a Value of
a Key; other symbols are keys in the registry.

MACHINE
    SYSTEM
        CurrentControlSet
            Services
                <devicename>
                    Performance
                        :First Counter (updated to show current value)
                        :First Help    (updated to show current value)
                        :Last Counter  (updated to show current value)
                        :Last Help     (updated to show current value)
                
MACHINE
    SOFTWARE
        Microsoft
            Windows NT
                CurrentVersion
                    Perflib            
                        :Last Counter (updated to show current value)
                        :Last Help    (updated to show current value)  



After UnLodCtr is run to remove a driver's counters from the data file,
the following changes to the registry will take place

MACHINE
    SYSTEM
        CurrentControlSet
            Services
                <devicename>
                    Performance
                        :First Counter     (value deleted)
                        :First Help        (value deleted)
                        :Last Counter      (value deleted)
                        :Last Help         (value deleted)
                
MACHINE
    SOFTWARE
        Microsoft
            Windows NT
                CurrentVersion
                    Perflib            
                        :Last Counter (updated to show current value)
                        :Last Help    (updated to show current value)