/***************************************************************************** /* VM.c Virtual memory support functions for HTTPd using the LIB$*_VM_* routines. The reason for not using generic memory routines such as calloc() and malloc() for frequently requested and then disposed of dynamic memory is of course efficiency. The theory goes like this ... tailoring VMS memory zones against the expected request profile for it's use will result in faster and more efficient memory management. An added, and not inconsequential bonus, is the additional integrity checking the library routines can provide. A general zone is created for non-specific allocations. A zone for fixed sized memory blocks is created for the request structures. An individual zone is created for each request's heap. Hopefully the overhead of creating zone will be offset by the lower overhead probably required for managing a smaller and short-lived collection of chunks, and the documentation specifically states that reseting or deleting a memory zone is a more efficient method of disposing of virtual memory than individually freeing each chunk. A separate zone is created for each of volatile and permanent cache memory. MEMORY MANAGEMENT IS CONSIDERED CRUCIAL TO HTTPD FUNCTIONING. IF AN ALLOCATION OR FREEING GENERATES AN ERROR (E.G. INSUFFICIENT VIRTUAL, BAD BLOCK, ETC.) THEN THIS IS CONSIDERED SERIOUS (ESPECIALLY THE LATTER, INDICATING STRUCTURE CORRUPTION) AND THE SERVER EXITS REPORTING STATUS INFORMATION. Hence calls to memory allocation and freeing do not need to check for success as only successful operations will return to them! VERSION HISTORY --------------- 14-MAR-2011 MGD VM_OFFSET now 8 (natural alignment) instead of 4 04-JUL-2006 MGD use PercentOf() for more accurate percentages 16-APR-2005 MGD modify statistics to a maximum of 1024 pages and granularity of 8 (GZIP significantly increased memory requirements) 26-MAR-2005 MGD VmRequestTune() set 'VmRequestSizePages' dynamically 22-MAY-2004 MGD bugfix; VmGetRequest() error exit if zone create fails 27-JAN-2004 MGD VmCacheInit() and VmPermCacheInit() extend size reduced 09-DEC-2003 MGD VmGeneralInit() upped from 1024/1024 to 4096/4096 18-JUN-2003 MGD refine VmRequestTune() 14-JUN-2003 MGD request heap statistics and VmRequestTune() 24-MAY-2003 MGD VmPermCache..() 29-SEP-2001 MGD instance support 04-AUG-2001 MGD support module WATCHing 26-FEB-2001 MGD observation indicates VmGeneralInit() extend up to 1024 04-MAR-2000 MGD use FaolToNet(), et.al. 05-FEB-2000 MGD add module name and line number to VmFree/Realloc...() 16-JUL-1998 MGD observation indicates VmGeneralInit() initial up to 1024 07-DEC-1997 MGD using LIB$*_VM_* routines for virtual memory manipulation (and unbundled from support.c for v5.0) */ /*****************************************************************************/ #ifdef WASD_VMS_V7 #undef _VMS__V6__SOURCE #define _VMS__V6__SOURCE #undef __VMS_VER #define __VMS_VER 70000000 #undef __CRTL_VER #define __CRTL_VER 70000000 #endif /* standard C header files */ #include #include #include /* VMS related header files */ #include #include #include #include #include /* application related header files */ #include "wasd.h" #define WASD_MODULE "VM" /******************/ /* global storage */ /******************/ /* number of pages in initial and extend request virtual memory zone */ #define VM_REQUEST_SIZE_PAGES 48 #define VM_REQUEST_SIZE_PAGES_MIN 24 #define VM_REQUEST_SIZE_PAGES_MAX 1024 #define VM_REQUEST_SIZE_PAGES_STAT_MAX 1024 #define VM_REQUEST_SIZE_PAGES_STAT_GRANULE 8 /* For zone with realloc functions use the first longword to store the size. The second long brings the allocated memory back into quadword alignment. */ #define VM_OFFSET 8 /* Where appropriate allocate an extra few bytes for carriage-control in buffers, null string terminators, programming errors ;^), etc. */ #define VM_ELBOW_ROOM 8 unsigned long VmCacheVmZoneId, VmGeneralVmZoneId, VmPermCacheVmZoneId, VmRequestExtendPages, VmRequestSizePages, VmRequestVmZoneId; /* statistics are accumulated in a combination of ints and the structure */ #define VM_STATS_MAXMIN 0xffffffff unsigned long VmHeapStatsCount, VmHeapStatsGetCountMax, VmHeapStatsGetCountMin, VmHeapStatsFreeCountMax, VmHeapStatsFreeCountMin, VmHeapStatsReallocCountMax, VmHeapStatsReallocCountMin, VmHeapStatsResetCount; unsigned long VmHeapStatsPageCount [(VM_REQUEST_SIZE_PAGES_STAT_MAX/ VM_REQUEST_SIZE_PAGES_STAT_GRANULE)+1]; struct RequestHeapStatsStruct VmHeapStats; /********************/ /* external storage */ /********************/ extern int CacheEntryKBytesMax, CacheTotalKBytesMax, NetConcurrentMax; extern char HttpProtocol[], SoftwareID[]; extern CONFIG_STRUCT Config; extern HTTPD_GBLSEC *HttpdGblSecPtr; extern WATCH_STRUCT Watch; /*****************************************************************************/ /* Initialize the virtual memory zone general memory will be allocated from. */ VmGeneralInit () { static $DESCRIPTOR (ZoneNameDsc, "HTTPd General"); static unsigned long Algorithm = LIB$K_VM_QUICK_FIT, AlgorithmArg = 64, Flags = LIB$M_VM_BOUNDARY_TAGS | LIB$M_VM_GET_FILL0 | LIB$M_VM_TAIL_LARGE, ExtendSize = 4096, InitialSize = 4096, BlockSize = 64; int status; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) WatchThis (NULL, FI_LI, WATCH_MOD_VM, "VmGeneralInit()"); /* create the cache virtual memory zone */ status = lib$create_vm_zone (&VmGeneralVmZoneId, &Algorithm, &AlgorithmArg, &Flags, &ExtendSize, &InitialSize, &BlockSize, 0, 0, 0, &ZoneNameDsc, 0, 0); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$create_vm_zone()", FI_LI); } /*****************************************************************************/ /* Allocate memory for general use. */ char* VmGet (unsigned long ChunkSize) { int status; unsigned long BaseAddress; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) WatchThis (NULL, FI_LI, WATCH_MOD_VM, "VmGet() !UL", ChunkSize); ChunkSize += VM_OFFSET + VM_ELBOW_ROOM; status = lib$get_vm (&ChunkSize, &BaseAddress, &VmGeneralVmZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$get_vm()", FI_LI); *(unsigned long*)BaseAddress = ChunkSize-VM_OFFSET; return ((unsigned char*)BaseAddress+VM_OFFSET); } /*****************************************************************************/ /* Expand (or even contract) an individual a general-use chunk. See VmGet(). */ char* VmRealloc ( char *ChunkPtr, unsigned long ChunkSize, char *SourceModuleName, int SourceLineNumber ) { int status, CurrentChunkSize; unsigned long BaseAddress, FreeBaseAddress; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) { if (ChunkPtr) CurrentChunkSize = *(unsigned long*)(ChunkPtr-VM_OFFSET); else CurrentChunkSize = 0; WatchThis (NULL, FI_LI, WATCH_MOD_VM, "VmRealloc() !UL !UL", CurrentChunkSize, ChunkSize); } if (!ChunkPtr) return (VmGet (ChunkSize)); /* if this chunk satisfies the reallocation then just return it */ CurrentChunkSize = *(unsigned long*)(ChunkPtr-VM_OFFSET); if (CurrentChunkSize >= ChunkSize) return (ChunkPtr); /* allocate a new, larger chunk */ ChunkSize += VM_OFFSET + VM_ELBOW_ROOM; status = lib$get_vm (&ChunkSize, &BaseAddress, &VmGeneralVmZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$get_vm()", SourceModuleName, SourceLineNumber); /* copy the existing chunk into the new chunk */ memcpy ((unsigned char*)BaseAddress+VM_OFFSET, ChunkPtr, CurrentChunkSize); /* free the previous chunk */ FreeBaseAddress = ChunkPtr-VM_OFFSET; status = lib$free_vm (0, &FreeBaseAddress, &VmGeneralVmZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$free_vm()", SourceModuleName, SourceLineNumber); *(unsigned long*)BaseAddress = ChunkSize-VM_OFFSET; return ((unsigned char*)BaseAddress+VM_OFFSET); } /*****************************************************************************/ /* Release memory allocated for general use. */ VmFree ( char *ChunkPtr, char *SourceModuleName, int SourceLineNumber ) { int status, CurrentChunkSize; unsigned long BaseAddress; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) { if (ChunkPtr) CurrentChunkSize = *(unsigned long*)(ChunkPtr-VM_OFFSET); else CurrentChunkSize = 0; WatchThis (NULL, FI_LI, WATCH_MOD_VM, "VmFree() !UL", CurrentChunkSize); } BaseAddress = ChunkPtr-VM_OFFSET; status = lib$free_vm (0, &BaseAddress, &VmGeneralVmZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$free_vm()", SourceModuleName, SourceLineNumber); } /*****************************************************************************/ /* Initialize the virtual memory zone for the request structures. */ VmRequestInit () { static $DESCRIPTOR (ZoneNameDsc, "HTTPd Request"); static unsigned long Algorithm = LIB$K_VM_QUICK_FIT, AlgorithmArg = 32, Flags = LIB$M_VM_BOUNDARY_TAGS | LIB$M_VM_GET_FILL0 | LIB$M_VM_TAIL_LARGE, InitialSize, BlockSize = 64; int status; char *cptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) WatchThis (NULL, FI_LI, WATCH_MOD_VM, "VmRequestInit() !UL", NetConcurrentMax); cptr = getenv("WASD_VM_REQUEST_SIZE_PAGES"); if (cptr) VmRequestSizePages = atol(cptr); else { InstanceMutexLock (INSTANCE_MUTEX_HTTPD); VmRequestSizePages = HttpdGblSecPtr->VmRequestSizePages; InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD); } if (VmRequestSizePages < 24 || VmRequestSizePages > 128) VmRequestSizePages = VM_REQUEST_SIZE_PAGES; InitialSize = (NetConcurrentMax * sizeof(REQUEST_STRUCT)) / 512 + 1; /* create the cache virtual memory zone */ status = lib$create_vm_zone (&VmRequestVmZoneId, &Algorithm, &AlgorithmArg, &Flags, 0, &InitialSize, &BlockSize, 0, 0, 0, &ZoneNameDsc, 0, 0); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$create_vm_zone()", FI_LI); VmHeapStatsGetCountMin = VmHeapStatsFreeCountMin = VmHeapStatsReallocCountMin = VmHeapStats.FreeByteMin = VmHeapStats.GetByteMin = VmHeapStats.ReallocByteMin = VM_STATS_MAXMIN; } /*****************************************************************************/ /* Called periodically by HttpTick() and also on an ad hoc basis when VmReport() is used. Check most commonly required request heap pages and store it away for the next startup. I know the way the pages are calculated before being stored into the array (VmFreeRequest()) is not 100% percent accurate with what may have actually been used by LIB$VM but as this statistic is not available from anywhere I know of and it's not worth fooling around EXEC code to get it this will be close enough for jazz. */ VmRequestTune () { int idx; unsigned long PageCount, RequestCount, RequestCountTotal; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) WatchThis (NULL, FI_LI, WATCH_MOD_VM, "VmRequestTune()"); PageCount = RequestCount = RequestCountTotal = 0; for (idx = 0; idx < VM_REQUEST_SIZE_PAGES_MAX / VM_REQUEST_SIZE_PAGES_STAT_GRANULE; idx++) RequestCountTotal += VmHeapStatsPageCount[idx]; /* only take any notice of this if there's been at least 1,000 requests */ if (RequestCountTotal < 1000) return; for (idx = 0; idx < VM_REQUEST_SIZE_PAGES_MAX / VM_REQUEST_SIZE_PAGES_STAT_GRANULE; idx++) { if (!VmHeapStatsPageCount[idx]) continue; RequestCount += VmHeapStatsPageCount[idx]; /* if the percentage calculation would overflow forget it! */ if (RequestCount * 100 < RequestCount) return; /* if this still accounts for less than eighty percent of requests */ if (RequestCount * 100 / RequestCountTotal < 80) continue; break; } PageCount = (idx + 1) * VM_REQUEST_SIZE_PAGES_STAT_GRANULE; if (WATCH_MODULE(WATCH_MOD_VM)) WatchDataFormatted ("!UL !UL !UL% !UL\n", RequestCountTotal, RequestCount, PercentOf(RequestCount,RequestCountTotal), PageCount); if (PageCount) { /* change the value dynamically */ VmRequestSizePages = PageCount; /* place the value in the global section for next startup */ InstanceMutexLock (INSTANCE_MUTEX_HTTPD); HttpdGblSecPtr->VmRequestSizePages = PageCount; InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD); } } /*****************************************************************************/ /* Allocate a request structure with associated virtual memory zone ready for heap allocation. */ REQUEST_STRUCT* VmGetRequest () { static $DESCRIPTOR (ZoneNameDsc, "HTTPd Heap"); static unsigned long Algorithm = LIB$K_VM_QUICK_FIT, AlgorithmArg = 16, Flags = LIB$M_VM_BOUNDARY_TAGS | LIB$M_VM_GET_FILL0 | LIB$M_VM_TAIL_LARGE, BlockSize = 64; static unsigned long RequestStructSize = sizeof(REQUEST_STRUCT); int status; unsigned long BaseAddress; REQUEST_STRUCT *rqptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) WatchThis (NULL, FI_LI, WATCH_MOD_VM, "VmGetRequest()"); status = lib$get_vm (&RequestStructSize, &BaseAddress, &VmRequestVmZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$get_vm()", FI_LI); rqptr = (REQUEST_STRUCT*)BaseAddress; /* now create a virtual memory zone for the request's heap */ rqptr->VmHeapZoneId = 0; status = lib$create_vm_zone (&rqptr->VmHeapZoneId, &Algorithm, &AlgorithmArg, &Flags, &VmRequestSizePages, &VmRequestSizePages, &BlockSize, 0, 0, 0, &ZoneNameDsc, 0, 0); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$create_vm_zone()", FI_LI); rqptr->rqHeapStats.FreeByteMin = rqptr->rqHeapStats.GetByteMin = rqptr->rqHeapStats.ReallocByteMin = VM_STATS_MAXMIN; return (rqptr); } /*****************************************************************************/ /* Delete any virtual memory zone created for the request's heap, then return the request structure to the request virtual memory pool. */ VmFreeRequest ( REQUEST_STRUCT *rqptr, char *SourceModuleName, int SourceLineNumber ) { static unsigned long RequestStructSize = sizeof(REQUEST_STRUCT); static unsigned long PrevByteCount; int status; unsigned long PageCount; /*********/ /* begin */ /*********/ if (WATCHING(rqptr) && (WATCH_MODULE(WATCH_MOD_VM))) { PageCount = rqptr->rqHeapStats.ByteCount >> 9; if (rqptr->rqHeapStats.ByteCount & 0x1ff) PageCount++; WatchThis (rqptr, FI_LI, WATCH_MOD_VM, "VmFreeRequest()"); WatchDataFormatted ( "ByteCount: !SL PageCount: !SL VmRequestSizePages: !SL\n\ GetCount: !SL GetByteCount: !SL GetByteMax: !SL GetByteMin: !SL\n\ FreeCount: !SL FreeByteCount: !SL FreeByteMax: !SL FreeByteMin: !SL\n\ ReallocCount: !SL ReallocByteCount: !SL \ ReallocByteMax: !SL ReallocByteMin: !SL\n", rqptr->rqHeapStats.ByteCount, PageCount, VmRequestSizePages, rqptr->rqHeapStats.GetCount, rqptr->rqHeapStats.GetByteCount, rqptr->rqHeapStats.GetByteMax, rqptr->rqHeapStats.GetByteMin, rqptr->rqHeapStats.FreeCount, rqptr->rqHeapStats.FreeByteCount, rqptr->rqHeapStats.FreeByteMax, rqptr->rqHeapStats.FreeByteMin, rqptr->rqHeapStats.ReallocCount, rqptr->rqHeapStats.ReallocByteCount, rqptr->rqHeapStats.ReallocByteMax, rqptr->rqHeapStats.ReallocByteMin); } VmHeapStats.ByteCount += rqptr->rqHeapStats.ByteCount; if (VmHeapStats.ByteCount < PrevByteCount) { /* no nicker knotting here, just detect and reset */ memset (&VmHeapStats, 0, sizeof(VmHeapStats)); memset (&VmHeapStatsPageCount, 0, sizeof(VmHeapStatsPageCount)); VmHeapStatsGetCountMax = VmHeapStatsFreeCountMax = VmHeapStatsReallocCountMax = VmHeapStatsCount = 0; VmHeapStatsGetCountMin = VmHeapStatsFreeCountMin = VmHeapStatsReallocCountMin = VmHeapStats.FreeByteMin = VmHeapStats.GetByteMin = VmHeapStats.ReallocByteMin = VM_STATS_MAXMIN; VmHeapStatsResetCount++; VmHeapStats.ByteCount = rqptr->rqHeapStats.ByteCount; } PrevByteCount = VmHeapStats.ByteCount; VmHeapStatsCount++; PageCount = rqptr->rqHeapStats.ByteCount >> 9; if (rqptr->rqHeapStats.ByteCount & 0x1ff) PageCount++; if (PageCount >= VM_REQUEST_SIZE_PAGES_STAT_MAX) VmHeapStatsPageCount[VM_REQUEST_SIZE_PAGES_STAT_MAX/ VM_REQUEST_SIZE_PAGES_STAT_GRANULE]++; else VmHeapStatsPageCount[PageCount/VM_REQUEST_SIZE_PAGES_STAT_GRANULE]++; if (rqptr->rqHeapStats.GetCount) { VmHeapStats.GetCount += rqptr->rqHeapStats.GetCount; if (rqptr->rqHeapStats.GetCount > VmHeapStatsGetCountMax) VmHeapStatsGetCountMax = rqptr->rqHeapStats.GetCount; if (rqptr->rqHeapStats.GetCount < VmHeapStatsGetCountMin) VmHeapStatsGetCountMin = rqptr->rqHeapStats.GetCount; if (rqptr->rqHeapStats.GetByteMax > VmHeapStats.GetByteMax) VmHeapStats.GetByteMax = rqptr->rqHeapStats.GetByteMax; if (rqptr->rqHeapStats.GetByteMin != VM_STATS_MAXMIN && rqptr->rqHeapStats.GetByteMin < VmHeapStats.GetByteMin) VmHeapStats.GetByteMin = rqptr->rqHeapStats.GetByteMin; } if (rqptr->rqHeapStats.FreeCount) { VmHeapStats.FreeCount += rqptr->rqHeapStats.FreeCount; if (rqptr->rqHeapStats.FreeCount > VmHeapStatsFreeCountMax) VmHeapStatsFreeCountMax = rqptr->rqHeapStats.FreeCount; if (rqptr->rqHeapStats.FreeCount < VmHeapStatsFreeCountMin) VmHeapStatsFreeCountMin = rqptr->rqHeapStats.FreeCount; if (rqptr->rqHeapStats.FreeByteMax > VmHeapStats.FreeByteMax) VmHeapStats.FreeByteMax = rqptr->rqHeapStats.FreeByteMax; if (rqptr->rqHeapStats.FreeByteMin != VM_STATS_MAXMIN && rqptr->rqHeapStats.FreeByteMin < VmHeapStats.FreeByteMin) VmHeapStats.FreeByteMin = rqptr->rqHeapStats.FreeByteMin; } if (rqptr->rqHeapStats.ReallocCount) { VmHeapStats.ReallocCount += rqptr->rqHeapStats.ReallocCount; if (rqptr->rqHeapStats.ReallocCount > VmHeapStatsReallocCountMax) VmHeapStatsReallocCountMax = rqptr->rqHeapStats.ReallocCount; if (rqptr->rqHeapStats.ReallocCount < VmHeapStatsReallocCountMin) VmHeapStatsReallocCountMin = rqptr->rqHeapStats.ReallocCount; if (rqptr->rqHeapStats.ReallocByteMax > VmHeapStats.ReallocByteMax) VmHeapStats.ReallocByteMax = rqptr->rqHeapStats.ReallocByteMax; if (rqptr->rqHeapStats.ReallocByteMin != VM_STATS_MAXMIN && rqptr->rqHeapStats.ReallocByteMin < VmHeapStats.ReallocByteMin) VmHeapStats.ReallocByteMin = rqptr->rqHeapStats.ReallocByteMin; } /* free the request structure's virtual memory */ if (rqptr->VmHeapZoneId) { /* delete the request's heap's virtual memory zone */ status = lib$delete_vm_zone (&rqptr->VmHeapZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$delete_vm_zone()", FI_LI); } status = lib$free_vm (0, &rqptr, &VmRequestVmZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$free_vm()", SourceModuleName, SourceLineNumber); } /*****************************************************************************/ /* Allocate dynamic memory to an individual thread's heap. Return a pointer to the start of new *usable* memory area if successful, return a NULL if not. */ char* VmGetHeap ( REQUEST_STRUCT *rqptr, unsigned long ChunkSize ) { int status; unsigned long BaseAddress; /*********/ /* begin */ /*********/ if (WATCHING(rqptr) && (WATCH_MODULE(WATCH_MOD_VM))) WatchThis (rqptr, FI_LI, WATCH_MOD_VM, "VmGetHeap() !UL", ChunkSize); ChunkSize += VM_OFFSET + VM_ELBOW_ROOM; status = lib$get_vm (&ChunkSize, &BaseAddress, &rqptr->VmHeapZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$get_vm()", FI_LI); rqptr->rqHeapStats.GetCount++; rqptr->rqHeapStats.GetByteCount += ChunkSize; rqptr->rqHeapStats.ByteCount += ChunkSize; if (ChunkSize > rqptr->rqHeapStats.GetByteMax) rqptr->rqHeapStats.GetByteMax = ChunkSize; if (ChunkSize < rqptr->rqHeapStats.GetByteMin) rqptr->rqHeapStats.GetByteMin = ChunkSize; *(unsigned long*)BaseAddress = ChunkSize-VM_OFFSET; return ((unsigned char*)BaseAddress+VM_OFFSET); } /*****************************************************************************/ /* Expand (or even contract) an individual chunk. See VmGetHeap(). */ char* VmReallocHeap ( REQUEST_STRUCT *rqptr, char *ChunkPtr, unsigned long ChunkSize, char *SourceModuleName, int SourceLineNumber ) { int status, CurrentChunkSize; unsigned long BaseAddress, FreeBaseAddress; /*********/ /* begin */ /*********/ if (WATCHING(rqptr) && (WATCH_MODULE(WATCH_MOD_VM))) { if (ChunkPtr) CurrentChunkSize = *(unsigned long*)(ChunkPtr-VM_OFFSET); else CurrentChunkSize = 0; WatchThis (rqptr, FI_LI, WATCH_MOD_VM, "VmReallocHeap() !UL !UL", CurrentChunkSize, ChunkSize); } if (!ChunkPtr) return (VmGetHeap (rqptr, ChunkSize)); /* if this chunk satisfies the reallocation then just return it */ CurrentChunkSize = *(unsigned long*)(ChunkPtr-VM_OFFSET); if (CurrentChunkSize >= ChunkSize) return (ChunkPtr); /* allocate a new, larger chunk */ ChunkSize += VM_OFFSET + VM_ELBOW_ROOM; status = lib$get_vm (&ChunkSize, &BaseAddress, &rqptr->VmHeapZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$get_vm()", SourceModuleName, SourceLineNumber); /* copy the existing chunk into the new chunk */ memcpy ((unsigned char*)BaseAddress+VM_OFFSET, ChunkPtr, CurrentChunkSize); /* free the previous chunk */ FreeBaseAddress = ChunkPtr-VM_OFFSET; status = lib$free_vm (0, &FreeBaseAddress, &rqptr->VmHeapZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$free_vm()", SourceModuleName, SourceLineNumber); rqptr->rqHeapStats.ReallocCount++; rqptr->rqHeapStats.ReallocByteCount += ChunkSize; if (ChunkSize > rqptr->rqHeapStats.ReallocByteMax) rqptr->rqHeapStats.ReallocByteMax = ChunkSize; if (ChunkSize < rqptr->rqHeapStats.ReallocByteMin) rqptr->rqHeapStats.ReallocByteMin = ChunkSize; rqptr->rqHeapStats.ByteCount -= CurrentChunkSize; rqptr->rqHeapStats.ByteCount += ChunkSize; *(unsigned long*)BaseAddress = ChunkSize-VM_OFFSET; return ((unsigned char*)BaseAddress+VM_OFFSET); } /*****************************************************************************/ /* Release back into the virtual memory zone one chunk of request heap memory. */ VmFreeFromHeap ( REQUEST_STRUCT *rqptr, char *ChunkPtr, char *SourceModuleName, int SourceLineNumber ) { int status, CurrentChunkSize; unsigned long FreeBaseAddress; /*********/ /* begin */ /*********/ if (ChunkPtr) CurrentChunkSize = *(unsigned long*)(ChunkPtr-VM_OFFSET); else CurrentChunkSize = 0; if (WATCHING(rqptr) && (WATCH_MODULE(WATCH_MOD_VM))) WatchThis (rqptr, FI_LI, WATCH_MOD_VM, "VmFreeFromHeap() !UL", CurrentChunkSize); /* free the previous chunk */ FreeBaseAddress = ChunkPtr-VM_OFFSET; status = lib$free_vm (0, &FreeBaseAddress, &rqptr->VmHeapZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$free_vm()", SourceModuleName, SourceLineNumber); rqptr->rqHeapStats.FreeCount++; rqptr->rqHeapStats.FreeByteCount += CurrentChunkSize; if (CurrentChunkSize > rqptr->rqHeapStats.FreeByteMax) rqptr->rqHeapStats.FreeByteMax = CurrentChunkSize; if (CurrentChunkSize < rqptr->rqHeapStats.FreeByteMin) rqptr->rqHeapStats.FreeByteMin = CurrentChunkSize; rqptr->rqHeapStats.ByteCount -= CurrentChunkSize; } /*****************************************************************************/ /* Release back into the virtual memory zone the individually allocated chunks of memory (the zone is deleted on request structure release). */ VmFreeHeap ( REQUEST_STRUCT *rqptr, char *SourceModuleName, int SourceLineNumber ) { int status, CurrentChunkSize; /*********/ /* begin */ /*********/ if (WATCHING(rqptr) && (WATCH_MODULE(WATCH_MOD_VM))) WatchThis (rqptr, FI_LI, WATCH_MOD_VM, "VmFreeHeap()"); status = lib$reset_vm_zone (&rqptr->VmHeapZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$reset_vm_zone()", SourceModuleName, SourceLineNumber); } /*****************************************************************************/ /* Initialize the virtual memory zone cache memory will be allocated from. */ VmCacheInit (int TotalKBytesMax) { static $DESCRIPTOR (ZoneNameDsc, "HTTPd Cache"); static unsigned long Algorithm = LIB$K_VM_QUICK_FIT, AlgorithmArg = 128, Flags = LIB$M_VM_BOUNDARY_TAGS | LIB$M_VM_GET_FILL0 | LIB$M_VM_TAIL_LARGE, ExtendSize, InitialSize, BlockSize = CACHE_CHUNK_SIZE; int status; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) WatchThis (NULL, FI_LI, WATCH_MOD_VM, "VmCacheInit() !UL", TotalKBytesMax); if ((InitialSize = TotalKBytesMax * 2) <= 0) InitialSize = 32; ExtendSize = InitialSize / 2; /* create the cache virtual memory zone */ status = lib$create_vm_zone (&VmCacheVmZoneId, &Algorithm, &AlgorithmArg, &Flags, &ExtendSize, &InitialSize, &BlockSize, 0, 0, 0, &ZoneNameDsc, 0, 0); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$create_vm_zone()", FI_LI); } /*****************************************************************************/ /* */ char* VmGetCache (unsigned long ChunkSize) { int status; unsigned long BaseAddress; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) WatchThis (NULL, FI_LI, WATCH_MOD_VM, "VmGetCache() !UL", ChunkSize); status = lib$get_vm (&ChunkSize, &BaseAddress, &VmCacheVmZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$get_vm()", FI_LI); return ((char*)BaseAddress); } /*****************************************************************************/ /* Release back into the virtual memory zone the individually allocated chunks of cache memory. */ VmFreeCache ( char *ChunkPtr, char *SourceModuleName, int SourceLineNumber ) { int status, CurrentChunkSize; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) { if (ChunkPtr) CurrentChunkSize = *(unsigned long*)(ChunkPtr-VM_OFFSET); else CurrentChunkSize = 0; WatchThis (NULL, FI_LI, WATCH_MOD_VM, "VmFreeCache() !UL", CurrentChunkSize); } status = lib$free_vm (0, &ChunkPtr, &VmCacheVmZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$free_vm()", SourceModuleName, SourceLineNumber); } /*****************************************************************************/ /* Initialize the virtual memory zone permanent cache memory will be allocated from. */ VmPermCacheInit (int TotalKBytesMax) { static $DESCRIPTOR (ZoneNameDsc, "HTTPd Perm-Cache"); static unsigned long Algorithm = LIB$K_VM_QUICK_FIT, AlgorithmArg = 128, Flags = LIB$M_VM_BOUNDARY_TAGS | LIB$M_VM_GET_FILL0 | LIB$M_VM_TAIL_LARGE, ExtendSize, InitialSize, BlockSize = CACHE_PERM_CHUNK_SIZE; int status; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) WatchThis (NULL, FI_LI, WATCH_MOD_VM, "VmPermCacheInit() !UL", TotalKBytesMax); if ((InitialSize = TotalKBytesMax * 2) <= 0) InitialSize = 32; ExtendSize = InitialSize / 2; /* create the cache virtual memory zone */ status = lib$create_vm_zone (&VmPermCacheVmZoneId, &Algorithm, &AlgorithmArg, &Flags, &ExtendSize, &InitialSize, &BlockSize, 0, 0, 0, &ZoneNameDsc, 0, 0); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$create_vm_zone()", FI_LI); } /*****************************************************************************/ /* */ char* VmGetPermCache (unsigned long ChunkSize) { int status; unsigned long BaseAddress; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) WatchThis (NULL, FI_LI, WATCH_MOD_VM, "VmGetPermCache() !UL", ChunkSize); if (!VmPermCacheVmZoneId) VmPermCacheInit (CacheTotalKBytesMax); status = lib$get_vm (&ChunkSize, &BaseAddress, &VmPermCacheVmZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$get_vm()", FI_LI); return ((char*)BaseAddress); } /*****************************************************************************/ /* Release back into the virtual memory zone the individually allocated chunks of permanent cache memory. */ VmFreePermCache ( char *ChunkPtr, char *SourceModuleName, int SourceLineNumber ) { int status, CurrentChunkSize; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) { if (ChunkPtr) CurrentChunkSize = *(unsigned long*)(ChunkPtr-VM_OFFSET); else CurrentChunkSize = 0; WatchThis (NULL, FI_LI, WATCH_MOD_VM, "VmFreePermCache() !UL", CurrentChunkSize); } status = lib$free_vm (0, &ChunkPtr, &VmPermCacheVmZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$free_vm()", SourceModuleName, SourceLineNumber); } /*****************************************************************************/ /* Return a report on processes' virtual memory usage. This function blocks while executing. */ VmReport ( REQUEST_STRUCT *rqptr, REQUEST_AST NextTaskFunction ) { static unsigned long ShowVmCode123 = 0, ShowVmCode567 = 4, ShowVmZoneDetail = 3; static char BeginPageFao [] = "

