/***************************************************************************** /* 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. Zones for fixed sized memory blocks are created for the HTTP/2 and request structures. An individual zone is created for each HTTP/2 connection's and request's heap. The overhead of zone creation will be offset by the lower overhead 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. The request zone has the initial allocation "tuned". After an initial sample of requests the allocation becomes the running "average" of those requests (rounded into mutliples of the hard-wired chunk size). This averaging persists for a period (number of requests) before being reset to the initial sample state again. Logical names may be used to tailor the number of pages allocated for the initial and extend values of the heap zones. The logical name value should be "[,]" (extend value is optional and defaults to the supplied initial value). Defining an to be -1 disables LIB$*VM* zone management of the memory and standard DECC functions are used instead. Statistics are still collected and reported. Some memory management (e.g. request) must always be managed as a zone. Values are read during server startup. Examples: $ DEFINE /EXECUTIVE /TABLE=WASD_TABLE WASD_VM_CACHE "4096" $ DEFINE /EXECUTIVE /TABLE=WASD_TABLE WASD_VM_DECC "2048,4096" $ DEFINE /EXECUTIVE /TABLE=WASD_TABLE WASD_VM_EXPAT "128,128" $ DEFINE /EXECUTIVE /TABLE=WASD_TABLE WASD_VM_HTTP2 "80,128" $ DEFINE /EXECUTIVE /TABLE=WASD_TABLE WASD_VM_OPENSSL "-1" $ DEFINE /EXECUTIVE /TABLE=WASD_TABLE WASD_VM_PERMCACHE "512" $ DEFINE /EXECUTIVE /TABLE=WASD_TABLE WASD_VM_REQUEST "96,128" Request with "tune" parameters (sample of 64, and period 100 times greater): $ DEFINE /EXECUTIVE /TABLE=WASD_TABLE WASD_VM_REQUEST "256,512,64,100" NOTE: MEMORY MANAGEMENT IS CONSIDERED CRUCIAL TO HTTPD FUNCTIONING ------------------------------------------------------------------- IF A ZONE 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! MEMORY CHUNK PROLOGUE --------------------- All memory allocations contain a small prologue structure immediately in front of the location returned to the caller. This provides some memory management data (chunk size and guard magic) and 9 bytes of space immediately adjacent to the first returned byte. This space is intended to contain an HTTP/2 frame header immediately preceding data to be written to the network. It is always present in allocated memory whether used for HTTP/2 framing or not and its presence detected through some specific magic stored immediately preceding (and in this manner "close") to the returned pointer. See HTTP2.c and HTTP2NET.C for further background, example usage, etc. OPENSSL MEMORY STATISTICS ------------------------- The OpenSSL memory management calls malloc(), realloc() and free() can be intercepted by this module and used to provide statistics on their use. The calls are actually still processed by the DECC run-time equivalent calls. Compatible with "intercepting DECC calls" but not "OpenSSL memory management". OPENSSL MEMORY MANAGEMENT ------------------------- The OpenSSL memory management calls malloc(), realloc() and free() can be performed by this module using calls associated with dedicated zone "WASD OpenSSL". This can be used for investigative purpose or in production. The OpenSSL CRYPTO_set_mem_function() API is used to enable this. OpenSSL memory management and the DECC emulation are compatible. Compatible with "intercepting DECC calls" but not "OpenSSL memory statistics". INTERCEPTING DECC CALLS ----------------------- The void* VmDecc..() functions are designed to provide a DECC emulation for development and troubleshooting purposes. Defining VM_DECC macro true will enable an overloading of the DECC$CALLOC, DECC$MALLOC, DECC$REALLOC and DECC$FREE functions. These messages (or CPU equivalent) at link time indicate a successful overloading and may safely be ignored. Bit messy but hey. %LINK-W-MULDEF, symbol DECC$REALLOC multiply defined in module DECC$SHR_EV56 file SYS$COMMON:[SYSLIB]DECC$SHR_EV56.EXE;1 %LINK-W-MULDEF, symbol DECC$MALLOC multiply defined in module DECC$SHR_EV56 file SYS$COMMON:[SYSLIB]DECC$SHR_EV56.EXE;1 %LINK-W-MULDEF, symbol DECC$CALLOC multiply defined in module DECC$SHR_EV56 file SYS$COMMON:[SYSLIB]DECC$SHR_EV56.EXE;1 %LINK-W-MULDEF, symbol DECC$FREE multiply defined in module DECC$SHR_EV56 file SYS$COMMON:[SYSLIB]DECC$SHR_EV56.EXE;1 By default the actual memory management is performed by LIB$*VM*() routines with a created LIB$ memory zone. If the macro VM_DECC_LIB_VM macro is true then the memory management is performed by calls to LIB$VM..() calls, a slightly incestuous code path implemented to gather DECC statistics. DECC also seems to free() one or two, perhaps more, right at the beginning of server initialisation, perhaps from DECC initialisation itself. This is detected by the absence of approprite magic on the memory and would normally be considered a fatal error. So this code ignores magic-less memory chunks for a few seconds after server startup. VAX SUPPORT ----------- Is limited to the basic functionality of this module and DOES NOT support using zones for individual non-WASD memory management or statistic gathering and reporting. VERSION HISTORY --------------- 20-JUL-2021 MGD confine VmReport() items to WASD zones, unless logical name WASD_VM_REPORT_ALL is defined 19-NOV-2020 MGD VmGetRequest(), VmFreeHeap() provide "tuned" initial pages (yep, tuning again...) 11-AUG-2020 MGD VmCheckPgFlLimit() and WASD_VM_PGFL_LIMIT logical name 23-JAN-2020 MGD HTTP/2 heap initial and extend pages 128 to 256 26-JUL-2018 MGD here we go again - rework to pull more statistics 14-JUL-2018 MGD Bonne Fête Nationale! eliminate dynamic tuning of heap initial allocation assumes overhead of tuning out-weighs that of area mgmnt (and after a query and some discussion on comp.os.vms) add VmOpenSsl..() routines specifically for OpenSSL add VmDecc..() routines for development and troubleshooting VmReport() add lib$show_vm_zone_64 (perhaps looking ahead) 01-JUL-2018 MGD VmReport() include WATCH link in heap zone data 04-AUG-2015 MGD support HTTP/2 specific memory management add magic allowing various allocations to be differentiated HTTP2_STRUCT and REQUEST_STRUCT sizes via external storage 14-MAR-2011 MGD sizeof(VM_STRUCT) now 8 (natural alignment) instead of 4 04-JUL-2006 MGD use PercentOf32() 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 #include /* application related header files */ #include "wasd.h" #define WASD_MODULE "VM" /* allocation quantum for the zone */ #define VM_BLOCK_BYTES 64 /* Where appropriate allocate an extra few bytes for carriage-control in buffers, null string terminators, programming errors ;^) etc. */ #define VM_ELBOW_ROOM 8 #define VM_GUARD_SIZE sizeof(ulong) /* all overhead when allocating even a single byte of heap */ #define VM_ALLOC_OVERHEAD (sizeof(VM_STRUCT) + VM_ELBOW_ROOM + VM_GUARD_SIZE) /* unconditionally control VM module WATCHing - only for troubleshooting! */ #define WATCHON_WATCHOFF 0 /*****************/ /* GENERAL ZONES */ /*****************/ #define VM_REQUEST_INITIAL_PAGES 256 #define VM_REQUEST_EXTEND_PAGES 256 #define VM_REQUEST_MAX_PAGES 1024 #define VM_REQUEST_MIN_PAGES 32 /* tune after sampling this many requests */ #define VM_REQUEST_TUNE_SAMPLE 128 /* continue to tune for this multiple of the sample */ #define VM_REQUEST_TUNE_PERIOD 32 #define VM_HTTP2_INITIAL_PAGES 256 #define VM_HTTP2_EXTEND_PAGES 256 #define VM_HTTP2_MAX_PAGES 2048 #define VM_HTTP2_MIN_PAGES 128 #define VM_GENERAL_INITIAL_PAGES 4096 #define VM_GENERAL_EXTEND_PAGES 2048 #define VM_GENERAL_MAX_PAGES 16384 #define VM_GENERAL_MIN_PAGES 512 #define VM_CACHE_INITIAL_PAGES 1024 #define VM_CACHE_EXTEND_PAGES 1024 #define VM_CACHE_MAX_PAGES 8192 #define VM_CACHE_MIN_PAGES 256 #define VM_PERMCACHE_INITIAL_PAGES 256 #define VM_PERMCACHE_EXTEND_PAGES 256 #define VM_PERMCACHE_MAX_PAGES 8192 #define VM_PERMCACHE_MIN_PAGES 256 /*******/ #if VM_DECC /*******/ #if VM_OPENSSL /* if OpenSSL being VM managed then DECC needs a lot less */ # define VM_DECC_INITIAL_PAGES 256 # define VM_DECC_EXTEND_PAGES 256 # define VM_DECC_MAX_PAGES 4096 # define VM_DECC_MIN_PAGES 128 #else /* if OpenSSL *not* being managed then DECC needs to take up the slack */ # define VM_DECC_INITIAL_PAGES 16384 # define VM_DECC_EXTEND_PAGES 16384 # define VM_DECC_MAX_PAGES 131072 # define VM_DECC_MIN_PAGES 4096 #endif /* VM_OPENSSL */ # ifndef VM_DECC_LIB_VM # define VM_DECC_LIB_VM 0 # endif /* overload the local routines onto the DECC routines */ # define VmDeccMalloc DECC$MALLOC # define VmDeccCalloc DECC$CALLOC # define VmDeccRealloc DECC$REALLOC # define VmDeccFree DECC$FREE #endif /* VM_DECC */ /********/ #if VM_EXPAT /********/ #define VM_EXPAT_INITIAL_PAGES 256 #define VM_EXPAT_EXTEND_PAGES 256 #define VM_EXPAT_MAX_PAGES 2048 #define VM_EXPAT_MIN_PAGES 128 #endif /* VM_EXPAT */ /**********/ #if VM_OPENSSL /**********/ #define VM_OPENSSL_INITIAL_PAGES 16384 #define VM_OPENSSL_EXTEND_PAGES 16384 #define VM_OPENSSL_MAX_PAGES 131072 #define VM_OPENSSL_MIN_PAGES 4096 #endif /* VM_OPENSSL */ /******************/ /* global storage */ /******************/ static BOOL VmReportPlus, VmReportThisOne; static int VmDeccTickSecond; static VM_ZONE VmCacheZone; static VM_ZONE VmGeneralZone; static VM_ZONE VmHttp2Zone; static VM_ZONE VmPermCacheZone; static VM_ZONE VmRequestZone; static VM_ZONE VmExpatZone; static VM_ZONE VmOpenSslZone; #if VM_DECC static VM_ZONE VmDeccZone; #endif /* VM_DECC */ /********************/ /* external storage */ /********************/ extern BOOL HttpdServerStartup; extern int CacheEntryKBytesMax, CacheTotalKBytesMax, EfnWait, HttpdTickSecond, Http2StructSize, NetConcurrentMax, RequestStructSize; extern char ErrorSanityCheck[], HttpProtocol[], SoftwareID[]; extern CONFIG_STRUCT Config; extern HTTPD_GBLSEC *HttpdGblSecPtr; extern HTTPD_PROCESS HttpdProcess; extern WATCH_STRUCT Watch; /*****************************************************************************/ /* Initialize virtual memory management. */ void VmInit () { /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM) || WATCHON_WATCHOFF) { Watch.Module |= WATCH_MOD_VM; Watch.LogFile = 1; strzcpy (Watch.LogFileName, WATCH_NAME_DEFAULT, sizeof(Watch.LogFileName)); WatchThis (WATCHALL, WATCH_MOD_VM, "VmInit()"); } VmGeneralInit (); } /*****************************************************************************/ /* Allocate memory for an up-call function. Malloc is indicated by a chunk number of -1. */ void* VmGenericCalloc ( VM_ZONE *ZonePtr, int NumChunks, int ChunkSize, char *ModuleName, int LineNumber ) { int status; ulong BaseAddress; uchar *aptr; VM_STRUCT *vmptr; /*********/ /* begin */ /*********/ if (!ZonePtr->ZoneId) ErrorExitVmsStatus (SS$_BUGCHECK, NULL, FI_LI); if (WATCH_MODULE(WATCH_MOD_VM) || WATCHON_WATCHOFF) WatchThis (WATCHALL, WATCH_MOD_VM, "VmGenericCalloc() 0x!8XL !AZ !SL !SL", ZonePtr->ZoneId, ZonePtr->ReportTitle, NumChunks, ChunkSize); if (!ChunkSize) return (NULL); if (NumChunks > 1) ChunkSize *= NumChunks; ChunkSize += VM_ALLOC_OVERHEAD; if (ZonePtr->ZoneId == (ulong)-1) { vmptr = (VM_STRUCT*)LIB$VM_CALLOC (1, ChunkSize); if (!vmptr) ErrorExitVmsStatus (SS$_BUGCHECK, "lib$vm_calloc()", FI_LI); } else { status = lib$get_vm (&ChunkSize, &BaseAddress, &ZonePtr->ZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$get_vm()", FI_LI); vmptr = (VM_STRUCT*)BaseAddress; } ChunkSize -= VM_ALLOC_OVERHEAD; vmptr->size = ChunkSize; vmptr->magic = ZonePtr->MagicValue; /* guard is placed at the end of the memory chunk to detect overflow */ aptr = (uchar*)vmptr + (sizeof(VM_STRUCT) + VM_ELBOW_ROOM) + ChunkSize; *(ULONGPTR)aptr = VM_GUARD; if (NumChunks < 0) ZonePtr->MallocCount++; else ZonePtr->CallocCount++; ZonePtr->AllocBytes += ChunkSize; ZonePtr->InUseBytes = ZonePtr->AllocBytes - ZonePtr->DeallocBytes; if (ZonePtr->InUseBytes > ZonePtr->InUseMaxBytes) ZonePtr->InUseMaxBytes = ZonePtr->InUseBytes; if (WATCH_MODULE(WATCH_MOD_VM) || WATCHON_WATCHOFF) WatchThis (WATCHALL, WATCH_MOD_VM, "!UL !UL !8XL !8XL", vmptr, vmptr->size, vmptr->magic, *(ULONGPTR)aptr); return ((uchar*)vmptr + sizeof(VM_STRUCT)); } /*****************************************************************************/ /* Expand generic chunk. See VmGenericCalloc(). */ void* VmGenericRealloc ( VM_ZONE *ZonePtr, uchar *ChunkPtr, int ChunkSize, char *ModuleName, int LineNumber ) { int status; ulong BaseAddress, TheBaseAddress, TheChunkSize; uchar *aptr; VM_STRUCT *vmptr; /*********/ /* begin */ /*********/ if (!ZonePtr->ZoneId) ErrorExitVmsStatus (SS$_BUGCHECK, NULL, FI_LI); if (ChunkPtr) TheChunkSize = ((VM_STRUCT*)(ChunkPtr-sizeof(VM_STRUCT)))->size; else TheChunkSize = -1; if (WATCH_MODULE(WATCH_MOD_VM) || WATCHON_WATCHOFF) WatchThis (WATCHALL, WATCH_MOD_VM, "VmGenericRealloc() 0x!8XL !AZ !SL !SL !AZ:!UL", ZonePtr->ZoneId, ZonePtr->ReportTitle, TheChunkSize, ChunkSize, ModuleName, LineNumber); if (!ChunkPtr && ChunkSize) return (VmGenericCalloc (ZonePtr, -1, ChunkSize, ModuleName, LineNumber)); if (ChunkPtr && !ChunkSize) { VmGenericFree (ZonePtr, ChunkPtr, ModuleName, LineNumber); return (NULL); } if (!ChunkSize) return (NULL); vmptr = (VM_STRUCT*)(TheBaseAddress = ChunkPtr - sizeof(VM_STRUCT)); aptr = (uchar*)vmptr + (sizeof(VM_STRUCT) + VM_ELBOW_ROOM) + TheChunkSize; if (WATCH_MODULE(WATCH_MOD_VM) || WATCHON_WATCHOFF) WatchThis (WATCHALL, WATCH_MOD_VM, "!UL !UL !8XL !8XL", vmptr, vmptr->size, vmptr->magic, *(ULONGPTR)aptr); if (vmptr->magic != ZonePtr->MagicValue) ErrorExitVmsStatus (SS$_BUGCHECK, "magic", ModuleName, LineNumber); if (*(ULONGPTR)aptr != VM_GUARD) ErrorExitVmsStatus (SS$_BUGCHECK, "guard", ModuleName, LineNumber); /* if this chunk satisfies the reallocation then just return it */ TheChunkSize = ((VM_STRUCT*)TheBaseAddress)->size; if (TheChunkSize >= ChunkSize) return (ChunkPtr); /* allocate a new, larger chunk */ ChunkSize += VM_ALLOC_OVERHEAD; if (ZonePtr->ZoneId == (ulong)-1) { vmptr = (VM_STRUCT*)LIB$VM_CALLOC (1, ChunkSize); if (!vmptr) return (NULL); } else { status = lib$get_vm (&ChunkSize, &BaseAddress, &ZonePtr->ZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$get_vm()", FI_LI); vmptr = (VM_STRUCT*)BaseAddress; } ChunkSize -= VM_ALLOC_OVERHEAD; /* copy the existing chunk into the new chunk */ if (TheChunkSize > 0) memcpy (vmptr, (uchar*)TheBaseAddress, TheChunkSize + (sizeof(VM_STRUCT) + VM_ELBOW_ROOM)); aptr = (uchar*)vmptr + (sizeof(VM_STRUCT) + VM_ELBOW_ROOM) + ChunkSize; *(ULONGPTR)aptr = VM_GUARD; /* update the chunk size */ vmptr->size = ChunkSize; /* this is the GROWTH in memory due to reallocation */ ZonePtr->ReallocCount++; ZonePtr->AllocBytes += ChunkSize - TheChunkSize; ZonePtr->InUseBytes = ZonePtr->AllocBytes - ZonePtr->DeallocBytes; if (ZonePtr->InUseBytes > ZonePtr->InUseMaxBytes) ZonePtr->InUseMaxBytes = ZonePtr->InUseBytes; /* free the previous chunk */ if (ZonePtr->ZoneId == (ulong)-1) LIB$VM_FREE ((void*)TheBaseAddress); else { status = lib$free_vm (0, &TheBaseAddress, &ZonePtr->ZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$free_vm()", ModuleName, LineNumber); } if (WATCH_MODULE(WATCH_MOD_VM) || WATCHON_WATCHOFF) WatchThis (WATCHALL, WATCH_MOD_VM, "!UL !UL !8XL !8XL", vmptr, vmptr->size, vmptr->magic, *(ULONGPTR)aptr); return ((uchar*)vmptr + sizeof(VM_STRUCT)); } /*****************************************************************************/ /* Release memory allocated for generic use. */ void VmGenericFree ( VM_ZONE *ZonePtr, uchar *ChunkPtr, char *ModuleName, int LineNumber ) { int status, ChunkSize; ulong BaseAddress; uchar *aptr; VM_STRUCT *vmptr; /*********/ /* begin */ /*********/ if (!ZonePtr->ZoneId) ErrorExitVmsStatus (SS$_BUGCHECK, NULL, FI_LI); if (ChunkPtr) ChunkSize = ((VM_STRUCT*)(ChunkPtr-sizeof(VM_STRUCT)))->size; else ChunkSize = 0; if (WATCH_MODULE(WATCH_MOD_VM) || WATCHON_WATCHOFF) WatchThis (WATCHALL, WATCH_MOD_VM, "VmGenericFree() 0x!8XL !AZ !SL !AZ:!UL", ZonePtr->ZoneId, ZonePtr->ReportTitle, ChunkSize, ModuleName, LineNumber); if (!ChunkPtr) return; vmptr = (VM_STRUCT*)(BaseAddress = ChunkPtr - sizeof(VM_STRUCT)); aptr = (uchar*)vmptr + (sizeof(VM_STRUCT) + VM_ELBOW_ROOM) + ChunkSize; if (WATCH_MODULE(WATCH_MOD_VM) || WATCHON_WATCHOFF) WatchThis (WATCHALL, WATCH_MOD_VM, "!UL !UL !8XL !8XL", vmptr, vmptr->size, vmptr->magic, *(ULONGPTR)aptr); /* see module prologue for explanation */ if (vmptr->magic != ZonePtr->MagicValue) ErrorExitVmsStatus (SS$_BUGCHECK, "magic", ModuleName, LineNumber); if (*(ULONGPTR)aptr != VM_GUARD) ErrorExitVmsStatus (SS$_BUGCHECK, "guard", ModuleName, LineNumber); ZonePtr->FreeCount++; ZonePtr->DeallocBytes += ChunkSize; ZonePtr->InUseBytes = ZonePtr->AllocBytes - ZonePtr->DeallocBytes; if (ZonePtr->InUseBytes > ZonePtr->InUseMaxBytes) ZonePtr->InUseMaxBytes = ZonePtr->InUseBytes; if (ZonePtr->ZoneId == (ulong)-1) LIB$VM_FREE ((void*)BaseAddress); else { status = lib$free_vm (0, &BaseAddress, &ZonePtr->ZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$free_vm()", ModuleName, LineNumber); } } /*****************************************************************************/ /* Initialize the virtual memory zone general memory will be allocated from. Also take parameters from logical names described above if present. */ void VmGeneralInit () { static $DESCRIPTOR (ZoneNameDsc, "WASD General"); static ulong Algorithm = LIB$K_VM_QUICK_FIT, AlgorithmArg = 64, Flags = LIB$M_VM_BOUNDARY_TAGS | LIB$M_VM_GET_FILL0 | LIB$M_VM_TAIL_LARGE, ExtendPages = 4096, InitialPages = 4096, BlockBytes = VM_BLOCK_BYTES; int status; char *cptr; VM_ZONE *vzptr = &VmGeneralZone; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM) || WATCHON_WATCHOFF) WatchThis (WATCHALL, WATCH_MOD_VM, "VmGeneralInit()"); vzptr->MagicValue = VM_MAGIC_GENERAL; vzptr->MaxPages = VM_GENERAL_MAX_PAGES; vzptr->MinPages = VM_GENERAL_MIN_PAGES; vzptr->InitialPages = VM_GENERAL_INITIAL_PAGES; vzptr->ExtendPages = VM_GENERAL_EXTEND_PAGES; vzptr->LogicalName = WASD_VM_GENERAL; vzptr->ReportTitle = "General"; VmSetPages (vzptr); /* create the general virtual memory zone */ status = lib$create_vm_zone (&vzptr->ZoneId, &Algorithm, &AlgorithmArg, &Flags, &vzptr->ExtendPages, &vzptr->InitialPages, &BlockBytes, 0, 0, 0, &ZoneNameDsc, 0, 0); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$create_vm_zone()", FI_LI); } /*****************************************************************************/ /* Allocate memory for general use. */ uchar* VmGet (ulong ChunkSize) { int status; ulong BaseAddress; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM) || WATCHON_WATCHOFF) WatchThis (WATCHALL, WATCH_MOD_VM, "VmGet() !UL", ChunkSize); return (VmGenericCalloc (&VmGeneralZone, 1, ChunkSize, FI_LI)); } /*****************************************************************************/ /* Expand (or even contract) an individual a general-use chunk. See VmGet(). */ uchar* VmRealloc ( uchar *ChunkPtr, ulong ChunkSize, char *ModuleName, int LineNumber ) { /*********/ /* begin */ /*********/ return (VmGenericRealloc (&VmGeneralZone, ChunkPtr, ChunkSize, ModuleName, LineNumber)); } /*****************************************************************************/ /* Release memory allocated for general use. */ void VmFree ( uchar *ChunkPtr, char *ModuleName, int LineNumber ) { /*********/ /* begin */ /*********/ VmGenericFree (&VmGeneralZone, ChunkPtr, ModuleName, LineNumber); } /*****************************************************************************/ /* Initialize the virtual memory zone for the request structures. */ void VmRequestInit () { static $DESCRIPTOR (ZoneNameDsc, "WASD Request Pool"); static ulong Algorithm = LIB$K_VM_QUICK_FIT, AlgorithmArg = 32, Flags = LIB$M_VM_BOUNDARY_TAGS | LIB$M_VM_GET_FILL0 | LIB$M_VM_TAIL_LARGE, BlockBytes = VM_BLOCK_BYTES; int status; ulong ExtendPages, InitialPages; char *cptr; VM_ZONE *vzptr = &VmRequestZone; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM) || WATCHON_WATCHOFF) WatchThis (WATCHALL, WATCH_MOD_VM, "VmRequestInit() !UL", NetConcurrentMax); /* not used in this function - used in VmGetRequest() */ vzptr->MagicValue = 0; vzptr->MaxPages = VM_REQUEST_MAX_PAGES; vzptr->MinPages = VM_REQUEST_MIN_PAGES; vzptr->InitialPages = VM_REQUEST_INITIAL_PAGES; vzptr->ExtendPages = VM_REQUEST_EXTEND_PAGES; vzptr->TuneSample = VM_REQUEST_TUNE_SAMPLE; vzptr->TunePeriod = VM_REQUEST_TUNE_PERIOD; vzptr->LogicalName = WASD_VM_REQUEST; vzptr->ReportTitle = "Request"; VmSetPages (vzptr); ExtendPages = InitialPages = ((NetConcurrentMax * RequestStructSize) / 512) + 1; /* create the virtual memory zone for the request pool */ status = lib$create_vm_zone (&vzptr->ZoneId, &Algorithm, &AlgorithmArg, &Flags, &ExtendPages, &InitialPages, &BlockBytes, 0, 0, 0, &ZoneNameDsc, 0, 0); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$create_vm_zone()", FI_LI); FaoToStdout ("%HTTPD-I-VM, request zone initialised\n"); } /*****************************************************************************/ /* Allocate a request structure with associated virtual memory zone ready for heap allocation. */ REQUEST_STRUCT* VmGetRequest (int ConnectNumber) { static $DESCRIPTOR (ProctorZoneNameFaoDsc, "WASD Proctor Heap !UL"); static $DESCRIPTOR (RequestZoneNameFaoDsc, "WASD Request Heap " "!-!UL"); static ulong Algorithm = LIB$K_VM_QUICK_FIT, AlgorithmArg = 16, Flags = LIB$M_VM_BOUNDARY_TAGS | LIB$M_VM_GET_FILL0 | LIB$M_VM_TAIL_LARGE, BlockBytes = VM_BLOCK_BYTES; int status; ushort slen; ulong BaseAddress, InitialPages; char ZoneName [128]; REQUEST_STRUCT *rqptr; $DESCRIPTOR (ZoneNameDsc, ZoneName); /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM) || WATCHON_WATCHOFF) WatchThis (WATCHALL, WATCH_MOD_VM, "VmGetRequest()"); if (ConnectNumber > 0) status = sys$fao (&RequestZoneNameFaoDsc, &slen, &ZoneNameDsc, ADMIN_REPORT_WATCH, ConnectNumber); else status = sys$fao (&ProctorZoneNameFaoDsc, &slen, &ZoneNameDsc, -(ConnectNumber)); if (VMSnok (status)) ErrorExitVmsStatus (status, "sys$fao()", FI_LI); ZoneName[ZoneNameDsc.dsc$w_length = slen] = '\0'; status = lib$get_vm (&RequestStructSize, &BaseAddress, &VmRequestZone.ZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$get_vm()", FI_LI); rqptr = (REQUEST_STRUCT*)BaseAddress; rqptr->ConnectNumber = ConnectNumber; if (!(InitialPages = VmRequestZone.StatInitial)) InitialPages = VmRequestZone.InitialPages; /* now create a virtual memory zone for the request's heap */ rqptr->VmHeapZoneId = 0; status = lib$create_vm_zone (&rqptr->VmHeapZoneId, &Algorithm, &AlgorithmArg, &Flags, &VmRequestZone.ExtendPages, &InitialPages, &BlockBytes, 0, 0, 0, &ZoneNameDsc, 0, 0); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$create_vm_zone()", FI_LI); return (rqptr); } /*****************************************************************************/ /* Delete any virtual memory zone created for the request's heap, then return the request structure to the request virtual memory pool. */ void VmFreeRequest ( REQUEST_STRUCT *rqptr, char *ModuleName, int LineNumber ) { static ulong PrevByteCount; int status; ulong PageCount; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_VM)) WatchThis (WATCHITM(rqptr), WATCH_MOD_VM, "VmFreeRequest()"); 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, &VmRequestZone.ZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "VmFreeRequest() lib$free_vm()", ModuleName, LineNumber); } /*****************************************************************************/ /* Allocate dynamic memory to an individual request's heap. Return a pointer to the start of new *usable* memory area if successful, explode if not. */ uchar* VmGetHeap ( REQUEST_STRUCT *rqptr, ulong ChunkSize ) { int status; ulong BaseAddress; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_VM)) WatchThis (WATCHITM(rqptr), WATCH_MOD_VM, "VmGetHeap() !UL", ChunkSize); ChunkSize += VM_ALLOC_OVERHEAD; status = lib$get_vm (&ChunkSize, &BaseAddress, &rqptr->VmHeapZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$get_vm()", FI_LI); ChunkSize -= VM_ALLOC_OVERHEAD; ((VM_STRUCT*)BaseAddress)->size = ChunkSize; ((VM_STRUCT*)BaseAddress)->magic = VM_MAGIC_REQUEST; return ((uchar*)BaseAddress + sizeof(VM_STRUCT)); } /*****************************************************************************/ /* Expand (or even contract) an individual chunk. See VmGetHeap(). */ uchar* VmReallocHeap ( REQUEST_STRUCT *rqptr, uchar *ChunkPtr, ulong ChunkSize, char *ModuleName, int LineNumber ) { int status; ulong BaseAddress, TheBaseAddress, TheChunkSize; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_VM)) { if (ChunkPtr) TheChunkSize = ((VM_STRUCT*)(ChunkPtr-sizeof(VM_STRUCT)))->size; else TheChunkSize = 0; WatchThis (WATCHITM(rqptr), WATCH_MOD_VM, "VmReallocHeap() !UL !UL", TheChunkSize, ChunkSize); } if (!ChunkPtr) return (VmGetHeap (rqptr, ChunkSize)); TheBaseAddress = ChunkPtr - sizeof(VM_STRUCT); if (((VM_STRUCT*)TheBaseAddress)->magic != VM_MAGIC_REQUEST) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, ModuleName, LineNumber); /* if this chunk satisfies the reallocation then just return it */ TheChunkSize = ((VM_STRUCT*)TheBaseAddress)->size; if (TheChunkSize >= ChunkSize) return (ChunkPtr); /* allocate a new, larger chunk */ ChunkSize += VM_ALLOC_OVERHEAD; status = lib$get_vm (&ChunkSize, &BaseAddress, &rqptr->VmHeapZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "VmReallocHeap() lib$get_vm()", ModuleName, LineNumber); ChunkSize -= VM_ALLOC_OVERHEAD; /* copy the existing chunk into the new chunk */ if (TheChunkSize > 0) memcpy ((uchar*)BaseAddress, (uchar*)TheBaseAddress, TheChunkSize + VM_ALLOC_OVERHEAD); /* update the chunk size */ ((VM_STRUCT*)BaseAddress)->size = ChunkSize; /* free the previous chunk */ status = lib$free_vm (0, &TheBaseAddress, &rqptr->VmHeapZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "VmReallocHeap() lib$free_vm()", ModuleName, LineNumber); return ((uchar*)BaseAddress+sizeof(VM_STRUCT)); } /*****************************************************************************/ /* Release back into the virtual memory zone one chunk of request heap memory. */ void VmFreeFromHeap ( REQUEST_STRUCT *rqptr, uchar *ChunkPtr, char *SourceModuleName, int SourceLineNumber ) { int status, ChunkSize; ulong BaseAddress; /*********/ /* begin */ /*********/ BaseAddress = ChunkPtr - sizeof(VM_STRUCT); ChunkSize = ((VM_STRUCT*)(BaseAddress))->size; if (WATCHMOD (rqptr, WATCH_MOD_VM)) WatchThis (WATCHITM(rqptr), WATCH_MOD_VM, "VmFreeFromHeap() !UL", ChunkSize); if (((VM_STRUCT*)BaseAddress)->magic != VM_MAGIC_REQUEST) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, SourceModuleName, SourceLineNumber); status = lib$free_vm (0, &BaseAddress, &rqptr->VmHeapZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "VmFreeFromHeap() lib$free_vm()", SourceModuleName, SourceLineNumber); } /*****************************************************************************/ /* Release back into the virtual memory zone the individually allocated chunks of memory (the zone is deleted on request structure release). */ void VmFreeHeap ( REQUEST_STRUCT *rqptr, char *SourceModuleName, int SourceLineNumber ) { static ulong ShowVmZoneDetail = 1; int status; ulong pages; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_VM)) WatchThis (WATCHITM(rqptr), WATCH_MOD_VM, "VmFreeHeap()"); if (VmRequestZone.TuneSample) { /********/ /* tune */ /********/ status = lib$show_vm_zone (&rqptr->VmHeapZoneId, &ShowVmZoneDetail, &VmFreeHeapStat, rqptr); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); if (rqptr->VmHeapStatPages > 0) { VmRequestZone.StatCount++; VmRequestZone.StatPages += rqptr->VmHeapStatPages; if (VmRequestZone.StatCount > VmRequestZone.TuneSample) { /* after an initial sample of request pages */ VmRequestZone.StatInitial = VmRequestZone.StatPages / VmRequestZone.StatCount; VmRequestZone.StatInitial /= VmRequestZone.InitialPages; VmRequestZone.StatInitial *= VmRequestZone.InitialPages; if (VmRequestZone.StatInitial > VmRequestZone.MaxPages) VmRequestZone.StatInitial = VmRequestZone.MaxPages; /* reset the tuning process after a given number of requests */ if (VmRequestZone.StatCount > VmRequestZone.TuneSample * VmRequestZone.TunePeriod) VmRequestZone.StatCount = VmRequestZone.StatPages = VmRequestZone.StatInitial = 0; } rqptr->VmHeapStatPages = 0; } } /********/ /* free */ /********/ /* well - reset, if we must be pedantic */ status = lib$reset_vm_zone (&rqptr->VmHeapZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "VmFreeHeap() lib$reset_vm_zone()", SourceModuleName, SourceLineNumber); } /*****************************************************************************/ /* Parse the output from lib$show_vm_zone() to determine how many pages were allocated to the request heap. This value can then be used to optimise request heap initial allocation. Kludgy but there is (still) no lib$stat_vm_zone(). */ int VmFreeHeapStat ( struct dsc$descriptor *DscPtr, REQUEST_STRUCT *rqptr ) { char *cptr, *czptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM) || WATCHON_WATCHOFF) WatchThis (WATCHALL, WATCH_MOD_VM, "VmFreeHeapStat() !UL !-!#AZ", DscPtr->dsc$w_length, DscPtr->dsc$a_pointer); if (rqptr->VmHeapStatPages || DscPtr->dsc$w_length < 60 || DscPtr->dsc$w_length > 90) return (SS$_NORMAL); for (czptr = (cptr = DscPtr->dsc$a_pointer) + DscPtr->dsc$w_length; cptr < czptr; cptr++) if (*cptr == 'I' && MATCH12(cptr,"Initial size")) break; if (cptr < czptr) for (cptr += 12; cptr < czptr; cptr++) if (*cptr == 'C' && MATCH12(cptr,"Current size")) break; if (cptr < czptr) { cptr += 15; rqptr->VmHeapStatPages = atol(cptr); if (WATCH_MODULE(WATCH_MOD_VM) || WATCHON_WATCHOFF) WatchThis (WATCHALL, WATCH_MOD_VM, "!SL", rqptr->VmHeapStatPages); } return (SS$_NORMAL); } /*****************************************************************************/ /* Initialize the virtual memory zone for the pool of HTTP/2 structures. */ void VmHttp2Init () { static $DESCRIPTOR (ZoneNameDsc, "WASD HTTP/2 Pool"); static ulong Algorithm = LIB$K_VM_QUICK_FIT, AlgorithmArg = 32, Flags = LIB$M_VM_BOUNDARY_TAGS | LIB$M_VM_GET_FILL0 | LIB$M_VM_TAIL_LARGE, BlockBytes = VM_BLOCK_BYTES; int status; ulong ExtendPages, InitialPages; char *cptr; VM_ZONE *vzptr = &VmHttp2Zone; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM) || WATCHON_WATCHOFF) WatchThis (WATCHALL, WATCH_MOD_VM, "VmHttp2Init() !UL", NetConcurrentMax); /* not used in this function - used in VmHttp2Get() */ vzptr->MagicValue = VM_MAGIC_HTTP2; vzptr->MaxPages = VM_HTTP2_MAX_PAGES; vzptr->MinPages = VM_HTTP2_MIN_PAGES; vzptr->InitialPages = VM_HTTP2_INITIAL_PAGES; vzptr->ExtendPages = VM_HTTP2_EXTEND_PAGES; vzptr->LogicalName = WASD_VM_HTTP2; vzptr->ReportTitle = "HTTP/2"; VmSetPages (vzptr); ExtendPages = InitialPages = (NetConcurrentMax / 10) * 16; /* create the HTTP/2 virtual memory zone */ status = lib$create_vm_zone (&vzptr->ZoneId, &Algorithm, &AlgorithmArg, &Flags, &ExtendPages, &InitialPages, &BlockBytes, 0, 0, 0, &ZoneNameDsc, 0, 0); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$create_vm_zone()", FI_LI); FaoToStdout ("%HTTPD-I-VM, HTTP/2 zone initialised\n"); } /*****************************************************************************/ /* Allocate an HTTP/2 structure with associated virtual memory zone ready for HTTP/2 heap allocation. This is used almost exclusively for write buffers. */ HTTP2_STRUCT* VmHttp2Get (int ConnectNumber) { static $DESCRIPTOR (ZoneNameFaoDsc, "WASD HTTP/2 Heap !-!UL"); static ulong Algorithm = LIB$K_VM_QUICK_FIT, AlgorithmArg = 16, Flags = LIB$M_VM_BOUNDARY_TAGS | LIB$M_VM_GET_FILL0 | LIB$M_VM_TAIL_LARGE, BlockBytes = VM_BLOCK_BYTES; int status; ushort slen; ulong BaseAddress; char ZoneName [128]; HTTP2_STRUCT *h2ptr; $DESCRIPTOR (ZoneNameDsc, ZoneName); /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM) || WATCHON_WATCHOFF) WatchThis (WATCHALL, WATCH_MOD_VM, "VmHttp2Get()"); status = sys$fao (&ZoneNameFaoDsc, &slen, &ZoneNameDsc, ADMIN_REPORT_WATCH, ConnectNumber); if (VMSnok (status)) ErrorExitVmsStatus (status, "sys$fao()", FI_LI); ZoneName[ZoneNameDsc.dsc$w_length = slen] = '\0'; status = lib$get_vm (&Http2StructSize, &BaseAddress, &VmHttp2Zone.ZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$get_vm()", FI_LI); h2ptr = (HTTP2_STRUCT*)BaseAddress; h2ptr->ConnectNumber = ConnectNumber; /* now create a virtual memory zone for the request's heap */ h2ptr->VmHeapZoneId = 0; status = lib$create_vm_zone (&h2ptr->VmHeapZoneId, &Algorithm, &AlgorithmArg, &Flags, &VmHttp2Zone.ExtendPages, &VmHttp2Zone.InitialPages, &BlockBytes, 0, 0, 0, &ZoneNameDsc, 0, 0); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$create_vm_zone()", FI_LI); return (h2ptr); } /*****************************************************************************/ /* Delete any virtual memory zone created for the HTTP/2 heap, then return the HTTP/2 structure to the request virtual memory pool. */ void VmHttp2Free ( HTTP2_STRUCT *h2ptr, char *SourceModuleName, int SourceLineNumber ) { static ulong PrevByteCount; int status; ulong PageCount; /*********/ /* begin */ /*********/ if (WATCHMOD (h2ptr, WATCH_MOD_VM)) WatchThis (WATCHITM(h2ptr), WATCH_MOD_VM, "VmHttp2Free()"); if (h2ptr->VmHeapZoneId) { /* delete the request's heap's virtual memory zone */ status = lib$delete_vm_zone (&h2ptr->VmHeapZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$delete_vm_zone()", FI_LI); } status = lib$free_vm (0, &h2ptr, &VmHttp2Zone.ZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "VmHttp2Free() lib$free_vm()", SourceModuleName, SourceLineNumber); } /*****************************************************************************/ /* Allocate dynamic memory to an individual HTTP/2 heap. Return a pointer to the start of new *usable* memory area if successful, explode if not. */ uchar* VmGet2Heap ( HTTP2_STRUCT *h2ptr, ulong ChunkSize ) { int status; ulong BaseAddress; /*********/ /* begin */ /*********/ if (WATCHMOD (h2ptr, WATCH_MOD_VM)) WatchThis (WATCHITM(h2ptr), WATCH_MOD_VM, "VmGet2Heap() !UL", ChunkSize); ChunkSize += VM_ALLOC_OVERHEAD; status = lib$get_vm (&ChunkSize, &BaseAddress, &h2ptr->VmHeapZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$get_vm()", FI_LI); ChunkSize -= VM_ALLOC_OVERHEAD; ((VM_STRUCT*)BaseAddress)->size = ChunkSize; ((VM_STRUCT*)BaseAddress)->magic = VM_MAGIC_HTTP2; return ((uchar*)BaseAddress+sizeof(VM_STRUCT)); } /*****************************************************************************/ /* Release back into the virtual memory zone one chunk of HTTP/2 heap memory. */ void VmFreeFrom2Heap ( HTTP2_STRUCT *h2ptr, uchar *ChunkPtr, char *SourceModuleName, int SourceLineNumber ) { int status, ChunkSize; ulong BaseAddress; /*********/ /* begin */ /*********/ BaseAddress = ChunkPtr - sizeof(VM_STRUCT); ChunkSize = ((VM_STRUCT*)(BaseAddress))->size; if (WATCHMOD (h2ptr, WATCH_MOD_VM)) WatchThis (WATCHITM(h2ptr), WATCH_MOD_VM, "VmFreeFrom2Heap() !UL", ChunkSize); if (((VM_STRUCT*)BaseAddress)->magic != VM_MAGIC_HTTP2) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, SourceModuleName, SourceLineNumber); status = lib$free_vm (0, &BaseAddress, &h2ptr->VmHeapZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "VmFreeFrom2Heap() lib$free_vm()", SourceModuleName, SourceLineNumber); } /*****************************************************************************/ /* Release back into the HTTP/2 virtual memory zone the individually allocated chunks of memory (the zone is deleted on request structure release). */ void VmFree2Heap ( HTTP2_STRUCT *h2ptr, char *SourceModuleName, int SourceLineNumber ) { int status; /*********/ /* begin */ /*********/ if (WATCHMOD (h2ptr, WATCH_MOD_VM)) WatchThis (WATCHITM(h2ptr), WATCH_MOD_VM, "VmFree2Heap()"); status = lib$reset_vm_zone (&h2ptr->VmHeapZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "VmFree2Heap() lib$reset_vm_zone()", SourceModuleName, SourceLineNumber); } /*****************************************************************************/ /* Initialize the virtual memory zone cache memory will be allocated from. */ void VmCacheInit (int TotalKBytesMax) { static $DESCRIPTOR (ZoneNameDsc, "WASD Cache"); static ulong Algorithm = LIB$K_VM_QUICK_FIT, AlgorithmArg = 128, Flags = LIB$M_VM_BOUNDARY_TAGS | LIB$M_VM_GET_FILL0 | LIB$M_VM_TAIL_LARGE, ExtendPages, InitialPages, BlockBytes = VM_BLOCK_BYTES; int status; VM_ZONE *vzptr = &VmCacheZone; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM) || WATCHON_WATCHOFF) WatchThis (WATCHALL, WATCH_MOD_VM, "VmCacheInit() !UL", TotalKBytesMax); vzptr->MagicValue = VM_MAGIC_CACHE; vzptr->MaxPages = VM_CACHE_MAX_PAGES; vzptr->MinPages = VM_CACHE_MIN_PAGES; vzptr->InitialPages = VM_CACHE_INITIAL_PAGES; vzptr->ExtendPages = VM_CACHE_EXTEND_PAGES; vzptr->LogicalName = WASD_VM_CACHE; vzptr->ReportTitle = "Cache"; VmSetPages (vzptr); if ((InitialPages = TotalKBytesMax * 2) <= 0) InitialPages = 32; ExtendPages = InitialPages / 2; /* create the cache virtual memory zone */ status = lib$create_vm_zone (&vzptr->ZoneId, &Algorithm, &AlgorithmArg, &Flags, &ExtendPages, &InitialPages, &BlockBytes, 0, 0, 0, &ZoneNameDsc, 0, 0); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$create_vm_zone()", FI_LI); FaoToStdout ("%HTTPD-I-VM, cache zone initialised\n"); } /*****************************************************************************/ /* */ uchar* VmGetCache (ulong ChunkSize) { /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM) || WATCHON_WATCHOFF) WatchThis (WATCHALL, WATCH_MOD_VM, "VmGetCache() !UL", ChunkSize); if (!VmCacheZone.ZoneId) VmCacheInit (CacheTotalKBytesMax); return (VmGenericCalloc (&VmCacheZone, 1, ChunkSize, FI_LI)); } /*****************************************************************************/ /* Release back into the virtual memory zone the individually allocated chunks of cache memory. */ void VmFreeCache ( uchar *ChunkPtr, char *ModuleName, int LineNumber ) { /*********/ /* begin */ /*********/ VmGenericFree (&VmCacheZone, ChunkPtr, ModuleName, LineNumber); } /*****************************************************************************/ /* Initialize the virtual memory zone permanent cache memory will be allocated from. */ void VmPermCacheInit (int TotalKBytesMax) { static $DESCRIPTOR (ZoneNameDsc, "WASD Perm-Cache"); static ulong Algorithm = LIB$K_VM_QUICK_FIT, AlgorithmArg = 128, Flags = LIB$M_VM_BOUNDARY_TAGS | LIB$M_VM_GET_FILL0 | LIB$M_VM_TAIL_LARGE, ExtendPages, InitialPages, BlockBytes = VM_BLOCK_BYTES; int status; VM_ZONE *vzptr = &VmPermCacheZone; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM) || WATCHON_WATCHOFF) WatchThis (WATCHALL, WATCH_MOD_VM, "VmPermCacheInit() !UL", TotalKBytesMax); vzptr->MagicValue = VM_MAGIC_PERMCACHE; vzptr->MaxPages = VM_PERMCACHE_MAX_PAGES; vzptr->MinPages = VM_PERMCACHE_MIN_PAGES; vzptr->InitialPages = VM_PERMCACHE_INITIAL_PAGES; vzptr->ExtendPages = VM_PERMCACHE_EXTEND_PAGES; vzptr->LogicalName = WASD_VM_PERMCACHE; vzptr->ReportTitle = "Permanent Cache"; VmSetPages (vzptr); if ((InitialPages = TotalKBytesMax * 2) <= 0) InitialPages = 32; ExtendPages = InitialPages / 2; /* create the cache virtual memory zone */ status = lib$create_vm_zone (&vzptr->ZoneId, &Algorithm, &AlgorithmArg, &Flags, &ExtendPages, &InitialPages, &BlockBytes, 0, 0, 0, &ZoneNameDsc, 0, 0); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$create_vm_zone()", FI_LI); FaoToStdout ("%HTTPD-I-VM, permanent cache zone initialised\n"); } /*****************************************************************************/ /* */ uchar* VmGetPermCache (ulong ChunkSize) { int status; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM) || WATCHON_WATCHOFF) WatchThis (WATCHALL, WATCH_MOD_VM, "VmGetPermCache() !UL", ChunkSize); if (!VmPermCacheZone.ZoneId) VmPermCacheInit (CacheTotalKBytesMax); return (VmGenericCalloc (&VmPermCacheZone, 1, ChunkSize, FI_LI)); } /*****************************************************************************/ /* Release back into the virtual memory zone the individually allocated chunks of permanent cache memory. */ void VmFreePermCache ( uchar *ChunkPtr, char *ModuleName, int LineNumber ) { /*********/ /* begin */ /*********/ VmGenericFree (&VmPermCacheZone, ChunkPtr, ModuleName, LineNumber); } /*****************************************************************************/ /* Once a minute HttpdTick() calls this function to check how much page file (and hence virtual memory) has been consumed. When the default limit has been exceeded the server proactively exits with an explanatory message. The logical name WASD_VM_PGFL_LIMIT defined as an integer 20..99 can change the default percentage. The rationale is that it's better to elegantly exit for a known reason than spit somewhere less obvious because memory wasn't available. */ void VmCheckPgFlLimit () { static ulong JpiPagFilCnt, JpiPgFlQuo; static VMS_ITEM_LIST3 JpiItems [] = { { sizeof(JpiPagFilCnt), JPI$_PAGFILCNT, &JpiPagFilCnt, 0 }, { sizeof(JpiPgFlQuo), JPI$_PGFLQUOTA, &JpiPgFlQuo, 0 }, { 0,0,0,0 } }; int limit, percent, status; char *cptr; char buf [64]; IO_SB IOsb; /*********/ /* begin */ /*********/ limit = VM_DEFAULT_PGFL_LIMIT; if (cptr = SysTrnLnm (WASD_VM_PGFL_LIMIT)) { limit = atoi(cptr); if (limit < 20 || limit > 99) limit = VM_DEFAULT_PGFL_LIMIT; } status = sys$getjpiw (EfnWait, 0, 0, &JpiItems, &IOsb, 0, 0); if (VMSok (status)) status = IOsb.Status; if (VMSnok (status)) ErrorExitVmsStatus (status, NULL, FI_LI); percent = 0; if (JpiPagFilCnt) percent = 100 - PercentOf32(JpiPagFilCnt, JpiPgFlQuo); if (percent > limit) { sprintf (buf, "current page file usage %d%% exceeds %d%% limit", percent, limit); ErrorExitVmsStatus (SS$_ABORT, buf, FI_LI); } } /*****************************************************************************/ /* Initialize the virtual memory zone DECC malloc(), calloc(), realloc() (and free()) memory will be allocated from. */ #if VM_DECC void VmDeccInit () { static $DESCRIPTOR (ZoneNameDsc, "WASD DECC"); static ulong Algorithm = LIB$K_VM_QUICK_FIT, AlgorithmArg = 64, Flags = LIB$M_VM_BOUNDARY_TAGS | LIB$M_VM_GET_FILL0 | LIB$M_VM_TAIL_LARGE, BlockBytes = VM_BLOCK_BYTES; int status; char *cptr; int64 Time64; VM_ZONE *vzptr = &VmDeccZone; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM) || WATCHON_WATCHOFF) WatchThis (WATCHALL, WATCH_MOD_VM, "VmDeccInit()"); /* we may have to conjure up our own time */ if (!HttpdTickSecond) HttpdTickSecond = HttpdGetTickSecond(); /* for the next 10 seconds DECC has a get-out-of-jail-free card */ VmDeccTickSecond = HttpdTickSecond + 10; if (vzptr->ZoneId) return; vzptr->MagicValue = VM_MAGIC_DECC; vzptr->MaxPages = VM_DECC_MAX_PAGES; vzptr->MinPages = VM_DECC_MIN_PAGES; vzptr->InitialPages = VM_DECC_INITIAL_PAGES; vzptr->ExtendPages = VM_DECC_EXTEND_PAGES; vzptr->LogicalName = WASD_VM_DECC; vzptr->ReportTitle = "DECC"; VmSetPages (vzptr); #if !VM_DECC_LIB_VM /* forced disable of zone-based memory management */ vzptr->ZoneId = (int)-1; #endif /* if zone-based memory management disabled */ if ((int)vzptr->ZoneId < 0) return; /* create the general virtual memory zone */ status = lib$create_vm_zone (&vzptr->ZoneId, &Algorithm, &AlgorithmArg, &Flags, &vzptr->ExtendPages, &vzptr->InitialPages, &BlockBytes, 0, 0, 0, &ZoneNameDsc, 0, 0); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$create_vm_zone()", FI_LI); if (HttpdServerStartup) FaoToStdout ("%HTTPD-I-VM, DECC zone initialised\n"); } #endif /* VM_DECC */ /*****************************************************************************/ /* Allocate memory for DECC use. */ #if VM_DECC void* VmDeccCalloc ( int NumChunks, int ChunkSize ) { /*********/ /* begin */ /*********/ if (!VmDeccZone.ZoneId) VmDeccInit (); return (VmGenericCalloc (&VmDeccZone, NumChunks, ChunkSize, FI_LI)); } #endif /* VM_DECC */ /*****************************************************************************/ /* A wrapper for malloc() which just uses calloc(). */ #if VM_DECC void* VmDeccMalloc (int ChunkSize) { /*********/ /* begin */ /*********/ if (!VmDeccZone.ZoneId) VmDeccInit (); return (VmGenericCalloc (&VmDeccZone, -1, ChunkSize, FI_LI)); } #endif /* VM_DECC */ /*****************************************************************************/ /* Expand an individual a DECC chunk. See VmDeccCalloc(). */ #if VM_DECC void* VmDeccRealloc ( uchar *ChunkPtr, int ChunkSize ) { /*********/ /* begin */ /*********/ if (!VmDeccZone.ZoneId) VmDeccInit (); return (VmGenericRealloc (&VmDeccZone, ChunkPtr, ChunkSize, FI_LI)); } #endif /* VM_DECC */ /*****************************************************************************/ /* Release memory allocated for DECC use. */ #if VM_DECC void VmDeccFree (uchar *ChunkPtr) { /*********/ /* begin */ /*********/ if (!VmDeccZone.ZoneId) VmDeccInit (); VmGenericFree (&VmDeccZone, ChunkPtr, FI_LI); } #endif /* VM_DECC */ /*****************************************************************************/ /* Initialise virtual memory management for EXPAT. */ #if VM_EXPAT BOOL VmExpatInit () { static $DESCRIPTOR (ZoneNameDsc, "WASD expat"); static ulong Algorithm = LIB$K_VM_QUICK_FIT, AlgorithmArg = 64, Flags = LIB$M_VM_BOUNDARY_TAGS | LIB$M_VM_TAIL_LARGE, BlockBytes = VM_BLOCK_BYTES; int status; char *cptr; VM_ZONE *vzptr = &VmExpatZone; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM) || WATCHON_WATCHOFF) WatchThis (WATCHALL, WATCH_MOD_VM, "VmExpatInit()"); if ((int)vzptr->ZoneId < 0) return (false); vzptr->MagicValue = VM_MAGIC_EXPAT; vzptr->MaxPages = VM_EXPAT_MAX_PAGES; vzptr->MinPages = VM_EXPAT_MIN_PAGES; vzptr->InitialPages = VM_EXPAT_INITIAL_PAGES; vzptr->ExtendPages = VM_EXPAT_EXTEND_PAGES; vzptr->LogicalName = WASD_VM_EXPAT; vzptr->ReportTitle = "EXPAT"; VmSetPages (vzptr); /* if zone-based memory management disabled */ if (vzptr->ZoneId == (ulong)-1) return (false); /* create the expat virtual memory zone */ status = lib$create_vm_zone (&vzptr->ZoneId, &Algorithm, &AlgorithmArg, &Flags, &vzptr->ExtendPages, &vzptr->InitialPages, &BlockBytes, 0, 0, 0, &ZoneNameDsc, 0, 0); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$create_vm_zone()", FI_LI); FaoToStdout ("%HTTPD-I-VM, EXPAT zone initialised\n"); return (true); } #else /* VM_EXPAT */ /******************/ /* NOT VM_EXPAT */ /******************/ BOOL VmExpatInit () { return (false); } /* allow ExpatInit() linkages without recompile */ void* VmExpatMalloc (int ChunkSize) { return (NULL); } void* VmExpatRealloc (uchar *ChunkPtr, int ChunkSize ) { return (NULL); } void VmExpatFree (uchar *ChunkPtr) { return; } #endif /* VM_EXPAT */ /*****************************************************************************/ /* Allocate memory for EXPAT use. */ #if VM_EXPAT void* VmExpatMalloc (int ChunkSize) { /*********/ /* begin */ /*********/ return (VmGenericCalloc (&VmExpatZone, -1, ChunkSize, FI_LI)); } #endif /* VM_EXPAT */ /*****************************************************************************/ /* Expand an individual EXPAT chunk. See VmGenericCalloc(). */ #if VM_EXPAT void* VmExpatRealloc ( uchar *ChunkPtr, int ChunkSize ) { /*********/ /* begin */ /*********/ return (VmGenericRealloc (&VmExpatZone, ChunkPtr, ChunkSize, FI_LI)); } #endif /* VM_EXPAT */ /*****************************************************************************/ /* Release memory allocated for EXPAT use. */ #if VM_EXPAT void VmExpatFree (uchar *ChunkPtr) { /*********/ /* begin */ /*********/ VmGenericFree (&VmExpatZone, ChunkPtr, FI_LI); } #endif /* VM_EXPAT */ /*****************************************************************************/ /* Initialise virtual memory management for OpenSSL. */ #if VM_OPENSSL BOOL VmOpenSslInit () { static $DESCRIPTOR (ZoneNameDsc, "WASD OpenSSL"); static ulong Algorithm = LIB$K_VM_QUICK_FIT, AlgorithmArg = 128, Flags = LIB$M_VM_BOUNDARY_TAGS | LIB$M_VM_TAIL_LARGE, BlockBytes = VM_BLOCK_BYTES; int status; char *cptr; VM_ZONE *vzptr = &VmOpenSslZone; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM) || WATCHON_WATCHOFF) WatchThis (WATCHALL, WATCH_MOD_VM, "VmOpenSslInit()"); if ((int)vzptr->ZoneId < 0) return (false); vzptr->MagicValue = VM_MAGIC_OPENSSL; vzptr->MaxPages = VM_OPENSSL_MAX_PAGES; vzptr->MinPages = VM_OPENSSL_MIN_PAGES; vzptr->InitialPages = VM_OPENSSL_INITIAL_PAGES; vzptr->ExtendPages = VM_OPENSSL_EXTEND_PAGES; vzptr->LogicalName = WASD_VM_OPENSSL; vzptr->ReportTitle = "OpenSSL"; VmSetPages (vzptr); /* if zone-based memory management disabled */ if (vzptr->ZoneId == (ulong)-1) return (false); /* create the OpenSSL virtual memory zone */ status = lib$create_vm_zone (&vzptr->ZoneId, &Algorithm, &AlgorithmArg, &Flags, &vzptr->ExtendPages, &vzptr->InitialPages, &BlockBytes, 0, 0, 0, &ZoneNameDsc, 0, 0); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$create_vm_zone()", FI_LI); FaoToStdout ("%HTTPD-I-VM, OpenSSL zone initialised\n"); return (true); } #else /* VM_OPENSSL */ /******************/ /* NOT VM_OPENSSL */ /******************/ BOOL VmOpenSslInit () { return (false); } /* allow SesolaInit() linkages without recompile */ void* VmOpenSslMalloc (int ChunkSize) { return (NULL); } void* VmOpenSslRealloc (uchar *ChunkPtr, int ChunkSize ) { return (NULL); } void VmOpenSslFree (uchar *ChunkPtr) { return; } #endif /* VM_OPENSSL */ /*****************************************************************************/ /* Allocate memory for OpenSSL use. NOTE: OpenSSL does not employ calloc(). */ #if VM_OPENSSL void* VmOpenSslMalloc (int ChunkSize) { /*********/ /* begin */ /*********/ return (VmGenericCalloc (&VmOpenSslZone, -1, ChunkSize, FI_LI)); } #endif /* VM_OPENSSL */ /*****************************************************************************/ /* Expand an individual OpenSSL chunk. See VmGenericCalloc(). */ #if VM_OPENSSL void* VmOpenSslRealloc ( uchar *ChunkPtr, int ChunkSize ) { /*********/ /* begin */ /*********/ return (VmGenericRealloc (&VmOpenSslZone, ChunkPtr, ChunkSize, FI_LI)); } #endif /* VM_OPENSSL */ /*****************************************************************************/ /* Release memory allocated for OpenSSL use. */ #if VM_OPENSSL void VmOpenSslFree (uchar *ChunkPtr) { /*********/ /* begin */ /*********/ VmGenericFree (&VmOpenSslZone, ChunkPtr, FI_LI); } #endif /* VM_OPENSSL */ /*****************************************************************************/ /* Set initial and extend pages for the zone. Set initial to -1 to disable *some* VM zones. Set initial to 0 to enable *some* VM zones and have default values. Default values are pages, and if a trailing 'p'. If a trailing 'b' then indicates bytes, or if 'm' megabytes (or mebibytes if you prefer), and pages are calculated using / 512 plus 5%. Some zones allow tuning (dynamic adjustment to initial allocation) and will accept an additional two integers, the initial sample size, and how long that tune is maintained before reset. */ void VmSetPages (VM_ZONE *ZonePtr) { long ExtendPages = 0, InitialPages = 0, TuneSample = -1, TunePeriod = -1; char *cptr; /*********/ /* begin */ /*********/ sys$gettim (&ZonePtr->LastLookTime64); /* disabled by default - only some callers pay attention to disabled */ ZonePtr->ZoneId = (ulong)-1; if (!(cptr = SysTrnLnm (ZonePtr->LogicalName))) return; /* logical name value should be "[,extend>]" integers */ while (*cptr && !isdigit(*cptr) && *cptr != '-') cptr++; InitialPages = atol(cptr); if (*cptr == '-') cptr++; while (*cptr && isdigit(*cptr)) cptr++; if (to_lower(*cptr) == 'b' || to_lower(*cptr) == 'm') { if (InitialPages > 0) { if (to_lower(*cptr) == 'm') InitialPages *= 1048576; InitialPages /= 512; InitialPages += (InitialPages * 5) / 100; } while (*cptr && isalpha(*cptr)) cptr++; } while (*cptr && !isdigit(*cptr) && *cptr != '-') cptr++; ExtendPages = atol(cptr); if (*cptr == '-') cptr++; while (*cptr && isdigit(*cptr)) cptr++; if (to_lower(*cptr) == 'b' || to_lower(*cptr) == 'm') { if (ExtendPages > 0) { if (to_lower(*cptr) == 'm') ExtendPages *= 1048576; ExtendPages /= 512; ExtendPages += (ExtendPages * 5) / 100; } while (*cptr && isalpha(*cptr)) cptr++; } /* value can have optional "[,[,]" integers */ while (*cptr && !isdigit(*cptr)) cptr++; if (*cptr) TuneSample = atol(cptr); while (*cptr && isdigit(*cptr)) cptr++; while (*cptr && !isdigit(*cptr)) cptr++; if (*cptr) TunePeriod = atol(cptr); while (*cptr && isdigit(*cptr)) cptr++; /* only some callers pay attention to disabled zones */ if (InitialPages >= 0) { /* zone-based management enabled */ ZonePtr->ZoneId = 0; if (!InitialPages) InitialPages = ZonePtr->InitialPages; if (!ExtendPages) ExtendPages = ZonePtr->ExtendPages; if (InitialPages > ZonePtr->MaxPages) InitialPages = ZonePtr->MaxPages; else if (InitialPages <= ZonePtr->MinPages) InitialPages = ZonePtr->MinPages; ZonePtr->InitialPages = InitialPages; if (ExtendPages > ZonePtr->MaxPages) ExtendPages = ZonePtr->MaxPages; else if (ExtendPages <= ZonePtr->MinPages) ExtendPages = ZonePtr->MinPages; ZonePtr->ExtendPages = ExtendPages; /* not all zones pay attention to these settings */ if (TuneSample >= 0) ZonePtr->TuneSample = TuneSample; else ZonePtr->TuneSample = VM_REQUEST_TUNE_SAMPLE; if (TunePeriod >= 0) ZonePtr->TunePeriod = TunePeriod; else ZonePtr->TunePeriod = VM_REQUEST_TUNE_PERIOD; } } /*****************************************************************************/ /* Return a report on processes' virtual memory usage. This function blocks while executing. */ #define SHOW_VM_64 0 #ifndef SHOW_VM_64 # define SHOW_VM_64 1 #endif #if SHOW_VM_64 /* prototype */ int VmWrite64 (struct dsc$descriptor*, int64); #endif #if VM_BACKPORT_FOR_DTAG /* see VM.H */ void Vm__Report (REQUEST_STRUCT*); void VmReport (REQUEST_STRUCT *rqptr, REQUEST_AST NextTaskFunction) { Vm__Report (rqptr); SysDclAst (NextTaskFunction, rqptr); } void Vm__Report (REQUEST_STRUCT *rqptr) #else void VmReport (REQUEST_STRUCT *rqptr) #endif { static ulong ShowVmCode123 = 0, ShowVmCode567 = 4, ShowVmZoneDetail = 3; #if SHOW_VM_64 static int64 ShowVm64Code123 = 0, ShowVm64Code567 = 4, ShowVm64ZoneDetail = 3; #endif /* SHOW_VM_64 */ static char BeginPageFao [] = "

