windows-cm-performance-tuning-200803007
authorJeffrey Altman <jaltman@secure-endpoints.com>
Fri, 7 Mar 2008 22:22:21 +0000 (22:22 +0000)
committerJeffrey Altman <jaltman@secure-endpoints.com>
Fri, 7 Mar 2008 22:22:21 +0000 (22:22 +0000)
LICENSE MIT

This is a first cut at a cache manager statistics monitor
that can be used to determine the necessary cache parameters
to support the working set.

Off by default the performance package can be activated
by setting "daemonPerformanceTuningInterval" in the service
Parameters key.  As with the other daemon interval values
the unit is in seconds.

At service start and each succeeding interval the cache
manager will write statistics to %TEMP%\afsd_performance.txt
showing the relative usage of cm_scache_t, cm_volume_t and
cm_buf_t objects.  The FID statistics keep track of all FIDs
seen by the cache manager during the service session whether
or not they are backed by any live object in the cache.

These statistics are not stored in the cache file.

src/WINNT/afsd/NTMakefile
src/WINNT/afsd/afsd.h
src/WINNT/afsd/cm_daemon.c
src/WINNT/afsd/cm_performance.c [new file with mode: 0644]
src/WINNT/afsd/cm_performance.h [new file with mode: 0644]

index 9aab931..bceb855 100644 (file)
@@ -63,6 +63,7 @@ INCFILES =\
        $(INCFILEDIR)\cm_buf.h \
        $(INCFILEDIR)\cm_freelance.h \
         $(INCFILEDIR)\cm_memmap.h \
+       $(INCFILEDIR)\cm_performance.h \
         $(INCFILEDIR)\afsd_eventlog.h \
         $(INCFILEDIR)\afsd_eventmessages.h \
         $(INCFILEDIR)\afskfw.h \
@@ -124,6 +125,7 @@ AFSDOBJS=\
        $(OUT)\cm_dnlc.obj \
        $(OUT)\cm_rpc.obj \
         $(OUT)\cm_memmap.obj \
+        $(OUT)\cm_performance.obj \
        $(OUT)\afsrpc_s.obj \
 !IFDEF OSICRASH
        $(OUT)\afsdcrash.obj \
index 27d45ed..ceeab6e 100644 (file)
@@ -53,6 +53,7 @@ BOOL APIENTRY About(HWND, unsigned int, unsigned int, long);
 #include "cm_buf.h"
 #include "cm_memmap.h"
 #include "cm_freelance.h"
+#include "cm_performance.h"
 #include "smb_ioctl.h"
 #include "afsd_init.h"
 #include "afsd_eventlog.h"
index 9ef65d4..ccd34a3 100644 (file)
@@ -34,6 +34,7 @@ long cm_daemonCheckVolCBInterval = 0;
 long cm_daemonCheckLockInterval  = 60;
 long cm_daemonTokenCheckInterval = 180;
 long cm_daemonCheckOfflineVolInterval = 600;
+long cm_daemonPerformanceTuningInterval = 0;
 
 osi_rwlock_t cm_daemonLock;
 
@@ -273,60 +274,70 @@ cm_DaemonCheckInit(void)
     dummyLen = sizeof(DWORD);
     code = RegQueryValueEx(parmKey, "daemonCheckDownInterval", NULL, NULL,
                            (BYTE *) &dummy, &dummyLen);
-    if (code == ERROR_SUCCESS)
+    if (code == ERROR_SUCCESS && dummy)
        cm_daemonCheckDownInterval = dummy;
     afsi_log("daemonCheckDownInterval is %d", cm_daemonCheckDownInterval);
 
     dummyLen = sizeof(DWORD);
     code = RegQueryValueEx(parmKey, "daemonCheckUpInterval", NULL, NULL,
                            (BYTE *) &dummy, &dummyLen);
-    if (code == ERROR_SUCCESS)
+    if (code == ERROR_SUCCESS && dummy)
        cm_daemonCheckUpInterval = dummy;
     afsi_log("daemonCheckUpInterval is %d", cm_daemonCheckUpInterval);
 
     dummyLen = sizeof(DWORD);
     code = RegQueryValueEx(parmKey, "daemonCheckVolInterval", NULL, NULL,
                            (BYTE *) &dummy, &dummyLen);