\n\ \n\
\n\ \n\ \n\
\n\n\
REQUEST HEAP STATISTICS for !UL requests (reset !UL time!%s)\n\n";

   static char  MoreStatsFao [] =
" !UL pages initial allocation (!UL default)\n\
 !UL request!%s extended from initial allocation (!UL%)\n\
 !UL pages set via dynamic tuning\n\
\n\
 !UL bytes average (!UL pages)\n\
 !UL call!%s to GET heap memory\n\
 !UL per request ave, !UL max, !UL min, !UL bytes max, !UL bytes min\n\
 !UL call!%s to FREE heap memory\n\
 !UL per request ave, !UL max, !UL min, !UL bytes max, !UL bytes min\n\
 !UL call!%s to REALLOC heap memory\n\
 !UL per request ave, !UL max, !UL min, !UL bytes max, !UL bytes min\n\
\n!90*-\n\n";

   static char  EndPageFao [] =
"
\n\
\n\ \n\ \n"; int cnt, idx, status, ByteCountAve, Count, Percent; unsigned short Length; unsigned long Context, ExtendCount, PageCountAve, PageCountTuned, ZoneId; unsigned long *vecptr; unsigned long FaoVector [32]; char Buffer [4096]; $DESCRIPTOR (BufferDsc, Buffer); /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) WatchThis (NULL, FI_LI, WATCH_MOD_VM, "VmReport()"); VmRequestTune (); InstanceMutexLock (INSTANCE_MUTEX_HTTPD); PageCountTuned = HttpdGblSecPtr->VmRequestSizePages; InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD); AdminPageTitle (rqptr, "Virtual Memory Report"); ByteCountAve = VmHeapStats.ByteCount / (VmHeapStatsCount ? VmHeapStatsCount : 1); PageCountAve = ByteCountAve >> 9; if (ByteCountAve & 0x1ff) PageCountAve++; status = FaoToNet (rqptr, BeginPageFao, VmHeapStatsCount, VmHeapStatsResetCount); ExtendCount = 0; for (idx = 0; idx < VM_REQUEST_SIZE_PAGES_MAX / VM_REQUEST_SIZE_PAGES_STAT_GRANULE; idx++) { if (!VmHeapStatsPageCount[idx]) continue; Percent = PercentOf (VmHeapStatsPageCount[idx], VmHeapStatsCount); if (idx*VM_REQUEST_SIZE_PAGES_STAT_GRANULE >= VmRequestSizePages) ExtendCount += VmHeapStatsPageCount[idx]; if (idx*VM_REQUEST_SIZE_PAGES_STAT_GRANULE < VM_REQUEST_SIZE_PAGES_STAT_MAX) status = FaoToNet (rqptr, " !UL-!UL pages !UL request!%s (!UL%)\n", idx*VM_REQUEST_SIZE_PAGES_STAT_GRANULE, ((idx+1)*VM_REQUEST_SIZE_PAGES_STAT_GRANULE)-1, VmHeapStatsPageCount[idx], Percent); else status = FaoToNet (rqptr, " >=!UL pages !UL request!%s (!UL%)\n", idx*VM_REQUEST_SIZE_PAGES_STAT_GRANULE, VmHeapStatsPageCount[idx], Percent); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } Percent = PercentOf (ExtendCount, VmHeapStatsCount); status = FaoToNet (rqptr, MoreStatsFao, VmRequestSizePages, VM_REQUEST_SIZE_PAGES, ExtendCount, Percent, PageCountTuned, ByteCountAve, PageCountAve, VmHeapStats.GetCount, VmHeapStats.GetCount / (VmHeapStatsCount ? VmHeapStatsCount : 1), VmHeapStatsGetCountMax, VmHeapStatsGetCountMin != VM_STATS_MAXMIN ? VmHeapStatsGetCountMin : 0, VmHeapStats.GetByteMax, VmHeapStats.GetByteMin != VM_STATS_MAXMIN ? VmHeapStats.GetByteMin : 0, VmHeapStats.FreeCount, VmHeapStats.FreeCount / (VmHeapStatsCount ? VmHeapStatsCount : 1), VmHeapStatsFreeCountMax, VmHeapStatsFreeCountMin != VM_STATS_MAXMIN ? VmHeapStatsFreeCountMin : 0, VmHeapStats.FreeByteMax, VmHeapStats.FreeByteMin != VM_STATS_MAXMIN ? VmHeapStats.FreeByteMin : 0, VmHeapStats.ReallocCount, VmHeapStats.ReallocCount / (VmHeapStatsCount ? VmHeapStatsCount : 1), VmHeapStatsReallocCountMax, VmHeapStatsReallocCountMin != VM_STATS_MAXMIN ? VmHeapStatsReallocCountMin : 0, VmHeapStats.ReallocByteMax, VmHeapStats.ReallocByteMin != VM_STATS_MAXMIN ? VmHeapStats.ReallocByteMin : 0); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); if (VMSnok (status = lib$show_vm (&ShowVmCode123, &VmWrite, rqptr))) { rqptr->rqResponse.ErrorTextPtr = "lib$show_vm()"; ErrorVmsStatus (rqptr, status, FI_LI); SysDclAst (NextTaskFunction, rqptr); return; } if (VMSnok (status = lib$show_vm (&ShowVmCode567, &VmWrite, rqptr))) { rqptr->rqResponse.ErrorTextPtr = "lib$show_vm()"; ErrorVmsStatus (rqptr, status, FI_LI); SysDclAst (NextTaskFunction, rqptr); return; } status = FaoToNet (rqptr, "\n"); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); Context = 0; while ((status = lib$find_vm_zone (&Context, &ZoneId)) == SS$_NORMAL) { status = FaoToNet (rqptr, "!90*-\n\n"); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); status = lib$show_vm_zone (&ZoneId, &ShowVmZoneDetail, &VmWrite, rqptr); if (VMSnok (status)) { rqptr->rqResponse.ErrorTextPtr = "lib$show_vm_zone()"; ErrorVmsStatus (rqptr, status, FI_LI); SysDclAst (NextTaskFunction, rqptr); return; } } if (status != LIB$_NOTFOU) { rqptr->rqResponse.ErrorTextPtr = "lib$find_vm_zone()"; ErrorVmsStatus (rqptr, status, FI_LI); SysDclAst (NextTaskFunction, rqptr); return; } status = FaolToNet (rqptr, EndPageFao, NULL); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN; ResponseHeader200 (rqptr, "text/html", &rqptr->NetWriteBufferDsc); SysDclAst (NextTaskFunction, rqptr); } /*****************************************************************************/ /* Action routine for lib$show_vm*() routines. Simply write the contents of the paremeter descriptor to the client plus a newline character. */ int VmWrite ( struct dsc$descriptor *DscPtr, REQUEST_STRUCT *rqptr ) { int status; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) WatchThis (NULL, FI_LI, WATCH_MOD_VM, "VmWrite()"); status = FaoToNet (rqptr, "!#&;AZ\n", DscPtr->dsc$w_length, DscPtr->dsc$a_pointer); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); return (SS$_NORMAL); } /*****************************************************************************/ /* */ VmDebug (unsigned long ZoneId) { #ifdef DBUG static unsigned long ShowVmZoneDetail = 3; int status; unsigned long Context; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) WatchThis (NULL, FI_LI, WATCH_MOD_VM, "VmDebug() !UL !60*-", ZoneId); if (ZoneId) { status = lib$show_vm_zone (&ZoneId, &ShowVmZoneDetail, 0, 0); if (VMSnok (status)) exit (status); } else { Context = 0; while ((status = lib$find_vm_zone (&Context, &ZoneId)) == SS$_NORMAL) { status = lib$show_vm_zone (&ZoneId, &ShowVmZoneDetail, 0, 0); if (VMSnok (status)) exit (status); } } if (WATCH_MODULE(WATCH_MOD_VM)) WatchDataFormatted ("----------\n"); #endif } /*****************************************************************************/