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= 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 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 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)