-    if (code == ERROR_SUCCESS)
+    if (code == ERROR_SUCCESS && dummy)
        cm_daemonCheckVolInterval = dummy;
     afsi_log("daemonCheckVolInterval is %d", cm_daemonCheckVolInterval);
 
     dummyLen = sizeof(DWORD);
     code = RegQueryValueEx(parmKey, "daemonCheckCBInterval", NULL, NULL,
                            (BYTE *) &dummy, &dummyLen);
-    if (code == ERROR_SUCCESS)
+    if (code == ERROR_SUCCESS && dummy)
        cm_daemonCheckCBInterval = dummy;
     afsi_log("daemonCheckCBInterval is %d", cm_daemonCheckCBInterval);
 
     dummyLen = sizeof(DWORD);
     code = RegQueryValueEx(parmKey, "daemonCheckVolCBInterval", NULL, NULL,
                            (BYTE *) &dummy, &dummyLen);
-    if (code == ERROR_SUCCESS)
+    if (code == ERROR_SUCCESS && dummy)
        cm_daemonCheckVolCBInterval = dummy;
     afsi_log("daemonCheckVolCBInterval is %d", cm_daemonCheckVolCBInterval);
 
     dummyLen = sizeof(DWORD);
     code = RegQueryValueEx(parmKey, "daemonCheckLockInterval", NULL, NULL,
                            (BYTE *) &dummy, &dummyLen);
-    if (code == ERROR_SUCCESS)
+    if (code == ERROR_SUCCESS && dummy)
        cm_daemonCheckLockInterval = dummy;
     afsi_log("daemonCheckLockInterval is %d", cm_daemonCheckLockInterval);
 
     dummyLen = sizeof(DWORD);
     code = RegQueryValueEx(parmKey, "daemonCheckTokenInterval", NULL, NULL,
                            (BYTE *) &dummy, &dummyLen);
-    if (code == ERROR_SUCCESS)
+    if (code == ERROR_SUCCESS && dummy)
        cm_daemonTokenCheckInterval = dummy;
     afsi_log("daemonCheckTokenInterval is %d", cm_daemonTokenCheckInterval);
 
     dummyLen = sizeof(DWORD);
     code = RegQueryValueEx(parmKey, "daemonCheckOfflineVolInterval", NULL, NULL,
                            (BYTE *) &dummy, &dummyLen);
-    if (code == ERROR_SUCCESS)
+    if (code == ERROR_SUCCESS && dummy)
        cm_daemonCheckOfflineVolInterval = dummy;
     afsi_log("daemonCheckOfflineVolInterval is %d", cm_daemonCheckOfflineVolInterval);
     
+    dummyLen = sizeof(DWORD);
+    code = RegQueryValueEx(parmKey, "daemonPerformanceTuningInterval", NULL, NULL,
+                           (BYTE *) &dummy, &dummyLen);
+    if (code == ERROR_SUCCESS)
+       cm_daemonPerformanceTuningInterval = dummy;
+    afsi_log("daemonPerformanceTuningInterval is %d", cm_daemonPerformanceTuningInterval);
+    
     RegCloseKey(parmKey);
+
+    if (cm_daemonPerformanceTuningInterval)
+        cm_PerformanceTuningInit();
 }
 
 /* periodic check daemon */
@@ -341,6 +352,7 @@ void cm_Daemon(long parm)
     time_t lastUpServerCheck;
     time_t lastTokenCacheCheck;
     time_t lastBusyVolCheck;
+    time_t lastPerformanceCheck;
     char thostName[200];
     unsigned long code;
     struct hostent *thp;
@@ -389,6 +401,8 @@ void cm_Daemon(long parm)
     lastUpServerCheck = now - cm_daemonCheckUpInterval/2 + (rand() % cm_daemonCheckUpInterval);
     lastTokenCacheCheck = now - cm_daemonTokenCheckInterval/2 + (rand() % cm_daemonTokenCheckInterval);
     lastBusyVolCheck = now - cm_daemonCheckOfflineVolInterval/2 * (rand() % cm_daemonCheckOfflineVolInterval);
