Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1814 lines
52 KiB

/*++
Copyright (c) 1997-1999 Microsoft Corporation
Module Name:
perrepsr.c
Abstract:
This file defines the server side of PERFMON support and contains the following :
1. Functions used to initialize the registry keys used by PERFMON.
2. Functions that are used by the File Replication Service
to add and delete Object Instances (PERFMON) from the Registry
and the hash tables (the basic hashing routines used here are
defined in the file util\qhash.c).
3. Functions to create and use the hash tables which store data of
the instance counters measured by PERFMON.
4. RPC server functions used by the performance dll (client) to collect
data and send it to the PERFMON app.
Author:
Rohan Kumar [rohank] 13-Sept-1998
David Orbits [Davidor} 6/Oct/98 - Revised. Changed nameing,
changed registry query, eliminated mallocs, closed
key handle leak, Moved priv funcs out of common
header, general cleanup.
Environment:
User Mode Service
Revision History:
--*/
//
// Included below are the header file that contain the definition
// of data structures used in the functions in this file. The header
// file "perffrs.h" defines the RPC interface and is generated at compile
// time by the build utility.
//
#include <perrepsr.h>
#include <perffrs.h>
#include "..\perfdll\repset.h"
//
// FRS_UniqueID and FRC_UniqueID are the Keys used to
// hash in the counter data structures into the hash tables
// for the Objects FILEREPLICASET and FILEREPLICACONN. They
// are unique for every instance of the Objects.
//
ULONGLONG FRS_UniqueID = 1;
ULONGLONG FRC_UniqueID = 1;
//
// The critical section object is used for acheiving mutual exclusion
// when adding or deleting instances (the UniqueID variable has to be safe)
//
CRITICAL_SECTION *PerfmonLock = NULL;
#define AcquirePerfmonLock EnterCriticalSection (PerfmonLock);
#define ReleasePerfmonLock LeaveCriticalSection (PerfmonLock);
//
// Hash Table definitions
//
PQHASH_TABLE HTReplicaSet, HTReplicaConn;
HANDLE PerfmonProcessSemaphore = INVALID_HANDLE_VALUE;
//
// The Context data structure used by the hash table enumeration routines
//
typedef struct _CONTEXT_DATA {
PWCHAR name; // name of the Instance
ULONGLONG KeyValue; // Key value of the Instance
ULONG OBJType; // Object Type of the Instance
} ContextData, *PContextData;
#define MAX_CMD_LINE 256
extern ReplicaSetValues RepSetInitData[FRS_NUMOFCOUNTERS];
#undef GET_EXCEPTION_CODE
#define GET_EXCEPTION_CODE(_x_) \
{ \
(_x_) = GetExceptionCode(); \
if (((LONG)(_x_)) < 0) { \
(_x_) = FRS_ERR_INTERNAL_API; \
} \
/* NTFRSAPI_DBG_PRINT2("Exception caught: %d, 0x%08x\n", (_x_), (_x_)); */ \
}
//
// The Total Instance
//
PHT_REPLICA_SET_DATA PMTotalInst = NULL;
//
// Internal functions
//
LONG
PmInitPerfmonRegistryKeys(
VOID
);
LONG
PmInitializeRegistry (
DWORD
);
ULONGLONG
PmFindTheKeyValue(
PContextData
);
VOID
PmSetTheCOCounters(
PHT_REPLICA_SET_DATA
);
DWORD
PmHashFunction(
PVOID,
ULONG
);
DWORD
PmSearchTable(
PQHASH_TABLE,
PQHASH_ENTRY,
PQHASH_ENTRY,
PVOID
);
VOID
InitializePerfmonServer (
VOID
)
/*++
Routine Description:
This routine inits the perfmon server for NTFRS.
It inits the crit sect for the PerfmonLock variable
It creates the hash tables of the specified size to store
Instance counter values for the Objects. It also assigns the hash function
to be used with each created table.
It inits the perfmon registry keys.
Arguments:
none
Return Value:
none
--*/
{
#undef DEBSUB
#define DEBSUB "InitializePerfmonServer:"
ULONG WStatus;
//
// Use a semaphore to ensure that only one process provides perfmon data.
// A unique semaphore is only needed in test setup when we want
// to run multiple copies of FRS on one machine and only want
// one of the copies to register the perfmon interface.
//
if (RunningAsAService) {
PerfmonProcessSemaphore = CreateSemaphoreW(NULL,
0,
0x7fffffff,
NULL);
}else{
PerfmonProcessSemaphore = CreateSemaphoreW(NULL,
0,
0x7fffffff,
L"NTFRS_Sempahore_");
}
WStatus = GetLastError();
if (!HANDLE_IS_VALID(PerfmonProcessSemaphore)) {
PerfmonProcessSemaphore = INVALID_HANDLE_VALUE;
DPRINT_WS(0,"CreateSemaphore returned", WStatus);
return;
}
if (WIN_ALREADY_EXISTS(WStatus)) {
FRS_CLOSE(PerfmonProcessSemaphore);
DPRINT(0,"PerfmonProcessSemaphore already exists\n");
return;
}
//
// Allocate memory for the lock
//
PerfmonLock = (CRITICAL_SECTION *) FrsAlloc (sizeof(CRITICAL_SECTION));
//
// Initialize the critical section object
//
INITIALIZE_CRITICAL_SECTION(PerfmonLock);
//
// create the hash tables and assign the hash functions. One table
// for replica set objects and one for connection objects.
//
HTReplicaSet = FrsAllocTypeSize(QHASH_TABLE_TYPE, HASHTABLESIZE);
SET_QHASH_TABLE_HASH_CALC(HTReplicaSet, PmHashFunction);
HTReplicaConn = FrsAllocTypeSize(QHASH_TABLE_TYPE, HASHTABLESIZE);
SET_QHASH_TABLE_HASH_CALC(HTReplicaConn, PmHashFunction);
}
VOID
ShutdownPerfmonServer (
VOID
)
/*++
Routine Description:
This routine is called by the application just before it ends
Arguments:
none
Return Value:
none
--*/
{
#undef DEBSUB
#define DEBSUB "ShutdownPerfmonServer:"
if (HANDLE_IS_VALID(PerfmonProcessSemaphore)) {
if (PerfmonLock != NULL) {
//
// Delete the critical section object & Free the allocated memory
//
DeleteCriticalSection (PerfmonLock);
PerfmonLock = FrsFree (PerfmonLock);
}
//
// Free the hash tables.
//
HTReplicaSet = FrsFreeType (HTReplicaSet);
HTReplicaConn = FrsFreeType (HTReplicaConn);
//
// Close the semaphore handle.
//
FRS_CLOSE(PerfmonProcessSemaphore);
}
}
DWORD
PmHashFunction (
IN PVOID Qkey,
IN ULONG len
)
/*++
Routine Description:
This is the hashing function used by the functions that Lookup,
Add or Delete entries from the Hash Tables. The Key is a 64 bit
number and the hashing function casts it to a 32 bit number and
returns it as the hash value.
Arguments:
QKey - Pointer to the Key to be hashed.
len - Length of QKey (unused here).
Return Value:
The hashed value of the Key.
--*/
{
#undef DEBSUB
#define DEBSUB "PmHashFunction:"
DWORD key; // hashed key value to be returned
PULONGLONG p; // hash the key to PULONGLONG
p = (PULONGLONG)Qkey;
key = (DWORD)*p;
return (key);
}
ULONG
PmSearchTable (
IN PQHASH_TABLE Table,
IN PQHASH_ENTRY BeforeNode,
IN PQHASH_ENTRY TargetNode,
IN OUT PVOID Context
)
/*++
Routine Description:
This routine is called by the QHashEnumerateTable function and is used
to add context to the enumeration. Here, we go through the table till
a node containing a specified instance (name contained in the context structure)
is reached.
Arguments:
Table - The hash table to be searched.
BeforeNode - The node previous to the target node in the hash table(unused).
AfterNode - The node which is being examined.
Context - A structure containing the name to be matched and key value to be set.
Return Value:
FrsErrorFoundKey - Key mapping the name was found
FrsErrorSuccess - Key was not found
--*/
{
#undef DEBSUB
#define DEBSUB "PmSearchTable:"
PContextData contxt;
PWCHAR InstanceName;
PHT_REPLICA_SET_DATA p;
PHT_REPLICA_CONN_DATA q;
//
// The context is of the type pointer to ContextData datastructure
//
contxt = (PContextData) Context;
InstanceName = (PWCHAR) contxt->name;
DPRINT1(5, "PERFMON: InstanceName: %ws\n", InstanceName);
DPRINT1(5, "PERFMON: TargetNode: %08x\n", TargetNode);
DPRINT1(5, "PERFMON: TargetNode->qDATA: %08X %08x\n",
PRINTQUAD(TargetNode->QData));
//
// The Object Type is either REPLICASET or REPLICACONN
//
if (contxt->OBJType == REPLICASET) {
//
// Its a REPLICASET Object
//
p = (PHT_REPLICA_SET_DATA)(TargetNode->QData);
DPRINT1(5, "PERFMON: p: %08x\n", p);
DPRINT1(5, "PERFMON: p->oid: %08x\n", p->oid);
DPRINT1(5, "PERFMON: p->oid->name: %08x\n", p->oid->name);
DPRINT1(5, "PERFMON: p->oid->name: %ws\n", p->oid->name);
DPRINT1(5, "PERFMON: p->oid->key: %08x %08x\n", PRINTQUAD(p->oid->key));
//
// Check to see if the names are the same
//
if ( (wcscmp(InstanceName, p->oid->name)) == 0) {
//
// Check to see if the names are the same
//
contxt->KeyValue = p->oid->key;
DPRINT(5, "PERFMON: FOUND\n");
return FrsErrorFoundKey;
}
else
return FrsErrorSuccess; // Continue enumerating through the list of nodes
}
else {
//
// Its a REPLICACONN Object.
//
q = (PHT_REPLICA_CONN_DATA)(TargetNode->QData);
DPRINT1(5, "PERFMON: q: %08x\n", q);
DPRINT1(5, "PERFMON: q->oid: %08x\n", q->oid);
DPRINT1(5, "PERFMON: q->oid->name: %08x\n", q->oid->name);
DPRINT1(5, "PERFMON: q->oid->name: %ws\n", q->oid->name);
DPRINT1(5, "PERFMON: q->oid->key: %08x %08x\n", PRINTQUAD(q->oid->key));
if ( (wcscmp(InstanceName, q->oid->name)) == 0) {
contxt->KeyValue = q->oid->key;
DPRINT(5, "PERFMON: FOUND\n");
return FrsErrorFoundKey;
}
else
return FrsErrorSuccess;
}
}
LONG
PmInitPerfmonRegistryKeys (
VOID
)
/*++
Routine Description:
This routine is the called by the ntfrs application to Initialize the
appropriate Keys and Values of the PERFMON Objects in the Registry.
It calls the PmInitializeRegistry routine (described below) on the Objects.
It also adds the total instance to the REPLICASET Object.
Arguments:
none
Return Value:
ERROR_SUCCESS - The Initialization was successful OR
Appropriate DWORD value for the Error status
--*/
{
#undef DEBSUB
#define DEBSUB "PmInitPerfmonRegistryKeys:"
LONG WStatus = ERROR_SUCCESS;
enum object ObjType;
//
// Initialize the REPLICASET Object
//
ObjType = REPLICASET;
WStatus = PmInitializeRegistry(ObjType);
if (!WIN_SUCCESS(WStatus)) {
DPRINT(0, "Error: PmInitializeRegistry(L\"FileReplicaSet\")\n");
return WStatus;
}
//
// Initialize the REPLICACONN Object
//
ObjType = REPLICACONN;
WStatus = PmInitializeRegistry(ObjType);
if (!WIN_SUCCESS(WStatus)) {
DPRINT(0, "Error: PmInitializeRegistry(L\"FileReplicaConn\")\n");
return WStatus;
}
//
// Set the fields of the total instance
//
PMTotalInst = (PHT_REPLICA_SET_DATA) FrsAlloc (sizeof(HT_REPLICA_SET_DATA));
PMTotalInst->RepBackPtr = NULL;
//
// Add it to the REPLICASET Hash table
//
WStatus = AddPerfmonInstance(REPLICASET, PMTotalInst, TOTAL_NAME);
return WStatus;
}
LONG
PmInitializeRegistry (
IN DWORD ObjectType
)
/*++
Routine Description:
This routine is the called by the PmInitPerfmonRegistryKeys function
to Initialize the appropriate Keys and Values of the Object (ObjectType)
in the Registry.
Arguments:
ObjectType - The Object whose Keys and Values have to be Initialized
Return Value:
ERROR_SUCCESS - The Initialization of the Object was successful OR
Appropriate DWORD value for the Error status
--*/
{
#undef DEBSUB
#define DEBSUB "PmInitializeRegistry:"
ULONG WStatus = ERROR_SUCCESS;
ULONG WStatus1;
DWORD size, flag;
HKEY key = INVALID_HANDLE_VALUE;
PWCHAR ObjSubKey, PerfSubKey, LinSubKey, OpFn, ClFn, CollFn, iniflCmd, iniflApp, unldCmd, unldApp;
WCHAR CommandLine[MAX_CMD_LINE];
DWORD type;
DWORD CounterVersion = 0;
DWORD Temp;
BOOL UnloadCounters = FALSE;
BOOL LoadCounters = FALSE;
//
// Set all the parameters used in the function depending upon the Type of Object
//
if ( ObjectType == REPLICASET ) {
//
// The Keys to be set in the registry
//
ObjSubKey = REPSETOBJSUBKEY;
PerfSubKey = REPSETPERFSUBKEY;
LinSubKey = REPSETLINSUBKEY;
//
// The Open function (called by PERFMON when it starts up) of REPLICASET
//
OpFn = REPSETOPENFN;
//
// The Close function (called by PERFMON when it closes) of REPLICASET
//
ClFn = REPSETCLOSEFN;
//
// The Collect function (called by PERFMON to collect data) of REPLICASET
//
CollFn = REPSETCOLLECTFN;
//
// The lodctr utility to add the counter values
//
iniflApp = LDCTRAPP;
iniflCmd = REPSETINI;
//
// The unlodctr utility to remove the counter values
//
unldApp = UNLDCTRAPP;
unldCmd = REPSETUNLD;
} else {
//
// Similar settings for the REPLICACONN Object
//
ObjSubKey = REPCONNOBJSUBKEY;
PerfSubKey = REPCONNPERFSUBKEY;
LinSubKey = REPCONNLINSUBKEY;
OpFn = REPCONNOPENFN;
ClFn = REPCONNCLOSEFN;
CollFn = REPCONNCOLLECTFN;
iniflApp = LDCTRAPP;
iniflCmd = REPCONNINI;
unldApp = UNLDCTRAPP;
unldCmd = REPCONNUNLD;
}
//
// Create a key for the Object under the Sevices Key in the Registry. If the Key
// already exists, its opened.
//
WStatus = RegCreateKeyEx (HKEY_LOCAL_MACHINE,
ObjSubKey,
0L,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&key,
&flag);
CLEANUP_WS(0, "Error: RegCreateKeyEx.", WStatus, CLEANUP2);
size = sizeof(DWORD);
WStatus = RegQueryValueEx(key, L"Counter Version", NULL, &type, (PUCHAR)&CounterVersion, &size);
if (!WIN_SUCCESS(WStatus) || (type != REG_DWORD) ||
CounterVersion != NtFrsPerfCounterVer) {
UnloadCounters = TRUE;
LoadCounters = TRUE;
}
FRS_REG_CLOSE(key);
//
// Create a key called Performance under the Object's Key (created above) in the Registry
//
WStatus = RegCreateKeyEx (HKEY_LOCAL_MACHINE,
PerfSubKey,
0L,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&key,
&flag);
CLEANUP_WS(0, "Error: RegCreateKeyEx.", WStatus, CLEANUP2);
//
// If its a newly created Performance key, we need to set some Values to it.
// If it is a newly created performance key then we need to load the counters.
//
if (flag == REG_CREATED_NEW_KEY) {
size = ((1 + wcslen(PERFDLLDIRECTORY)) * (sizeof(WCHAR)));
WStatus = RegSetValueEx (key, L"Library", 0L, REG_EXPAND_SZ,
(BYTE *)PERFDLLDIRECTORY, size);
CLEANUP_WS(0, "Error: RegSetValueEx.", WStatus, CLEANUP);
size = (1 + wcslen(OpFn)) * (sizeof(WCHAR));
WStatus = RegSetValueEx (key, L"Open", 0L, REG_SZ, (BYTE *)OpFn, size);
CLEANUP_WS(0, "Error: RegSetValueEx.", WStatus, CLEANUP);
size = (1 + wcslen(ClFn)) * (sizeof(WCHAR));
WStatus = RegSetValueEx (key, L"Close", 0L, REG_SZ, (BYTE *)ClFn, size);
CLEANUP_WS(0, "Error: RegSetValueEx.", WStatus, CLEANUP);
size = (1 + wcslen(CollFn)) * (sizeof(WCHAR));
WStatus = RegSetValueEx (key, L"Collect", 0L, REG_SZ, (BYTE *)CollFn, size);
CLEANUP_WS(0, "Error: RegSetValueEx.", WStatus, CLEANUP);
//
// The performance subkey is newly created. We only need
// to load counters as they are new.
//
UnloadCounters = FALSE;
LoadCounters = TRUE;
} else {
//
// The performance key exists. If this key exists then
// the counters are probably loaded. If someone has
// manually unloaded the counters by calling unloadctr from
// command line then the counters may be unloaded.
// Look for the FirstCounter/FirstHelp/LastCounter/LastHelp
// values. If they don't exist then the counters are not
// loaded. Mark them to be loaded.
//
size = sizeof(DWORD);
WStatus = RegQueryValueEx(key, L"First Counter", NULL, &type, (PUCHAR)&Temp, &size);
if (!WIN_SUCCESS(WStatus) || (type != REG_DWORD)){
// counters are not loaded.
LoadCounters = TRUE;
}
}
FRS_REG_CLOSE(key);
if (UnloadCounters == TRUE) {
//
// Run unlodctr command on the application incase counters have changed
// Copy the command line because CreateProcess() wants to be able to
// write into it. Sigh.
//
wcscpy(CommandLine, unldCmd);
DPRINT1(1,"Running: %ws\n", CommandLine);
WStatus = FrsRunProcess(unldApp,
CommandLine,
INVALID_HANDLE_VALUE,
INVALID_HANDLE_VALUE,
INVALID_HANDLE_VALUE);
//
// If the unloadctr above failed then don't execute loadctr.
// This avoids the registry from getting corrupted.
//
DPRINT1_WS(0, "Error Running %ws;", CommandLine, WStatus);
}
if (LoadCounters == TRUE ) {
//
// Run the lodctr command on the .ini file of the Object
// Copy the command line because CreateProcess() wants to be able to
// write into it. Sigh.
//
WStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, ObjSubKey, 0, KEY_ALL_ACCESS, &key);
CLEANUP_WS(0, "Error: RegOpenKeyEx.", WStatus, CLEANUP2);
wcscpy(CommandLine, iniflCmd);
DPRINT1(1,"Running: %ws\n", CommandLine);
WStatus = FrsRunProcess(iniflApp,
CommandLine,
INVALID_HANDLE_VALUE,
INVALID_HANDLE_VALUE,
INVALID_HANDLE_VALUE);
if (!WIN_SUCCESS(WStatus)) {
//
// If there was an error loading the counters then set
// the "Counter Version" value to 0 so we try to
// load the counters the next time.
//
CounterVersion = 0;
WStatus = RegSetValueEx(key, L"Counter Version", 0, REG_DWORD, (PCHAR)&CounterVersion, sizeof(DWORD));
CLEANUP_WS(0, "Error: RegSetValueEx.", WStatus, CLEANUP);
DPRINT1_WS(0, "Error Running %ws;", CommandLine, WStatus);
goto CLEANUP;
}
//
// If the counters are loaded correctly then update the
// "Counter Version" so we don't load them again next
// time.
//
WStatus = RegSetValueEx(key, L"Counter Version", 0, REG_DWORD, (PCHAR)&NtFrsPerfCounterVer, sizeof(DWORD));
CLEANUP_WS(0, "Error: RegSetValueEx.", WStatus, CLEANUP);
FRS_REG_CLOSE(key);
}
//
// Create a key called Linkage under the Object's Key (created above) in the Registry
//
WStatus = RegCreateKeyEx (HKEY_LOCAL_MACHINE,
LinSubKey,
0L,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&key,
&flag);
CLEANUP_WS(0, "Error: RegCreateKeyEx. (LINKAGE)", WStatus, CLEANUP2);
//
// Create the Export Value (for instances of Objects) under Linkage
// Its set to NULL when the ntfrs applicationis started and the
// Instances are added as they get created by the application
//
WStatus = RegSetValueEx (key, L"Export", 0L, REG_MULTI_SZ, NULL, 0);
CLEANUP_WS(0, "Error: RegSetValueEx Export.", WStatus, CLEANUP);
CLEANUP:
FRS_REG_CLOSE(key);
CLEANUP2:
//
// If the Initialization was successful, return ERROR_SUCCESS
//
return WStatus;
}
ULONG
AddPerfmonInstance (
IN DWORD ObjectType,
IN PVOID addr,
IN PWCHAR InstanceName
)
/*++
Routine Description:
This routine is called by the ntfrs application to add an Instance of
a particular Object Type to the Registry and the Hash Table.
Arguments:
ObjectType - The Object whose instance has to be added
addr - The data structure for the Instance Counters stored in Hash Table
InstanceName - The instance name of the object.
Return Value:
ERROR_SUCCESS - The Initialization of the Object was successful OR
Appropriate DWORD value for the Error status
--*/
{
#undef DEBSUB
#define DEBSUB "AddPerfmonInstance:"
BOOL flag = TRUE;
BOOL HaveKey = FALSE;
ULONG WStatus = ERROR_SUCCESS;
ULONG WStatus1;
ULONG GStatus;
DWORD Type, size, len, totallen;
HKEY key = INVALID_HANDLE_VALUE;
PWCHAR SubKey, p, r, s;
PWCHAR NewExport = NULL;
PWCHAR ValueData = NULL;
PHT_REPLICA_SET_DATA rsdata;
PHT_REPLICA_CONN_DATA rcdata;
PQHASH_TABLE HashTable;
PULONGLONG QKey;
PULONGLONG QData;
PPERFMON_OBJECT_ID PmOid;
//
// Addition must be mutually exclusive
// Check is its safe to enter before going ahead
//
if (!HANDLE_IS_VALID(PerfmonProcessSemaphore)) {
return ERROR_SUCCESS;
}
//
// Alloc the perfmon object ID struct and the space for the instance name.
//
PmOid = (PPERFMON_OBJECT_ID) FrsAlloc (sizeof(PERFMON_OBJECT_ID));
PmOid->name = FrsAlloc((wcslen(InstanceName)+1) * sizeof(WCHAR));
wcscpy(PmOid->name, InstanceName);
AcquirePerfmonLock;
//
// set up params based on object type. Alloc storage for OID and name.
//
if ( ObjectType == REPLICASET ) {
//
// L"SYSTEM\\CurrentControlSet\\Services\\FileReplicaSet\\Linkage"
//
SubKey = REPSETLINSUBKEY;
rsdata = (PHT_REPLICA_SET_DATA) addr;
if (rsdata->oid != NULL) {
WStatus = ERROR_INVALID_PARAMETER;
goto CLEANUP;
}
rsdata->oid = PmOid;
HashTable = HTReplicaSet;
QKey = &(rsdata->oid->key);
QData = (PULONGLONG)&(rsdata);
} else {
//
// L"SYSTEM\\CurrentControlSet\\Services\\FileReplicaConn\\Linkage"
//
SubKey = REPCONNLINSUBKEY;
rcdata = (PHT_REPLICA_CONN_DATA) addr;
if (rcdata->oid != NULL) {
WStatus = ERROR_INVALID_PARAMETER;
goto CLEANUP;
}
rcdata->oid = PmOid;
HashTable = HTReplicaConn;
QKey = &(rcdata->oid->key);
QData = (PULONGLONG)&(rcdata);
}
//
// Open the Linkge Key of the Objevt which contains the Export Value
//
WStatus = RegOpenKeyEx (HKEY_LOCAL_MACHINE, SubKey, 0L, KEY_ALL_ACCESS, &key);
CLEANUP1_WS(0, "Error: RegOpenKeyEx (%ws).", SubKey, WStatus, CLEANUP);
HaveKey = TRUE;
//
// Fetch the Export value
//
WStatus = RegQueryValueEx(key, L"Export", NULL, &Type, NULL, &size);
CLEANUP_WS(0, "RegQueryValueEx(Export);", WStatus, CLEANUP);
if (Type != REG_MULTI_SZ) {
DPRINT2(0, "RegQueryValueEx(Export) is Type %d; not Type %d\n",
Type, REG_MULTI_SZ);
WStatus = ERROR_NO_TOKEN;
goto CLEANUP;
}
//
// Need to check if size == 0 as FrsAlloc asserts if called with 0 as the
// first parameter (prefix fix).
//
ValueData = (size == 0) ? NULL : (PWCHAR) FrsAlloc (size);
WStatus = RegQueryValueEx(key, L"Export", NULL, &Type, (PUCHAR)ValueData, &size);
CLEANUP_WS(0, "RegQueryValueEx(Export);", WStatus, CLEANUP);
if (Type != REG_MULTI_SZ) {
DPRINT2(0, "RegQueryValueEx(Export) is Type %d; not Type %d\n",
Type, REG_MULTI_SZ);
WStatus = ERROR_NO_TOKEN;
goto CLEANUP;
}
DPRINT1(4, "Export was = %ws\n", ValueData);
if (size > sizeof(WCHAR)) {
len = (1 + wcslen(InstanceName)) * sizeof(WCHAR);
totallen = size + len;
p = (PWCHAR) FrsAlloc (totallen);
NewExport = p;
r = ValueData;
while (TRUE) {
if ( (wcscmp(r, InstanceName)) == 0 ) {
//
// The Instance Value already exists
//
flag = FALSE;
break;
}
wcscpy(p, r);
p = wcschr(p, L'\0');
r = wcschr(r, L'\0');
p = p + 1;
r = r + 1;
if ( *r == L'\0') {
break;
}
}
if (flag) {
wcscpy(p, InstanceName);
p = wcschr(p, L'\0');
*(p+1) = L'\0';
//
// If its a new Instance add it to the hash table
//
if ( ObjectType == REPLICASET ) {
//
// Set the ID of the Instance and increment for next.
//
rsdata->oid->key = FRS_UniqueID;
FRS_UniqueID++;
} else {
//
// Set the ID of the Instance and increment for next.
//
rcdata->oid->key = FRC_UniqueID;
FRC_UniqueID++;
}
} else {
//
// This Instance already exists, so make no changes to the Export value
//
WStatus = ERROR_ALREADY_EXISTS;
goto CLEANUP;
}
} else {
//
// This is the only Instance of the Object
//
len = (2 + wcslen(InstanceName)) * sizeof(WCHAR);
NewExport = (PWCHAR) FrsAlloc (len);
wcscpy(NewExport, InstanceName);
p = wcschr(NewExport, L'\0');
*(p+1) = L'\0';
totallen = len;
if ( ObjectType == REPLICASET ) {
rsdata->oid->key = FRS_UniqueID;
FRS_UniqueID++;
} else {
rcdata->oid->key = FRC_UniqueID;
FRC_UniqueID++;
}
}
DPRINT4(4, "PERFMON: Type: %d, Key: %08x %08x, QData: %08x %08x, Name: %ws\n",
ObjectType, PRINTQUAD(*QKey), PRINTQUAD(*QData), InstanceName);
GStatus = QHashInsert(HashTable, QKey, QData, 0, FALSE);
if (GStatus != GHT_STATUS_SUCCESS) {
DPRINT(0, "Error: QHashInsert Failed\n");
WStatus = ERROR_ALREADY_EXISTS;
goto CLEANUP;
}
//
// Set the Export Value with the added instance (if any)
//
WStatus = RegSetValueEx (key, L"Export", 0L, REG_MULTI_SZ, (BYTE *)NewExport, totallen);
CLEANUP_WS(0, "Error: RegSetValueEx (Export).", WStatus, CLEANUP);
WStatus = ERROR_SUCCESS;
CLEANUP:
if (!WIN_SUCCESS(WStatus)) {
DPRINT1_WS(0, "ERROR: Add instance failed for %ws :", InstanceName, WStatus);
//
// Failed to add the instance. Free the OID and name.
//
FrsFree(PmOid->name);
if ( ObjectType == REPLICASET ) {
rsdata->oid = FrsFree (PmOid);
} else {
rcdata->oid = FrsFree (PmOid);
}
}
//
// Free the malloced memory
//
ValueData = FrsFree (ValueData);
NewExport = FrsFree (NewExport);
if (HaveKey) {
FRS_REG_CLOSE(key);
}
//
// Its safe to leave the critical section now
//
ReleasePerfmonLock;
return WStatus;
}
DWORD
DeletePerfmonInstance(
IN DWORD ObjectType,
IN PVOID addr
)
/*++
Routine Description:
This routine is called by the ntfrs application to delete an Instance of a particular
Object Type from the Registry and the Hash Table. It is very similar to the adding
function described above.
Arguments:
ObjectType - The Object whose instance has to be added
addr - The data structure for the Instance Counters stored in Hash Table
Return Value:
ERROR_SUCCESS - The Initialization of the Object was successful OR
Appropriate DWORD value for the Error status
--*/
{
#undef DEBSUB
#define DEBSUB "DeletePerfmonInstance:"
ULONGLONG QKey;
ULONG WStatus = ERROR_SUCCESS;
ULONG WStatus1;
PQHASH_TABLE HashTable;
ULONG GStatus;
DWORD Type, size, len, TotalLen;
HKEY key = INVALID_HANDLE_VALUE;
PWCHAR SubKey, p, r, s, InstanceName;
PWCHAR ValueData = NULL;
PWCHAR q = NULL;
PHT_REPLICA_SET_DATA rsdata;
PHT_REPLICA_CONN_DATA rcdata;
if (addr == NULL) {
return ERROR_SUCCESS;
}
//
// Deletion must be mutually exclusive
// Check is its safe to enter before going ahead
//
if (!HANDLE_IS_VALID(PerfmonProcessSemaphore)) {
return ERROR_SUCCESS;
}
if ( ObjectType == REPLICASET ) {
//
// Replica Set Object
//
SubKey = REPSETLINSUBKEY;
rsdata = (HT_REPLICA_SET_DATA *) addr;
if ((rsdata->oid == NULL) || (rsdata->oid->name == NULL)) {
return ERROR_SUCCESS;
}
InstanceName = rsdata->oid->name;
HashTable = HTReplicaSet;
QKey = rsdata->oid->key;
DPRINT1(4, "Replica Free - %ws\n", InstanceName);
} else {
//
// Replica Connection Object.
//
SubKey = REPCONNLINSUBKEY;
rcdata = (HT_REPLICA_CONN_DATA *) addr;
if ((rcdata->oid == NULL) || (rcdata->oid->name == NULL)) {
return ERROR_SUCCESS;
}
InstanceName = rcdata->oid->name;
HashTable = HTReplicaConn;
QKey = rcdata->oid->key;
DPRINT1(4, "Cxtion Free - %ws\n", InstanceName);
}
AcquirePerfmonLock;
//
// Pull the Instance key from the hash table.
//
DPRINT1(4, "QKey: %08x %08x\n", PRINTQUAD(QKey));
if (QKey != QUADZERO ) {
GStatus = QHashDelete(HashTable, &QKey);
if (GStatus != GHT_STATUS_SUCCESS) {
DPRINT1(0, "Error: QHashDelete. GStatus = %d\n", GStatus);
}
}
WStatus = RegOpenKeyEx (HKEY_LOCAL_MACHINE, SubKey, 0L, KEY_ALL_ACCESS, &key);
CLEANUP1_WS(0, "RegOpenKeyEx(%ws);", SubKey, WStatus, CLEANUP_UNLOCK);
//
// Fetch the Export value
//
WStatus = RegQueryValueEx(key, L"Export", NULL, &Type, NULL, &size);
CLEANUP_WS(0, "RegQueryValueEx(Export);", WStatus, CLEANUP);
if (Type != REG_MULTI_SZ) {
DPRINT2(0, "RegQueryValueEx(Export) is Type %d; not Type %d\n",
Type, REG_MULTI_SZ);
WStatus = ERROR_NO_TOKEN;
goto CLEANUP;
}
len = (1 + wcslen(InstanceName)) * sizeof(WCHAR);
if (size < len) {
WStatus = ERROR_INVALID_PARAMETER;
goto CLEANUP;
}
TotalLen = (size - len);
//
// Need to check if size == 0 as FrsAlloc asserts if called with 0 as the first parameter (prefix fix).
//
ValueData = (size == 0) ? NULL : (PWCHAR) FrsAlloc (size);
WStatus = RegQueryValueEx(key, L"Export", NULL, &Type, (PUCHAR)ValueData, &size);
CLEANUP_WS(0, "RegQueryValueEx(Export);", WStatus, CLEANUP);
if (Type != REG_MULTI_SZ) {
DPRINT2(0, "RegQueryValueEx(Export) is Type %d; not Type %d\n",
Type, REG_MULTI_SZ);
WStatus = ERROR_NO_TOKEN;
goto CLEANUP;
}
DPRINT1(4, "Export was = %ws\n", ValueData);
// Note: Perf: fix the below to do an inplace delete of the instance strimg.
//
// For REG_MULTI_SZ there are two UNICODE_NULLs at the end, one is accounted
// for above.
//
if (TotalLen > sizeof(WCHAR)) {
p = (PWCHAR) FrsAlloc (TotalLen);
q = p;
r = ValueData;
while (TRUE) {
if ( (wcscmp(r, InstanceName)) == 0) {
r = wcschr(r, L'\0');
r = r + 1;
if (*r == L'\0')
break;
else
continue;
}
wcscpy(p, r);
p = wcschr(p, L'\0');
r = wcschr(r, L'\0');
p = p + 1;
r = r + 1;
if (*r == L'\0') {
*p = L'\0';
break;
}
}
}
else {
q = NULL;
TotalLen = 0;
}
DPRINT1(4, "Export now = %ws\n", q);
//
// Set the Export Value to the Updated Instance List
//
WStatus = RegSetValueEx (key, L"Export", 0L, REG_MULTI_SZ, (BYTE *)q, TotalLen);
CLEANUP_WS(0, "RegSetValueEx(Export);", WStatus, CLEANUP);
CLEANUP:
//
// Free up the malloced memory
//
FrsFree (ValueData);
FrsFree (q);
FRS_REG_CLOSE(key);
//
// Free the name and oid struct so this func is not called again when the
// replica set or connection struct is finally freed.
//
if ( ObjectType == REPLICASET ) {
rsdata->oid->name = FrsFree(rsdata->oid->name);
rsdata->oid = FrsFree(rsdata->oid);
} else {
rcdata->oid->name = FrsFree(rcdata->oid->name);
rcdata->oid = FrsFree(rcdata->oid);
}
CLEANUP_UNLOCK:
//
// Its safe to leave the critical section now
//
ReleasePerfmonLock;
return WStatus;
}
ULONGLONG
PmFindTheKeyValue (
IN PContextData Context
)
/*++
Routine Description:
This routine is called by the RPC server function GetIndicesOfInstancesFromServer, to
get the index value of an Instance.
Arguments:
Context - The structure containing the name of the Instance whose Key
value has to be determined.
Return Value:
The Key for the Instance or INVALIDKEY if the Instance was not
found in the Hash Table
--*/
{
#undef DEBSUB
#define DEBSUB "PmFindTheKeyValue:"
ULONGLONG QKeyValue = (ULONGLONG)INVALIDKEY;
DWORD ret;
PQHASH_TABLE HashTable;
if (!HANDLE_IS_VALID(PerfmonProcessSemaphore)) {
return (ULONGLONG)INVALIDKEY;
}
HashTable = (Context->OBJType == REPLICASET) ? HTReplicaSet : HTReplicaConn;
try {
//
// Deletion must be mutually exclusive
// Check is its safe to enter before going ahead
//
AcquirePerfmonLock;
//
// Enumerate through the Hash Table and if a matching Instance
// name is found, return its Key value
//
ret = QHashEnumerateTable(HashTable, PmSearchTable, Context);
if ( ret == FrsErrorFoundKey) {
QKeyValue = Context->KeyValue;
}
} finally {
ReleasePerfmonLock;
}
return QKeyValue;
}
//
// The function is implemented in frsrpc.c
//
DWORD
FrsRpcAccessChecks(
IN HANDLE ServerHandle,
IN DWORD RpcApiIndex
);
DWORD
GetIndicesOfInstancesFromServer (
IN handle_t Handle,
IN OUT OpenRpcData *packt
)
/*++
Routine Description:
This is an RPC server routine that is called by the client (Performance DLL
for the FileReplicaSet and FileRepicaConn Objects of PERFMON) to set the
indices for Instance names
Arguments:
Handle - The RPC binding handle
packt - The structure (sent by the client) containing the Instance Names
whose indices have to be set and passed back to the client
Return Value:
none
--*/
{
#undef DEBSUB
#define DEBSUB "GetIndicesOfInstancesFromServer:"
LONG i;
ContextData context;
ULONG WStatus;
LONG NumInstanceNames;
WStatus = FrsRpcAccessChecks(Handle, ACX_COLLECT_PERFMON_DATA);
CLEANUP_WS(4, "Collect Perfmon Data Access check failed.", WStatus, CLEANUP);
//
// Its possible that the RPC end points of PERFMON get initialized before
// the InitializePerfmonServer gets called. If this RPC call has been made
// before initialization, return error so that the Open function gets called
// again by the perflib dll.
//
if (PMTotalInst == NULL) {
WStatus = ERROR_INVALID_DATA;
}
CLEANUP_WS(4, "Perfmon server uninitialized. Can't return data.", WStatus, CLEANUP);
try {
if ((packt == NULL) || (packt->ver == NULL)) {
DPRINT(4, "PERFMON: ERROR_INVALID_PARAMETER\n");
return ERROR_INVALID_PARAMETER;
}
//
// Set the version to zero(its unused)
//
*(packt->ver) = 0;
//
// Set the appropriate Object Type
//
if (packt->ObjectType == REPSET) {
context.OBJType = REPLICASET;
}
else
if (packt->ObjectType == REPCONN) {
context.OBJType = REPLICACONN;
} else {
DPRINT(4, "PERFMON: ERROR_INVALID_PARAMETER\n");
return ERROR_INVALID_PARAMETER;
}
//
// Check valid parameters.
//
if ((packt->instnames == NULL) ||
(packt->indices == NULL) ||
(packt->numofinst > packt->instnames->size) ||
(packt->numofinst > packt->indices->size)) {
DPRINT(4, "PERFMON: ERROR_INVALID_PARAMETER\n");
return ERROR_INVALID_PARAMETER;
}
NumInstanceNames = packt->numofinst;
for (i = 0; i < NumInstanceNames; i++) {
context.name = packt->instnames->InstanceNames[i].name;
if ((context.name == NULL) ||
(wcslen(context.name) > PERFMON_MAX_INSTANCE_LENGTH)) {
DPRINT(4, "PERFMON: ERROR_INVALID_PARAMETER\n");
return ERROR_INVALID_PARAMETER;
}
DPRINT2(4, "The instance name of instance %d is %ws\n", i+1, context.name);
//
// Set the Index for the Instance name
//
packt->indices->index[i] = (DWORD) PmFindTheKeyValue (&context);
DPRINT2(4, "The instance index of instance %ws is %d\n",
context.name, packt->indices->index[i]);
}
} except (EXCEPTION_EXECUTE_HANDLER) {
//
// Exception
//
GET_EXCEPTION_CODE(WStatus);
}
CLEANUP:
return WStatus;
}
DWORD
GetCounterDataOfInstancesFromServer(
IN handle_t Handle,
IN OUT CollectRpcData *packg
)
/*++
Routine Description:
This is an RPC server routine that is called by the client (Performance DLL
for the FileReplicaSet and FileRepicaConn Objects of PERFMON) to collect
data for the Instance counters.
Arguments:
Handle - The RPC binding handle
packg - The structure (sent by the client) containing the indices of
instances whose counters data has to be sent back to the client.
Return Value:
none
--*/
{
#undef DEBSUB
#define DEBSUB "GetCounterDataOfInstancesFromServer:"
ULONGLONG InstanceId;
ULONGLONG CData;
ULONG WStatus = ERROR_SUCCESS;
LONG i, j;
DWORD GStatus;
ULONG_PTR Flags;
BOOL FirstPass;
PBYTE vd;
PHT_REPLICA_SET_DATA rsdat;
PHT_REPLICA_CONN_DATA rcdat;
ULONG COffset, CSize, DataSize;
LONG NumInstances;
PQHASH_TABLE HashTable;
PWCHAR OurName;
PReplicaSetCounters Total, Rsi;
//
// Make security check on callers access to perfmon data.
//
WStatus = FrsRpcAccessChecks(Handle, ACX_COLLECT_PERFMON_DATA);
CLEANUP_WS(4, "Collect Perfmon Data Access check failed.", WStatus, CLEANUP);
//
// Its possible that the RPC end points of PERFMON get initialized before
// the InitializePerfmonServer gets called. This is possible if the service
// is stopped and restarted and PERFMON continued to run in between.
//
if (PMTotalInst == NULL) {
WStatus = ERROR_INVALID_DATA;
}
CLEANUP_WS(4, "Perfmon server uninitialized. Can't return data.", WStatus, CLEANUP);
try {
if (packg == NULL) {
DPRINT(4, "PERFMON: ERROR_INVALID_PARAMETER\n");
return ERROR_INVALID_PARAMETER;
}
//
// Set the appropriate Object Type
//
if (packg->ObjectType == REPSET) {
DataSize = SIZEOF_REPSET_COUNTER_DATA;
HashTable = HTReplicaSet;
} else
if (packg->ObjectType == REPCONN) {
DataSize = SIZEOF_REPCONN_COUNTER_DATA;
HashTable = HTReplicaConn;
} else {
DPRINT(4, "PERFMON: ERROR_INVALID_PARAMETER\n");
return ERROR_INVALID_PARAMETER;
}
//
// Check valid parameters.
//
if ((packg->databuff == NULL) ||
(packg->indices == NULL) ||
(packg->databuff->data == NULL) ||
(packg->numofinst > packg->indices->size)) {
DPRINT(4, "PERFMON: ERROR_INVALID_PARAMETER\n");
return ERROR_INVALID_PARAMETER;
}
NumInstances = packg->numofinst;
//
// Set vd to the memory where counter data is to be filled
//
vd = packg->databuff->data;
DPRINT1(5, "PERFMON: packg->ObjectType: %d\n", packg->ObjectType);
DPRINT1(5, "PERFMON: packg->numofinst: %d\n", packg->numofinst);
DPRINT1(5, "PERFMON: packg->databuff->data: %d\n", vd);
DPRINT1(5, "PERFMON: packg->databuff->size: %d\n", packg->databuff->size);
if (packg->ObjectType == REPSET) {
//
// First accumulate the totals for the replica set object
//
FirstPass = TRUE;
Total = &PMTotalInst->FRSCounter;
for (i = 0; i < NumInstances; i++) {
if (packg->indices->index[i] == INVALIDKEY) {
//
// If the key is INVALID, data is zeros
//
DPRINT(5, "PERFMON: Invalid Key sent.\n");
continue;
}
//
// set the value of index to quadword InstanceId
//
InstanceId = (ULONGLONG)packg->indices->index[i];
//
// Lookup for the counter data for the index value of the Instance
//
GStatus = QHashLookup(HTReplicaSet, &InstanceId, &CData, &Flags);
if (GStatus != GHT_STATUS_SUCCESS) {
DPRINT(5, "PERFMON: Key not found.\n");
continue;
}
rsdat = (PHT_REPLICA_SET_DATA)(CData);
//
// Skip the total instance.
//
if (wcscmp(rsdat->oid->name, TOTAL_NAME) == 0) {
continue;
}
Rsi = &rsdat->FRSCounter;
//
// Accumulate the counters for this instance into the total.
//
for (j = 0; j < FRS_NUMOFCOUNTERS; j++) {
//
// If a count is Service Wide then leave the Total alone.
//
if (BooleanFlagOn(RepSetInitData[j].Flags, PM_RS_FLAG_SVC_WIDE)) {
continue;
}
COffset = RepSetInitData[j].offset;
CSize = RepSetInitData[j].size;
if (CSize == sizeof(ULONG)) {
if (FirstPass) {
*(PULONG)((PCHAR) Total + COffset) = (ULONG) 0;
}
*(PULONG)((PCHAR) Total + COffset) +=
*(PULONG)((PCHAR) Rsi + COffset);
} else
if (CSize == sizeof(ULONGLONG)) {
if (FirstPass) {
*(PULONGLONG)((PCHAR) Total + COffset) = QUADZERO;
}
*(PULONGLONG)((PCHAR) Total + COffset) +=
*(PULONGLONG)((PCHAR) Rsi + COffset);
}
}
FirstPass = FALSE;
}
}
//
// Check if the buffer is large enough to send all the
// requested data.
//
if (packg->databuff->size < (LONG)(NumInstances*DataSize)) {
DPRINT(4, "PERFMON: ERROR_INVALID_PARAMETER\n");
return ERROR_INVALID_PARAMETER;
}
//
// Now return the data to Perfmon.
//
for (i = 0; i < NumInstances; i++) {
//
// The amount of data returned should not exceed the buffer size
//
if ((vd - packg->databuff->data) > packg->databuff->size) {
DPRINT(4, "PERFMON: ERROR_INVALID_PARAMETER\n");
return ERROR_INVALID_PARAMETER;
}
if (packg->indices->index[i] == INVALIDKEY) {
//
// If the key is INVALID, data is zeros
//
DPRINT(5, "PERFMON: Invalid Key sent.\n");
ZeroMemory (vd, DataSize);
vd += DataSize;
continue;
}
//
// set the value of index to quadword InstanceId
//
InstanceId = (ULONGLONG)packg->indices->index[i];
//
// Lookup for the counter data for the index value of the Instance
//
GStatus = QHashLookup(HashTable, &InstanceId, &CData, &Flags);
if ( GStatus == GHT_STATUS_SUCCESS) {
if (packg->ObjectType == REPSET) {
//
// Return data for replica set
//
rsdat = (PHT_REPLICA_SET_DATA)(CData);
OurName = rsdat->oid->name;
//
// Set all the Change Order counters which are the sum of the
// ones already set.
//
PmSetTheCOCounters(rsdat);
CopyMemory (vd, &(rsdat->FRSCounter), DataSize);
} else {
//
// Return data for replica connection
//
rcdat = (PHT_REPLICA_CONN_DATA)(CData);
OurName = rcdat->oid->name;
CopyMemory (vd, &(rcdat->FRCCounter), DataSize);
}
DPRINT2(5, "%ws is the name associated with index %d\n",
OurName, packg->indices->index[i]);
} else {
//
// Instance not found, return zeros for counter data
//
DPRINT1(0, "The instance not found for index %d\n",
packg->indices->index[i]);
ZeroMemory (vd, DataSize);
}
//
// Increment vd by SIZEOF_REPSET_COUNTER_DATA
//
vd += DataSize;
}
} except (EXCEPTION_EXECUTE_HANDLER) {
//
// Exception
//
GET_EXCEPTION_CODE(WStatus);
}
CLEANUP:
return WStatus;
}
VOID
PmSetTheCOCounters(
PHT_REPLICA_SET_DATA RSData
)
/*++
Routine Description:
This routine sets the Change Order countters which are the sums
of the counters already set in the ntfrs application
Arguments:
RSData - Pointer to the HT_REPLICA_SET_DATA structure whose counters
need to be set
Return Value:
none
--*/
{
#undef DEBSUB
#define DEBSUB "PmSetTheCOCounters:"
//
// Set the Local and Remote Retried Counter Values
//
RSData->FRSCounter.LCORetried = RSData->FRSCounter.LCORetriedGen +
RSData->FRSCounter.LCORetriedFet +
RSData->FRSCounter.LCORetriedIns +
RSData->FRSCounter.LCORetriedRen;
RSData->FRSCounter.RCORetried = RSData->FRSCounter.RCORetriedGen +
RSData->FRSCounter.RCORetriedFet +
RSData->FRSCounter.RCORetriedIns +
RSData->FRSCounter.RCORetriedRen;
//
// Set all the CO counter values
//
RSData->FRSCounter.COIssued = RSData->FRSCounter.LCOIssued +
RSData->FRSCounter.RCOIssued;
RSData->FRSCounter.CORetired = RSData->FRSCounter.LCORetired +
RSData->FRSCounter.RCORetired;
RSData->FRSCounter.COAborted = RSData->FRSCounter.LCOAborted +
RSData->FRSCounter.RCOAborted;
RSData->FRSCounter.CORetried = RSData->FRSCounter.LCORetried +
RSData->FRSCounter.RCORetried;
RSData->FRSCounter.CORetriedGen = RSData->FRSCounter.LCORetriedGen +
RSData->FRSCounter.RCORetriedGen;
RSData->FRSCounter.CORetriedFet = RSData->FRSCounter.LCORetriedFet +
RSData->FRSCounter.RCORetriedFet;
RSData->FRSCounter.CORetriedIns = RSData->FRSCounter.LCORetriedIns +
RSData->FRSCounter.RCORetriedIns;
RSData->FRSCounter.CORetriedRen = RSData->FRSCounter.LCORetriedRen +
RSData->FRSCounter.RCORetriedRen;
RSData->FRSCounter.COMorphed = RSData->FRSCounter.LCOMorphed +
RSData->FRSCounter.RCOMorphed;
RSData->FRSCounter.COPropagated = RSData->FRSCounter.LCOPropagated +
RSData->FRSCounter.RCOPropagated;
RSData->FRSCounter.COReceived = RSData->FRSCounter.RCOReceived;
RSData->FRSCounter.COSent = RSData->FRSCounter.LCOSent +
RSData->FRSCounter.RCOSent;
}