\n\ \n\
\n\ \n\ \n\
\
Request Heap Initial Pages: !UL (!ULkB) \n\
              Extend Pages: !UL (!ULkB)\n\
\n\
 HTTP/2 Heap Initial Pages: !UL (!ULkB)\n\
              Extend Pages: !UL (!ULkB)\n\
\n\
WsSize: !UL (!ULMB)  WsPeak: !UL (!ULMB)  VirtPeak: !UL (!ULMB)\n\
\n\
  PgFl: !UL/!UL (!UL/!ULMB !UL%)\n\
\n";

   static char  EndPageFao [] =
"
\n\
\n\ \n\ \n"; static ulong JpiPageFlts, JpiPagFilCnt, JpiVirtPeak, JpiWsSize, JpiWsPeak, Pid = 0; static VMS_ITEM_LIST3 JpiItems [] = { { sizeof(JpiPageFlts), JPI$_PAGEFLTS, &JpiPageFlts, 0 }, { sizeof(JpiPagFilCnt), JPI$_PAGFILCNT, &JpiPagFilCnt, 0 }, { sizeof(JpiVirtPeak), JPI$_VIRTPEAK, &JpiVirtPeak, 0 }, { sizeof(JpiWsSize), JPI$_WSSIZE, &JpiWsSize, 0 }, { sizeof(JpiWsPeak), JPI$_WSPEAK, &JpiWsPeak, 0 }, {0,0,0,0} }; int cnt, status; ushort Length; ulong Context, ZoneId; char Buffer [4096]; IO_SB IOsb; $DESCRIPTOR (BufferDsc, Buffer); #if SHOW_VM_64 int64 Context64, ZoneId64; #endif /* SHOW_VM_64 */ /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM) || WATCHON_WATCHOFF) WatchThis (WATCHALL, WATCH_MOD_VM, "VmReport()"); status = sys$getjpiw (EfnWait, &Pid, 0, &JpiItems, &IOsb, 0, 0); if (VMSok (status)) status = IOsb.Status; if (VMSnok (status)) { rqptr->rqResponse.ErrorTextPtr = "sys$getjpiw()"; ErrorVmsStatus (rqptr, status, FI_LI); #if !VM_BACKPORT_FOR_DTAG AdminEnd (rqptr); #endif return; } VmReportPlus = strsame (rqptr->rqHeader.RequestUriPtr, ADMIN_REPORT_MEMORY_PLUS, -1); if (VmReportPlus) AdminPageTitle (rqptr, "Virtual Memory+ Report"); else AdminPageTitle (rqptr, "Virtual Memory Report"); status = FaoToNet (rqptr, BeginPageFao, VmRequestZone.StatInitial ? VmRequestZone.StatInitial : VmRequestZone.InitialPages, VmRequestZone.StatInitial ? VmRequestZone.StatInitial * 512 / 1024 : VmRequestZone.InitialPages * 512 / 1024, VmRequestZone.TuneSample, VmRequestZone.TunePeriod, &VmRequestZone.StatPages, &VmRequestZone.StatCount, VmRequestZone.ExtendPages, VmRequestZone.ExtendPages * 512 / 1024, VmHttp2Zone.InitialPages, VmHttp2Zone.InitialPages * 512 / 1024, VmHttp2Zone.ExtendPages, VmHttp2Zone.ExtendPages * 512 / 1024, JpiWsSize, JpiWsSize * 512 / 1048576, JpiWsPeak, JpiWsPeak * 512 / 1048576, JpiVirtPeak, JpiVirtPeak * 512 / 1048576, JpiPagFilCnt, HttpdProcess.PgFlQuo, ((ulong)JpiPagFilCnt * 512) / 1048576, ((ulong)HttpdProcess.PgFlQuo * 512) / 1048576, 100 - PercentOf32((ulong)JpiPagFilCnt,(ulong)HttpdProcess.PgFlQuo)); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); #if VM_DECC VmReportZone (rqptr, &VmDeccZone); VmReportZone (rqptr, &VmGeneralZone); VmReportZone (rqptr, &VmCacheZone); VmReportZone (rqptr, &VmPermCacheZone); #endif /* VM_DECC */ #if VM_OPENSSL if (VmOpenSslZone.ZoneId != (ulong)-1) VmReportZone (rqptr, &VmOpenSslZone); #endif #if VM_EXPAT if (VmExpatZone.ZoneId != (ulong)-1) VmReportZone (rqptr, &VmExpatZone); #endif if (VMSnok (status = lib$show_vm (&ShowVmCode123, &VmWrite, rqptr))) FaoToNet (rqptr, "lib$show_vm() !&S\n", status); if (VMSnok (status = lib$show_vm (&ShowVmCode567, &VmWrite, rqptr))) FaoToNet (rqptr, "lib$show_vm() !&S\n", status); FaoToNet (rqptr, "\n"); Context = 0; while ((status = lib$find_vm_zone (&Context, &ZoneId)) == SS$_NORMAL) { status = lib$show_vm_zone (&ZoneId, &ShowVmZoneDetail, &VmWrite, rqptr); if (VMSnok (status)) FaoToNet (rqptr, "lib$show_vm_zone() !&S\n", status); } if (status != LIB$_NOTFOU) FaoToNet (rqptr, "lib$find_vm_zone() !&S\n", status); /**********/ #if SHOW_VM_64 /**********/ if (VMSnok (status = lib$show_vm_64 (&ShowVm64Code123, &VmWrite, (int64)rqptr))) FaoToNet (rqptr, "lib$show_vm_64() !&S\n", status); if (VMSnok (status = lib$show_vm_64 (&ShowVm64Code567, &VmWrite, (int64)rqptr))) FaoToNet (rqptr, "lib$show_vm_64() !&S\n", status); FaoToNet (rqptr, "\n"); Context64 = 0; while ((status = lib$find_vm_zone_64 (&Context64, &ZoneId64)) == SS$_NORMAL) { status = lib$show_vm_zone_64 (&ZoneId64, &ShowVm64ZoneDetail, &VmWrite64, (int64)rqptr); if (VMSnok (status)) FaoToNet (rqptr, "lib$show_vm_zone_64() !&S\n", status); } if (status != LIB$_NOTFOU) FaoToNet (rqptr, "lib$find_vm_zone_64() !&S\n", status); #endif /* SHOW_VM_64 */ FaolToNet (rqptr, EndPageFao, NULL); rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN; ResponseHeader200 (rqptr, "text/html", &rqptr->NetWriteBufferDsc); #if !VM_BACKPORT_FOR_DTAG AdminEnd (rqptr); #endif } /*****************************************************************************/ /* See VmReportZone(). */ void VmDebugOpenSslZone (char *module, int lnumber) { /*********/ /* begin */ /*********/ fprintf (stdout, "***** %s:%d *****\n", module, lnumber); VmReportZone (NULL, &VmOpenSslZone); } /*****************************************************************************/ /* If |rqptr| is NULL this is written to allowing it to be used for inline code debugging. Suggest something like the immediately above. */ void VmReportZone ( REQUEST_STRUCT *rqptr, VM_ZONE *ZonePtr ) { static char HeapFao [] = "!AZ Heap Initial Pages: !UL (!ULkB)\n\ !#AZ Extend Pages: !UL (!ULkB)\n"; static char StatsFao [] = "!AZ Statistics (LIB$VM..)\n"; static char DataFao [] = " calloc() !&,UL\n\ malloc() !&,UL\n\ realloc() !&,UL\n\ free() !&,UL\n\ Allocated !&,@SQ bytes\n\ Deallocated !&,@SQ bytes\n\ Still In Use !&,@SQ bytes (!AZ!AZ∆ !AZ!&,@SQ !SL%)\n\ Max !&,@SQ bytes\n"; static char PlusMinus [2] = { 0, 0 }; int blen, status, percentage; ushort slen; int64 Delta64; char *bptr; char LastLookAgo [64], ReportBuffer [1024]; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM) || WATCHON_WATCHOFF) WatchThis (WATCHALL, WATCH_MOD_VM, "VmReportZone()"); if (!ZonePtr->ZoneId) return; ThisLongAgo (&ZonePtr->LastLookTime64, LastLookAgo); if (!ZonePtr->LastLookBytes) Delta64 = 0; else Delta64 = ZonePtr->InUseBytes - ZonePtr->LastLookBytes; if (Delta64 > 0) PlusMinus[0] = '+'; else PlusMinus[0] = '\0'; if (Delta64) percentage = (int)((Delta64 * 100) / ZonePtr->InUseBytes); else percentage = 0; bptr = ReportBuffer; blen = sizeof(ReportBuffer)-1; /* if zone-based memory management disabled */ if (ZonePtr->ZoneId == (ulong)-1) status = FaoToBuffer (bptr, blen, &slen, StatsFao, ZonePtr->ReportTitle); else status = FaoToBuffer (bptr, blen, &slen, HeapFao, ZonePtr->ReportTitle, ZonePtr->InitialPages, ZonePtr->InitialPages * 512 / 1024, strlen(ZonePtr->ReportTitle), " ", ZonePtr->ExtendPages, ZonePtr->ExtendPages * 512 / 1024); bptr += slen; blen -= slen; if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); if (rqptr && slen) { *bptr++ = '\n'; blen--; } status = FaoToBuffer (bptr, blen, &slen, DataFao, ZonePtr->CallocCount, ZonePtr->MallocCount, ZonePtr->ReallocCount, ZonePtr->FreeCount, &ZonePtr->AllocBytes, &ZonePtr->DeallocBytes, &ZonePtr->InUseBytes, LastLookAgo, LastLookAgo[0] ? " " : "", PlusMinus, &Delta64, percentage, &ZonePtr->InUseMaxBytes); bptr += slen; blen -= slen; if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); if (rqptr && slen) { *bptr++ = '\n'; blen--; } *bptr = '\0'; if (rqptr) FaoToNet (rqptr, "!AZ", ReportBuffer); else fwrite (ReportBuffer, bptr-ReportBuffer, 1, stdout); ZonePtr->LastLookBytes = ZonePtr->InUseBytes; sys$gettim (&ZonePtr->LastLookTime64); } /*****************************************************************************/ /* Action routine for lib$show_vm*() routines. Simply write the contents of the parameter descriptor to the client plus a newline character - using some specially crafted HTML forbidden character escapes allowing an anchor to shine through. */ int VmWrite ( struct dsc$descriptor *DscPtr, REQUEST_STRUCT *rqptr ) { static int EmptyLineCount; int status; char buf [1024]; char *cptr, *czptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM) || WATCHON_WATCHOFF) WatchThis (WATCHALL, WATCH_MOD_VM, "VmWrite()"); #if 0 FaoToNet (rqptr, "!UL |!-!#AZ|\n", DscPtr->dsc$w_length, DscPtr->dsc$a_pointer); #endif czptr = (cptr = DscPtr->dsc$a_pointer) + DscPtr->dsc$w_length; /* Zone Id = 7B65E290, Zone name = "DEFAULT_ZONE" */ if (cptr < czptr && MATCH8 (cptr, "Zone Id ")) { if (!VmReportPlus) { for (sptr = cptr + 20; sptr < czptr; sptr++) { if (MATCH8 (sptr, "Zone nam")) { sptr += 13; if (!MATCH4 (sptr, "WASD")) { VmReportThisOne = false; return (SS$_NORMAL); } break; } } } VmReportThisOne = true; EmptyLineCount = 0; FaoToNet (rqptr, "!90*-\n\n"); zptr = (sptr = buf) + sizeof(buf)-8; while (cptr < czptr && sptr < zptr) { if (*cptr == '<') { if (SAME3 (cptr, '')) { SET4 (sptr, ''); sptr += 4; cptr += 4; } else { SET4 (sptr, '<'); sptr += 4; cptr++; } } else if (SAME2 (cptr, '\">')) { SET2 (sptr, '\">'); sptr += 2; cptr += 2; } else if (*cptr == '>') { SET4 (sptr, '>'); sptr += 4; cptr++; } else if (*cptr == '&') { SET4 (sptr, '&'); sptr += 4; *sptr++ = ';'; cptr++; } else *sptr++ = *cptr++; } } else if (VmReportThisOne) { if (cptr < czptr) EmptyLineCount = 0; else if (EmptyLineCount++) return (SS$_NORMAL); zptr = (sptr = buf) + sizeof(buf)-8; while (cptr < czptr && sptr < zptr) *sptr++ = *cptr++; } else return (SS$_NORMAL); *sptr++ = '\n'; *sptr = '\0'; NetWriteBuffered (rqptr, NULL, buf, sptr-buf); return (SS$_NORMAL); } /*****************************************************************************/ /* Wrapper for VmWrite(). */ #if SHOW_VM_64 int VmWrite64 ( struct dsc$descriptor *DscPtr, int64 UserParam /* 32 bit pointer to the request */ ) { /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM) || WATCHON_WATCHOFF) WatchThis (WATCHALL, WATCH_MOD_VM, "VmWrite64()"); VmWrite (DscPtr, (ulong)UserParam); return (SS$_NORMAL); } #endif /* SHOW_VM_64 */ /*****************************************************************************/ /* */ void VmDebug (ulong ZoneId) { #ifdef DBUG static ulong ShowVmZoneDetail = 3; int status; ulong Context; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM) || WATCHON_WATCHOFF) WatchThis (WATCHALL, 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) || WATCHON_WATCHOFF) WatchDataFormatted ("----------\n"); #endif } /*****************************************************************************/