+    if (cm_daemonPerformanceTuningInterval)
+        lastPerformanceCheck = now - cm_daemonPerformanceTuningInterval/2 * (rand() % cm_daemonPerformanceTuningInterval);
 
     while (daemon_ShutdownFlag == 0) {
        /* check to see if the listener threads halted due to network 
@@ -539,7 +553,18 @@ void cm_Daemon(long parm)
         if (daemon_ShutdownFlag == 1) {
             break;
         }
-       thrd_Sleep(10000);              /* sleep 10 seconds */
+
+        if (cm_daemonPerformanceTuningInterval &&
+            now > lastPerformanceCheck + cm_daemonPerformanceTuningInterval &&
+             daemon_ShutdownFlag == 0) {
+            lastPerformanceCheck = now;
+            cm_PerformanceTuningCheck();
+            if (daemon_ShutdownFlag == 1)
+                break;
+           now = osi_Time();
+        }
+        
+        thrd_Sleep(10000);             /* sleep 10 seconds */
     }
     thrd_SetEvent(cm_Daemon_ShutdownEvent);
 }       
diff --git a/src/WINNT/afsd/cm_performance.c b/src/WINNT/afsd/cm_performance.c
new file mode 100644 (file)
index 0000000..f3f4e87
--- /dev/null
@@ -0,0 +1,542 @@
+/* 
+ *  Copyright (c) 2008 - Secure Endpoints Inc.
+ */
+
+#include <afs/param.h>
+#include <afs/stds.h>
+
+#include <windows.h>
+#include <winsock2.h>
+#include <iphlpapi.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <string.h>
+
+#include "afsd.h"
+
+static cm_fid_stats_t ** fidStatsHashTablep = NULL;
+static afs_uint32        fidStatsHashTableSize = 0;
+
+/*
+ * algorithm and implementation adapted from code written by
+ * Frank Pilhofer <fp@fpx.de> 
+ */
+afs_uint32 nearest_prime(afs_uint32 s)
+{
+#define TEST(f,x)      (*(f+(x)/16)&(1<<(((x)%16L)/2)))
+#define SET(f,x)       *(f+(x)/16)|=1<<(((x)%16L)/2)
+    unsigned char *feld=NULL, *zzz;
+    afs_uint32 teste=1, mom, hits=1, count, max, alloc, largest_prime = 0;
+
+    max = s + 10000L;
+
+    while (feld==NULL)
+        zzz = feld = malloc (alloc=((max>>4)+1L));
+
+    for (count=0; count<alloc; count++) 
+        *zzz++ = 0x00;
+
+    while ((teste+=2) < max) {
+        if (!TEST(feld, teste)) {
+            for (mom=3L*teste; mom<max; mom+=teste<<1) 
+                SET (feld, mom);
+        }
+    }
+
+    count=s-2; 
+    if (s%2==0) 
+        count++;
+    while ((count+=2)<max) {
+        if (!TEST(feld,count)) {
+            largest_prime = count;
+            break;
+        }
+    }
+    free (feld);
+    return largest_prime;
+}
+
+/* We never free these objects so we don't need to 
+ * worry about reuse or tracking the allocations
+ * 
+ * The management of the free allocation is not
+ * thread safe because we never expect this code
+ * to be called from more than one thread.
+ */
+cm_fid_stats_t * cm_PerformanceGetNew(void)
+{
+    static cm_fid_stats_t * allocp = NULL;
+    static afs_int32        allocSize = 0;
+    static afs_int32        nextFree = 0;
+
+    if (nextFree < allocSize)
+        return &allocp[nextFree++];
+
+    allocp = (cm_fid_stats_t *)malloc(32768);
+    if (allocp == NULL)
+        return NULL;
+
+    allocSize = 32768/sizeof(cm_fid_stats_t);
+    memset(allocp, 0, 32768);
+    nextFree = 1;
+    return allocp;
+}
+
+void cm_PerformanceInsertToHashTable(cm_fid_stats_t *statp)
+{
+    afs_uint32 hash = statp->fid.hash % fidStatsHashTableSize;
+    
+    statp->nextp = fidStatsHashTablep[hash];
+    fidStatsHashTablep[hash] = statp;
+}
+
+void cm_PerformanceAddSCache(cm_scache_t *scp)
+{
+    cm_fid_stats_t * statp = cm_PerformanceGetNew();
+
+    if (!statp)
+        return;
+
+    lock_ObtainRead(&scp->rw);
+    if (!(scp->flags & CM_SCACHEFLAG_DELETED)) {
+        statp->fid = scp->fid;
+        statp->fileLength = scp->length;
+        statp->fileType   = scp->fileType;
+        statp->flags = CM_FIDSTATS_FLAG_HAVE_SCACHE;
+        if (cm_HaveCallback(scp))
+            statp->flags |= CM_FIDSTATS_FLAG_CALLBACK;
+        if (scp->flags & CM_SCACHEFLAG_RO)
+            statp->flags |= CM_FIDSTATS_FLAG_RO;
+        if (scp->flags & CM_SCACHEFLAG_PURERO)
+            statp->flags |= CM_FIDSTATS_FLAG_PURERO;
+    }
+    lock_ReleaseRead(&scp->rw);
+
+#if 0
+    if (statp->fid.vnode == 1) {
+        cm_volume_t *volp = NULL;
+        cm_cell_t *cellp = NULL;
+        cm_req_t req;
+
+        cm_InitReq(&req);
+        
+        cellp = cm_FindCellByID(statp->fid.cell, 0);
+        if (cellp) {
+            if (!cm_GetVolumeByID(cellp, statp->fid.volume, cm_rootUserp, &req, 0, &volp)) {
+                statp->flags |= CM_FIDSTATS_HAVE_VOLUME;
+                cm_PutVolume(volp);
+            }
+        }
+    }
+#endif
+
+    cm_PerformanceInsertToHashTable(statp);
+}
+
+
+void cm_PerformanceTuningInit(void)
+{
+    afs_uint32 i;
+    cm_scache_t *scp;
+    cm_volume_t *volp;
+    cm_buf_t *bp;
+    cm_fid_t fid;
+    afs_uint32 hash;
+    cm_fid_stats_t * statp;
+
+    fidStatsHashTableSize = nearest_prime(cm_data.stats/3);
+    if (fidStatsHashTableSize == 0)
+        fidStatsHashTableSize = cm_data.stats/3;
+    fidStatsHashTablep = (cm_fid_stats_t **)malloc(fidStatsHashTableSize * sizeof(cm_fid_stats_t *));
+    if (fidStatsHashTablep == NULL) {
+        fidStatsHashTableSize = 0;
+        return;
+    }
+
+    memset(fidStatsHashTablep, 0, fidStatsHashTableSize * sizeof(cm_fid_stats_t *));
+
+    lock_ObtainRead(&cm_scacheLock);
+    for (i=0; i<cm_data.scacheHashTableSize; i++) {
+        for (scp=cm_data.scacheHashTablep[i]; scp; scp=scp->nextp) { 
+            if (scp->fid.cell == 0)
+                continue;
+            cm_PerformanceAddSCache(scp);
+        }
+    }
+    lock_ReleaseRead(&cm_scacheLock);
+
+    lock_ObtainRead(&cm_volumeLock);
+    for(volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
+        if (volp->rw.ID) {
+            cm_SetFid(&fid, volp->cellp->cellID, volp->rw.ID, 1, 1);
+            hash = fid.hash % fidStatsHashTableSize;
+
+            for (statp = fidStatsHashTablep[hash]; statp; statp = statp->nextp) {
+                if (!cm_FidCmp(&fid, &statp->fid)) {
+                    statp->flags |= CM_FIDSTATS_FLAG_HAVE_VOLUME;
+                    break;
+                }
+            }
+            if (!statp) {
+                statp = cm_PerformanceGetNew();
+                statp->fid = fid;
+                statp->fileType = CM_SCACHETYPE_DIRECTORY;
+                statp->flags = CM_FIDSTATS_FLAG_HAVE_VOLUME;
+                cm_PerformanceInsertToHashTable(statp);
+            }
+        }
+        if (volp->ro.ID) {
+            cm_SetFid(&fid, volp->cellp->cellID, volp->ro.ID, 1, 1);
+            hash = fid.hash % fidStatsHashTableSize;
+
+            for (statp = fidStatsHashTablep[hash]; statp; statp = statp->nextp) {
+                if (!cm_FidCmp(&fid, &statp->fid)) {
+                    statp->flags |= CM_FIDSTATS_FLAG_HAVE_VOLUME;
+                    break;
+                }
+            }
+            if (!statp) {
+                statp = cm_PerformanceGetNew();
+                statp->fid = fid;
+                statp->fileType = CM_SCACHETYPE_DIRECTORY;
+                statp->flags = CM_FIDSTATS_FLAG_HAVE_VOLUME | CM_FIDSTATS_FLAG_RO | CM_FIDSTATS_FLAG_PURERO;
+                cm_PerformanceInsertToHashTable(statp);
+            }
+        }
+        if (volp->bk.ID) {
+            cm_SetFid(&fid, volp->cellp->cellID, volp->bk.ID, 1, 1);
+            hash = fid.hash % fidStatsHashTableSize;
+
+            for (statp = fidStatsHashTablep[hash]; statp; statp = statp->nextp) {
+                if (!cm_FidCmp(&fid, &statp->fid)) {
+                    statp->flags |= CM_FIDSTATS_FLAG_HAVE_VOLUME;
+                    break;
+                }
+            }
+            if (!statp) {
+                statp = cm_PerformanceGetNew();
+                statp->fid = fid;
+                statp->fileType = CM_SCACHETYPE_DIRECTORY;
+                statp->flags = CM_FIDSTATS_FLAG_HAVE_VOLUME | CM_FIDSTATS_FLAG_RO;
+                cm_PerformanceInsertToHashTable(statp);
+            }
+        }
+    }  
+    lock_ReleaseRead(&cm_volumeLock);
+
+    lock_ObtainRead(&buf_globalLock);
+    for (bp = cm_data.buf_allp; bp; bp=bp->allp) {
+        int valid = 0;
+    
+        if (bp->fid.cell == 0)
+            continue;
+
+        lock_ReleaseRead(&buf_globalLock);
+        scp = cm_FindSCache(&bp->fid);
+        if (scp) {
+            lock_ObtainMutex(&bp->mx);
+            lock_ObtainRead(&scp->rw);
+            valid = cm_HaveBuffer(scp, bp, TRUE);
+            lock_ReleaseRead(&scp->rw);
+            lock_ReleaseMutex(&bp->mx);
+            cm_ReleaseSCache(scp);
+        
+            if (valid) {
+                hash = bp->fid.hash % fidStatsHashTableSize;
+                for (statp = fidStatsHashTablep[hash]; statp; statp = statp->nextp) {
+                    if (!cm_FidCmp(&bp->fid, &statp->fid)) {
+                        statp->buffers++;
+                        break;
+                    }
+                }
+            }
+        }
+        lock_ObtainRead(&buf_globalLock);
+    }
+    lock_ReleaseRead(&buf_globalLock);
+
+    cm_PerformancePrintReport();
+}
+
+void cm_PerformanceTuningCheck(void) 
+{
+    afs_uint32 i;
+    cm_scache_t *scp;
+    cm_volume_t *volp;
+    cm_buf_t *bp;
+    cm_fid_t fid;
+    afs_uint32 hash;
+    cm_fid_stats_t * statp;
+
+    if (fidStatsHashTablep == NULL)
+        return;
+
+    /* Clean all cm_fid_stat_t objects first */
+    for (i = 0; i < fidStatsHashTableSize; i++) {
+        for (statp = fidStatsHashTablep[i]; statp; statp = statp->nextp) {
+            statp->flags &= (CM_FIDSTATS_FLAG_RO | CM_FIDSTATS_FLAG_PURERO);
+            statp->buffers = 0;
+            statp->fileLength.QuadPart = 0;
+        }
+    }
+
+    lock_ObtainRead(&cm_scacheLock);
+    for (i=0; i<cm_data.scacheHashTableSize; i++) {
+        for (scp=cm_data.scacheHashTablep[i]; scp; scp=scp->nextp) {
+            if (scp->fid.cell == 0)
+                continue;
+            hash = scp->fid.hash % fidStatsHashTableSize;
+
+            for (statp = fidStatsHashTablep[hash]; statp; statp = statp->nextp) {
+                if (!cm_FidCmp(&fid, &statp->fid)) {
+                    statp->fileType = scp->fileType;
+                    if (cm_HaveCallback(scp))
+                        statp->flags |= CM_FIDSTATS_FLAG_CALLBACK;
+                    statp->flags |= CM_FIDSTATS_FLAG_HAVE_SCACHE;
+                    break;
+                }
+            }
+            if (!statp)
+                cm_PerformanceAddSCache(scp);
+        }
+    }
+    lock_ReleaseRead(&cm_scacheLock);
+
+    lock_ObtainRead(&cm_volumeLock);
+    for(volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
+        if (volp->rw.ID) {
+            cm_SetFid(&fid, volp->cellp->cellID, volp->rw.ID, 1, 1);
+            hash = fid.hash % fidStatsHashTableSize;
+
+            for (statp = fidStatsHashTablep[hash]; statp; statp = statp->nextp) {
+                if (!cm_FidCmp(&fid, &statp->fid)) {
+                    statp->flags |= CM_FIDSTATS_FLAG_HAVE_VOLUME;
+                    break;
+                }
+            }
+            if (!statp) {
+                statp = cm_PerformanceGetNew();
+                statp->fid = fid;
+                statp->fileType = CM_SCACHETYPE_DIRECTORY;
+                statp->flags = CM_FIDSTATS_FLAG_HAVE_VOLUME;
+                cm_PerformanceInsertToHashTable(statp);
+            }
+        }
+        if (volp->ro.ID) {
+            cm_SetFid(&fid, volp->cellp->cellID, volp->ro.ID, 1, 1);
+            hash = fid.hash % fidStatsHashTableSize;
+
+            for (statp = fidStatsHashTablep[hash]; statp; statp = statp->nextp) {
+                if (!cm_FidCmp(&fid, &statp->fid)) {
+                    statp->flags |= CM_FIDSTATS_FLAG_HAVE_VOLUME;
+                    break;
+                }
+            }
+            if (!statp) {
+                statp = cm_PerformanceGetNew();
+                statp->fid = fid;
+                statp->fileType = CM_SCACHETYPE_DIRECTORY;
+                statp->flags = CM_FIDSTATS_FLAG_HAVE_VOLUME | CM_FIDSTATS_FLAG_RO | CM_FIDSTATS_FLAG_PURERO;
+                cm_PerformanceInsertToHashTable(statp);
+            }
+        }
+        if (volp->bk.ID) {
+            cm_SetFid(&fid, volp->cellp->cellID, volp->bk.ID, 1, 1);
+            hash = fid.hash % fidStatsHashTableSize;
+
+            for (statp = fidStatsHashTablep[hash]; statp; statp = statp->nextp) {
+                if (!cm_FidCmp(&fid, &statp->fid)) {
+                    statp->flags |= CM_FIDSTATS_FLAG_HAVE_VOLUME;
+                    break;
+                }
+            }
+            if (!statp) {
+                statp = cm_PerformanceGetNew();
+                statp->fid = fid;
+                statp->fileType = CM_SCACHETYPE_DIRECTORY;
+                statp->flags = CM_FIDSTATS_FLAG_HAVE_VOLUME | CM_FIDSTATS_FLAG_RO;
+                cm_PerformanceInsertToHashTable(statp);
+            }
+        }
+    }  
+    lock_ReleaseRead(&cm_volumeLock);
+
+    lock_ObtainRead(&buf_globalLock);
+    for (bp = cm_data.buf_allp; bp; bp=bp->allp) {
+        int valid = 0;
+    
+        if (bp->fid.cell == 0)
+            continue;
+
+        lock_ReleaseRead(&buf_globalLock);
+        scp = cm_FindSCache(&bp->fid);
+        if (scp) {
+            lock_ObtainMutex(&bp->mx);
+            lock_ObtainRead(&scp->rw);
+            valid = cm_HaveBuffer(scp, bp, TRUE);
+            lock_ReleaseRead(&scp->rw);
+            lock_ReleaseMutex(&bp->mx);
+            cm_ReleaseSCache(scp);
+        
+            if (valid) {
+                hash = bp->fid.hash % fidStatsHashTableSize;
+                for (statp = fidStatsHashTablep[hash]; statp; statp = statp->nextp) {
+                    if (!cm_FidCmp(&bp->fid, &statp->fid)) {
+                        statp->buffers++;
+                        break;
+                    }
+                }
+            }
+        }
+        lock_ObtainRead(&buf_globalLock);
+    }
+    lock_ReleaseRead(&buf_globalLock);
+
+    cm_PerformancePrintReport();
+}
+
+void cm_PerformancePrintReport(void)
+{
+    afs_uint32 i;
+    cm_fid_stats_t *statp;
+
+    afs_uint32 rw_vols = 0, ro_vols = 0, bk_vols = 0;
+    afs_uint32 fid_cnt = 0, fid_w_vol = 0, fid_w_scache = 0, fid_w_buffers = 0, fid_w_callbacks = 0;
+    afs_uint32 fid_w_scache_no_vol = 0, fid_w_scache_no_buf = 0, fid_w_vol_no_scache = 0, fid_w_buf_no_scache = 0;
+    afs_uint32 fid_file = 0, fid_dir = 0, fid_mp = 0, fid_sym = 0, fid_other = 0;
+    afs_uint32 fid_0k = 0, fid_1k = 0, fid_4k = 0, fid_64k = 0, fid_1m = 0, fid_20m = 0, fid_100m = 0, fid_1g = 0, fid_2g = 0, fid_large = 0;
+
+    HANDLE hLogFile;
+    char logfileName[MAX_PATH+1];
+    DWORD dwSize;
+
+    if (fidStatsHashTablep == NULL)
+        return;
+
+    /* Clean all cm_fid_stat_t objects first */
+    for (i = 0; i < fidStatsHashTableSize; i++) {
+        for (statp = fidStatsHashTablep[i]; statp; statp = statp->nextp) {
+            /* summarize the data */
+            fid_cnt++;
+
+            if ((statp->flags & (CM_FIDSTATS_FLAG_RO | CM_FIDSTATS_FLAG_PURERO)) == (CM_FIDSTATS_FLAG_RO | CM_FIDSTATS_FLAG_PURERO))
+                ro_vols++;
+            else if (statp->flags & CM_FIDSTATS_FLAG_RO) 
+                bk_vols++;
+            else
+                rw_vols++;
+
+            if (statp->flags & CM_FIDSTATS_FLAG_HAVE_VOLUME)
+                fid_w_vol++;
+
+            if (statp->flags & CM_FIDSTATS_FLAG_HAVE_SCACHE)
+                fid_w_scache++;
+
+            if (statp->flags & CM_FIDSTATS_FLAG_CALLBACK)
+                fid_w_callbacks++;
+
+            if (statp->buffers > 0)
+                fid_w_buffers++;
+
+            if ((statp->flags & CM_FIDSTATS_FLAG_HAVE_SCACHE) &&
+                !(statp->flags & CM_FIDSTATS_FLAG_HAVE_VOLUME))
+                fid_w_scache_no_vol++;
+
+            if ((statp->flags & CM_FIDSTATS_FLAG_HAVE_SCACHE) &&
+                statp->buffers == 0)
+                fid_w_scache_no_buf++;
+
+            if ((statp->flags & CM_FIDSTATS_FLAG_HAVE_VOLUME) &&
+                !(statp->flags & CM_FIDSTATS_FLAG_HAVE_SCACHE))
+                fid_w_vol_no_scache++;
+
+            if (!(statp->flags & CM_FIDSTATS_FLAG_HAVE_SCACHE) &&
+                statp->buffers > 0)
+                fid_w_buf_no_scache++;
+
+            switch (statp->fileType) {
+            case CM_SCACHETYPE_FILE       :
+                fid_file++;
+                break;
+            case CM_SCACHETYPE_DIRECTORY  :
+                fid_dir++;
+                break;
+            case CM_SCACHETYPE_SYMLINK    :
+                fid_sym++;
+                break;
+            case CM_SCACHETYPE_MOUNTPOINT :
+                fid_mp++;
+                break;
+            case CM_SCACHETYPE_DFSLINK    :
+            case CM_SCACHETYPE_INVALID    :
+            default:
+                fid_other++;
+            }
+
+            if (statp->fileType == CM_SCACHETYPE_FILE) {
+                if (statp->fileLength.HighPart == 0) {
+                    if (statp->fileLength.LowPart == 0)
+                        fid_0k++;
+                    else if (statp->fileLength.LowPart <= 1024)
+                        fid_1k++;
+                    else if (statp->fileLength.LowPart <= 4096) 
+                        fid_4k++;
+                    else if (statp->fileLength.LowPart <= 65536)
+                        fid_64k++;
+                    else if (statp->fileLength.LowPart <= 1024*1024)
+                        fid_1m++;
+                    else if (statp->fileLength.LowPart <= 20*1024*1024)
+                        fid_20m++;
+                    else if (statp->fileLength.LowPart <= 100*1024*1024)
+                        fid_100m++;
+                    else if (statp->fileLength.LowPart <= 1024*1024*1024)
+                        fid_1g++;
+                    else
+                        fid_2g++;
+                } else {    
+                    fid_large++;
+                }   
+            }
+        }
+    }
+
+    dwSize = GetEnvironmentVariable("TEMP", logfileName, sizeof(logfileName));
+    if ( dwSize == 0 || dwSize > sizeof(logfileName) )
+    {
+        GetWindowsDirectory(logfileName, sizeof(logfileName));
+    }
+    strncat(logfileName, "\\afsd_performance.log", sizeof(logfileName));
+
+    hLogFile = CreateFile(logfileName, FILE_APPEND_DATA, FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 
+                          FILE_ATTRIBUTE_NORMAL, NULL);
+
+    if (hLogFile) {
+        char output[1024];
+        char t[100];
+        int zilch;
+
+        GetTimeFormat(LOCALE_SYSTEM_DEFAULT, 0, NULL, NULL, t, sizeof(t));
+
+        StringCbPrintfA(output, sizeof(output),
+                        "TIME - %s\r\n"
+                        "INUSE- stats=(%u of %u) vols=(%u of %u) bufs=(%I64u of %I64u)\r\n"
+                        "FIDs - total=%u haveVol=%u haveStat=%u haveCB=%u haveBuf=%u haveStatNoVol=%u haveVolNoStat=%u haveStatNoBuf=%u haveBufNoStat=%u\r\n"
+                        "VOLs - rw=%u ro=%u bk=%u\r\n"
+                        "TYPEs- file=%u dir=%u mp=%u sym=%u unk=%u\r\n"
+                        "SIZEs- 0kb=%u 1kb=%u 4kb=%u 64kb=%u 1mb=%u 20m=%u 100mb=%u 1gb=%u 2gb=%u larger=%u\r\n\r\n",
+                         t, 
+                         cm_data.currentSCaches, cm_data.maxSCaches, cm_data.currentVolumes, cm_data.maxVolumes,
+                         cm_data.buf_nbuffers - buf_CountFreeList(), cm_data.buf_nbuffers,
+                         fid_cnt, fid_w_vol, fid_w_scache, fid_w_callbacks, fid_w_buffers,
+                         fid_w_scache_no_vol, fid_w_vol_no_scache, fid_w_scache_no_buf, fid_w_buf_no_scache,
+                         rw_vols, ro_vols, bk_vols,
+                         fid_file, fid_dir, fid_mp, fid_sym, fid_other,
+                         fid_0k, fid_1k, fid_4k, fid_64k, fid_1m, fid_20m, fid_100m, fid_1g, fid_2g, fid_large
+                         );
+        WriteFile(hLogFile, output, (DWORD)strlen(output), &zilch, NULL);
+
+        CloseHandle(hLogFile);
+    }
+
+
+}
diff --git a/src/WINNT/afsd/cm_performance.h b/src/WINNT/afsd/cm_performance.h
new file mode 100644 (file)
index 0000000..afcc11a
--- /dev/null
@@ -0,0 +1,37 @@
+/* 
+ *  Copyright (c) 2008 - Secure Endpoints Inc.
+ */
+
+/* 
+ *  The performance module when activated collects 
+ *  data necessary to analyze the usage of the cache 
+ *  manager and establish recommendations for future
+ *  cache manager configuration changes. 
+ *
+ *  As a starting point, the package will collect 
+ *  a list of all FIDs accessed during the session
+ *  which will be used to periodically analyzed the
+ *  contents of the cm_buf_t, cm_scache_t, cm_volume_t
+ *  and cm_cell_t pools.  
+
+ */
+
+typedef struct cm_fid_stats {
+    cm_fid_t    fid;
+    afs_uint32  fileType;
+    osi_hyper_t  fileLength;
+    afs_uint32  flags;
+    afs_uint32  buffers;
+    struct cm_fid_stats * nextp;
+} cm_fid_stats_t;
+
+#define CM_FIDSTATS_FLAG_HAVE_SCACHE 0x01  /* set if cm_scache_t present */
+#define CM_FIDSTATS_FLAG_HAVE_VOLUME 0x02  /* set on (vnode = 1) if cm_vol_t present */
+#define CM_FIDSTATS_FLAG_RO          0x04
+#define CM_FIDSTATS_FLAG_PURERO      0x08    
+#define CM_FIDSTATS_FLAG_CALLBACK    0x10 
+
+extern void cm_PerformanceTuningCheck(void);
+
+extern void cm_PerformancePrintReport(void);
+