// // File: hzMemory.cpp // // Legal Notice: This file is part of the HadronZoo C++ Class Library. Copyright 2025 HadronZoo Project (http://www.hadronzoo.com) // // The HadronZoo C++ Class Library is free software: You can redistribute it, and/or modify it under the terms of the GNU Lesser General Public License, as published by the Free // Software Foundation, either version 3 of the License, or any later version. // // The HadronZoo C++ Class Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR // A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License along with the HadronZoo C++ Class Library. If not, see http://www.gnu.org/licenses. //
// // Desc: 1) Optional overide of general case new and delete operators (when hzOveride.h is included in application's main .cpp file) // 2) Provision of memory use diagnostics //
#include <iostream> #include <fstream>
#include <sys/stat.h> #include <malloc.h>
#include "hzBasedefs.h" #include "hzDatabase.h" #include "hzDate.h" #include "hzDocument.h" #include "hzTextproc.h" #include "hzUnixacc.h"
using namespace std ;
/* ** Global Variables */
global hzMeminfo _hzGlobal_Memstats ; // Memory statistics
#ifdef OVERRIDE global bool _hzGlobal_XM = true ; // New/Delete Override activated #else global bool _hzGlobal_XM = false ; // New/Delete Override not activated #endif
/* ** SECTION 1: Overide of global new and delete operator */
class _hz_heap { struct _rblk { // RAM block. // // The global new/delete overload regime allocates small objects (between 8 and 128 bytes) from one of 16 separate free list, each of which deals only with objects of a specific size. There is a free list for 8 byte objects, another for 16 byte objects and so on. When any of these free lists become // empty and new RAM block is allocated from main memory to top them up. // // The RAM blocks are 64K minus 8 bytes. The (not very compelling) reason for this weird size is that system memory allocation overhead is 8 bytes, so each block consumes // exactly 64K of real memory. Given // the RAM blocks are always the same size, RAM blocks assigned to the free list of 8 byte objects will contain twice the number of 'object slots' that // RAM blocks assigned to the free list of 16 byte objects will have. The larger the fixed sized objects, the fewer each RAM block will hold. For some // sizes, a small proportion of the RAM block is wasted. Upon allocation of a RAM block, as many slots that will fit for the intended size, are set up // within the block so that one slot points to the next with the last one pointing to NULL. Then the whole lot is added to the free list for the object // size. // // The reason for allocating small objects in this way, is to eliminate the aforementioned 8 byte per allocation overhead. This module is a recent and // experimental addition to the HadronZoo Class Library. Long before a global new/delete overload was attempted, a similar regime was introduced in the // hzString module. Ultimately the global new/delete overload may replace that in the hzString module, however this is not a priority. Before this will // come an overview of how memory is used within real life applications. Considerable evolution is anticipated!
int32_t m_size ; // If block is dedicated show the size, otherwise this will be 0 int32_t m_usage ; // If the block is not dedicated, this will be the next object address to allocate uint64_t m_data[1200] ; // Data area } ;
// Mutexs hzLockRWD m008 ; // Mutex for 8 byte allocations hzLockRWD m016 ; // Mutex for 16 byte allocations hzLockRWD m024 ; // Mutex for 24 byte allocations hzLockRWD m032 ; // Mutex for 32 byte allocations hzLockRWD m040 ; // Mutex for 40 byte allocations hzLockRWD mAll ; // Mutex for all the above hzLockRWD mSze ; // Mutex for oversized allocations
// Freelist pointers (last 3 digits denotes size) uint64_t* fL008 ; // Freelist pointer for 8 byte objects uint64_t* fL016 ; // Freelist pointer for 16 byte objects uint64_t* fL024 ; // Freelist pointer for 24 byte objects uint64_t* fL032 ; // Freelist pointer for 32 byte objects uint64_t* fL040 ; // Freelist pointer for 40 byte objects
_rblk* _mkblk (int32_t size) ; public: void* m_allBlocks[8192] ; // Pointers to all managed blocks (max of 8192 blocks)
// Number of used objects uint32_t nU008 ; // In use counter for 8 byte objects uint32_t nU016 ; // In use counter for 16 byte objects uint32_t nU024 ; // In use counter for 24 byte objects uint32_t nU032 ; // In use counter for 32 byte objects uint32_t nU040 ; // In use counter for 40 byte objects
// Number of free objects uint32_t nF008 ; // Free counter for 8 byte objects uint32_t nF016 ; // Free counter for 16 byte objects uint32_t nF024 ; // Free counter for 24 byte objects uint32_t nF032 ; // Free counter for 32 byte objects uint32_t nF040 ; // Free counter for 40 byte objects
uint32_t nCallsMalloc ; // Number of objects over 128 bytes uint32_t nCallsFree ; // Number of objects over 128 bytes uint32_t nOver ; // Number of objects over 128 bytes uint32_t nOverSize ; // Number of bytes in objects over 128 bytes uint32_t numAllBlks ; // Number of blocks
uint32_t m_allSizes[1001] ; // Numbers alocated for each object size. uint32_t m_allSizeA[1001] ; // Number of alocations for each object size. uint32_t m_allSizeF[1001] ; // Number of deletions for each object size.
_hz_heap (void) { m008.Setname("Mem_fl_008") ; m016.Setname("Mem_fl_016") ; m024.Setname("Mem_fl_024") ; m032.Setname("Mem_fl_032") ; m040.Setname("Mem_fl_040") ;
fL008=0; fL016=0; fL024=0; fL032=0; fL040=0;
nU008 = nU016 = nU024 = nU032 = nU040 = 0 ; nF008 = nF016 = nF024 = nF032 = nF040 = 0 ;
nCallsMalloc = nCallsFree = nOver = nOverSize = numAllBlks = 0 ; }
~_hz_heap (void) { shutdown() ; }
void* allocate (uint32_t size) ; void release (void* ptr) ; void shutdown (void) ; };
/* ** Variables for memory regime */
static _hz_heap* s_Heap = 0 ; // The global heap static hzChain s_memReport ; // Chain for memory report compilation static uint32_t s_nBytesInit ; // Total bytes allocated under new override static uint32_t s_nBytesExit ; // Total bytes reserved under new override
// The actual working functions are static void* _regime_alloc_Init (uint32_t size) ; // Only called ONCE with the first thing allocated in the program anywhere static void* _regime_alloc_Syst (uint32_t size) ; // Allocate using malloc static void* _regime_alloc_Ovrd (uint32_t size) ; // Allocate using hzHeap static void _regime_free_Syst (void* mem) ; // This frees objects static void _regime_free_Ovrd (void* mem) ; // This frees objects static void* _regime_alloc_Exit (uint32_t size) ; // Allocates memory during shutdown of the memory manager
// Note that hz_mem_allocate() and hz_mem_release() just called according to function pointers void* (*fnptr_Alloc) (uint32_t) = _regime_alloc_Init ; // Allocator for new/delete override void (*fnptr_Free) (void*) = _regime_free_Syst ; // De-allocator for new/delete override
/* ** Heap functions */
_hz_heap::_rblk* _hz_heap::_mkblk (int32_t size) { _rblk* pBlk ; // Block pointer uint64_t* ptr ; // Treat internal data as 8 byte ints int32_t n ; // Data segment iterator int32_t max ; // Max number of data segments
// Allocate the block pBlk= (_rblk*) malloc(sizeof(_rblk)) ;
nCallsMalloc++; nOverSize += malloc_usable_size(pBlk) ;
m_allBlocks[numAllBlks] = pBlk ; numAllBlks++ ; memset(pBlk, 0, sizeof(_rblk)) ;
// Partition the block switch (size) { case 8: max = 1200 ; nF008 += max ; break ; case 16: max = 600 ; nF016 += max ; break ; case 24: max = 400 ; nF024 += max ; break ; case 32: max = 300 ; nF032 += max ; break ; case 40: max = 240 ; nF040 += max ; break ; }
size /= 8 ;
ptr = pBlk->m_data ; ptr += size ;
for (n = 0, max-- ; max ; max--, n += size) { pBlk->m_data[n] = (uint64_t) ptr ; ptr += size ; } pBlk->m_size = size * 8 ;
return pBlk ; }
void* _hz_heap::allocate (uint32_t size) { // Allocate memory from the memory regime if required size is 64 bytes or less and from the heap otherwise. Under the regime, allocation is // always from a freelist of segments of 8,16,24, ... 128 bytes. If the appropriate freelist is empty, a new block is allocated and divided // into multiple segments. Allocations of greater than 128 bytes are placed in a map for oversized objects. // // Arguments: 1) size Number of bytes to allocate // // Returns: Pointer to the allocated memory
_rblk* pBlk = 0; // Fixed block allocated if any void* pObj = 0; // Object allocated uint64_t* ptr; // Intermeadiate pointer for free list navigation int32_t nDiv; // Position devider
nCallsMalloc++; nDiv = size%8; if (nDiv) size += (8-nDiv);
switch (size) { case 8: m008.LockWrite(); if (!fL008) {pBlk=_mkblk(8); fL008=pBlk->m_data;} nF008-- ; nU008++; ptr=fL008; fL008=(uint64_t*)*ptr; m008.Unlock(); break; case 16: m016.LockWrite(); if (!fL016) {pBlk=_mkblk(16); fL016=pBlk->m_data;} nF016-- ; nU016++; ptr=fL016; fL016=(uint64_t*)*ptr; m016.Unlock(); break; case 24: m024.LockWrite(); if (!fL024) {pBlk=_mkblk(24); fL024=pBlk->m_data;} nF024-- ; nU024++; ptr=fL024; fL024=(uint64_t*)*ptr; m024.Unlock(); break; case 32: m032.LockWrite(); if (!fL032) {pBlk=_mkblk(32); fL032=pBlk->m_data;} nF032-- ; nU032++; ptr=fL032; fL032=(uint64_t*)*ptr; m032.Unlock(); break; case 40: m040.LockWrite(); if (!fL040) {pBlk=_mkblk(40); fL040=pBlk->m_data;} nF040-- ; nU040++; ptr=fL040; fL040=(uint64_t*)*ptr; m040.Unlock(); break;
default: // Non-standard size. mSze.LockWrite();
pObj = malloc(size);
nDiv = malloc_usable_size(pObj) ; if (nDiv < 8192) { m_allSizes[nDiv/8]++ ; m_allSizeA[nDiv/8]++ ; } nOver++ ; nOverSize += nDiv ;
mSze.Unlock();
return pObj; }
return (void*) ptr; }
void _hz_heap::release (void* pObj) { // Places object in free list if it is of one of the precribed sizes, otherwise it frees it from the OS managed heap // // Arguments: 1) pObj Pointer to previously decclard memory // Returns: None
_rblk* pBlk ; // Block in which object is found uint64_t* ptr ; // Object as uint64_t* char* pVoid ; // Start of block (for pointer comparison) char* pLim ; // End of block (for pointer comparison) uint32_t size = 0 ; // Size derived from block in which object is found uint32_t nPos ; // Position in hzOrder uint32_t nDiv ; // Position devider
if (!pObj) return ; if (pObj == this) return ;
nCallsFree++; ptr = (uint64_t*) pObj ;
// Size is not supplied so must be determined from the only thing that is supplied - the pointer to the object to be freed. From the // address we can determine what block it is in and so the size.
mAll.LockWrite() ; size = 1000000 ; for (nPos = 2 ; nPos < numAllBlks ; nPos *= 2) ; nDiv = nPos / 2 ; nPos-- ;
for (;;) { //if (nPos >= m_allBlocks.Count()) if (nPos >= numAllBlks) nPos -= nDiv ; else { //pLim = pVoid = (char*) m_allBlocks.GetObj(nPos) ; pLim = pVoid = (char*) m_allBlocks[nPos] ; pLim += sizeof(_rblk) ;
if (pObj < pVoid) nPos -= nDiv ; else if (pObj > pLim) nPos += nDiv ; else { pBlk = (_rblk*) pVoid ; size = pBlk->m_size ; break ; } }
if (!nDiv) break ; nDiv /= 2 ; } mAll.Unlock() ;
// The following sets a pointer to the start of the object (first 8 bytes). Then it sets the contents of the start of the object so that it points // to the start of the freelist (for the object size). The new start of the freelist is then set to the pointer (ie it is set to the start of the // freed object)
switch (size) { case 8: m008.LockWrite(); nU008--; nF008++ ; ptr=(uint64_t*)pObj; *ptr=(uint64_t)fL008; fL008=ptr; m008.Unlock(); break; case 16: m016.LockWrite(); nU016--; nF016++ ; ptr=(uint64_t*)pObj; *ptr=(uint64_t)fL016; fL016=ptr; m016.Unlock(); break; case 24: m024.LockWrite(); nU024--; nF024++ ; ptr=(uint64_t*)pObj; *ptr=(uint64_t)fL024; fL024=ptr; m024.Unlock(); break; case 32: m032.LockWrite(); nU032--; nF032++ ; ptr=(uint64_t*)pObj; *ptr=(uint64_t)fL032; fL032=ptr; m032.Unlock(); break; case 40: m040.LockWrite(); nU040--; nF040++ ; ptr=(uint64_t*)pObj; *ptr=(uint64_t)fL040; fL040=ptr; m040.Unlock(); break; default: nOver-- ; nDiv = malloc_usable_size(pObj) ; if (nDiv < 8192) { m_allSizes[nDiv/8]-- ; m_allSizeF[nDiv/8]++ ; } nOverSize -= nDiv ; break ; } }
void _hz_heap::shutdown (void) { // Shutdown the heap // // Arguments: None // Returns: None
fnptr_Alloc = _regime_alloc_Exit ; }
/* ** Global memory management functions */
void* hz_mem_allocate (uint32_t size) { // Category: Memory // // This function is ALWAYS called whenever operator new or new[] is called on ANY object. It will invoke whater function fnptr_Alloc points to // // Argument: size Requested number of bytes for allocation // // Returns: Pointer to the allocated memory
return fnptr_Alloc(size) ; }
void hz_mem_release (void* ptr) { // Category: Memory // // This function is ALWAYS called whenever operator delete or delete[] is called on ANY object. It will invoke whater function fnptr_Free points to // // Argument: ptr Pointer to object to be freed // // Returns: None
fnptr_Free(ptr) ; }
static void* _regime_alloc_Init (uint32_t size) { // Category: Memory // // Under the override regime operator new is defined as _hz_mem_allocate(), which calls whatever function fnptr_Alloc points to (initially this function). Delete is defined as // _hz_mem_release(), which calls whatever function fnptr_Free points to. Currently, fnptr_Free always points to _regime_free_norm(), which tests _hzGlobal_XM to decide how to // delete memory. // // The first memory allocated for the program is allocated via this function, purely so _hzGlobal_XM can be tested before ANY further allocations. This function decides to set // fnptr_Alloc to either _regime_alloc_Syst() for malloc/free and _regime_alloc_Ovrd for the override - then calls the selected function to allocate the requested memory. All // subsequent allocations will be from whichever funtion fnptr_Alloc is set to. // // Arguments: 1) size Number of bytes to allocate // Returns: Pointer to allocated memory block
static bool bBeenHere = false ;
void* ptr ; // New object space
if (_hzGlobal_XM) { if (bBeenHere) { printf("_regime_alloc_Init: Duplicate Call\n") ; exit(1) ; } bBeenHere = true ;
// printf("Allocating the heap %p %p\n", *fnptr_Alloc, *fnptr_Free) ; fflush(stdout) ; s_Heap = (_hz_heap*) malloc(sizeof(_hz_heap)) ; memset(s_Heap, 0, sizeof(_hz_heap)) ;
s_Heap->nCallsMalloc++ ; s_Heap->nOverSize += sizeof(_hz_heap) ;
fnptr_Alloc = _regime_alloc_Ovrd ; fnptr_Free = _regime_free_Ovrd ; } else { fnptr_Alloc = _regime_alloc_Syst ; fnptr_Free = _regime_free_Syst ; }
// printf("Now Allocating the memory using approved fn\n") ; fflush(stdout) ; ptr = fnptr_Alloc(size) ; return ptr ; } static void* _regime_alloc_Syst (uint32_t size) { // Category: Memory // // Only called during new-overide initialization // // Arguments: 1) size Bytes to allocate // Returns: Pointer to the allocated block
void* ptr ; // New object space
s_nBytesInit += size ; ptr = malloc(size) ;
return ptr ; }
static void* _regime_alloc_Ovrd (uint32_t size) { // Category: Memory // // This will allocate an object of the required size from a managed block if the size is small and by a direct call to malloc otherwise. In the // latter case, the object will be placed in the map of outsized objects. // // Arguments: 1) size Number of bytes requested // Returns: Pointer to the allocated block
if (!s_Heap) { // Fatal("No heap initialized\n") ; printf("No heap initialized\n") ; fflush(stdout) ; exit(1) ; }
// printf("_regime_alloc_Ovrd %d bytes\n", size) ; fflush(stdout) ; return s_Heap->allocate(size) ; }
static void _regime_free_Ovrd (void* ptr) { s_Heap->release(ptr) ; } static void _regime_free_Syst (void* ptr) { free(ptr) ; }
static void* _regime_alloc_Exit (uint32_t size) { // Category: Memory // // Only called to allocate memory during shutdown (generally for diagnostic pursose only) // // Arguments: 1) size Bytes to allocate // Returns: Pointer to the allocated block
void* ptr ; // New object space
s_nBytesExit += size ; ptr = malloc(size) ; memset(ptr, 0, size) ;
return ptr ; }
/* ** SECTION 2: Memory use dianostics */
hzMeminfo::hzMeminfo (void) { m_numMCH = 0 ; // Total number of hzMCH instances (with or without data) m_numMCH_D = 0 ; // Number of hzMCH instances with data m_numChain = 0 ; // Total number of hzChain instances (with or without data container) m_numChainDC = 0 ; // Number of hzChain instances with data container m_numChainBlks = 0 ; // Total number of hzChain blocks m_numChainBF = 0 ; // Number of hzChain blocks in free list
memset(m_strSm_u, 0, sizeof(uint32_t) * 32) ; // Small string spaces (8 to 256 bytes), in use memset(m_strSm_f, 0, sizeof(uint32_t) * 32) ; // Small string spaces (8 to 256 bytes), free
m_numSblks = 0 ; // Number of string superblocks m_numStrOver = 0 ; // Number of hzString instances (oversize) m_ramStrOver = 0 ; // Total memory allocated to oversized hzStrings m_numStrings = 0 ; // Number of hzString instances m_numMemblkA = 0 ; // Number of type A memblk instances in RAM (for size A 16 byte objects) m_numMemblkB = 0 ; // Number of type B memblk instances in RAM (for size B 24 byte objects) m_numMemblkC = 0 ; // Number of type C memblk instances in RAM (for size C 32 byte objects) m_numMemblkD = 0 ; // Number of type D memblk instances in RAM (for size D 48 byte objects) m_numMemblkE = 0 ; // Number of type E memblk instances in RAM (for size E 64 byte objects) m_numIsams = 0 ; // Number of ISAM collections m_numIsamIndx = 0 ; // Number of ISAM index blocks m_numIsamData = 0 ; // Number of ISAM data blocks m_numArrays = 0 ; // Number of hzArray instances m_numArrayDA = 0 ; // Number of hzArray instances with data area m_numLists = 0 ; // Number of hzList instances m_numListDC = 0 ; // Number of hzList instances with data area m_numQues = 0 ; // Number of hzQue instances m_numStacks = 0 ; // Number of hzStack instances m_numSmaps = 0 ; // Number of hzMapS instances m_numMmaps = 0 ; // Number of hzMapM instances m_numSpmaps = 0 ; // Number of hzLookup instances m_numSets = 0 ; // Number of hzSet instances m_numVectors = 0 ; // Number of hzVect instances m_numBitmaps = 0 ; // Number of hzBitmap instances m_numBitmapSB = 0 ; // Number of hzBitmap 'segment block' instances m_numMCHB = 0 ; // Number of 'micro chain' blocks. m_numDochtm = 0 ; // Number of hzDocHtm instances m_numDocxml = 0 ; // Number of hzDocXml instances m_numBincron = 0 ; // Number of hdbBinCron instances m_numBinstore = 0 ; // Number of hdbBinStore instances }
static const char* _snt (uint32_t nValue) { return FormalNumber(nValue, 13) ; } static const char* _snh (uint32_t nValue) { return FormalNumber(nValue, 0) ; }
static void _report_mem_item3 (hzChain& Z, const char* title, uint32_t vA, uint32_t vB, uint32_t vC, bool bHtml) { // Reports 3 values (e.g allocated and in use, free reserves and total)
if (bHtml) Z.Printf("\t<tr align=\"right\"><td align=\"left\">%s</td><td>%s</td><td>%s</td><td>%s</td></tr>\n", title, _snh(vA), _snh(vB), _snh(vC)) ; else Z.Printf("%s %s %s %s\n", title, _snt(vA), _snt(vB), _snt(vC)) ; }
static void _report_mem_itemA (hzChain& Z, const char* title, uint32_t curr, uint32_t prev, uint32_t sum, bool bHtml) { // Reports 3 values (e.g allocated and in use, free reserves and total)
if (bHtml) { Z << "<tr align=\"right\"><td align=\"left\">" ; if (!title) Z << "untitled" ; else Z << title ; Z << "</td><td></td><td></td><td></td><td></td>" ;
Z.Printf("<td>%s</td><td>%s</td><td>%s</td></tr>\n", _snh(curr), _snh(prev), _snh(sum)) ; } else { Z.Printf("%s %s %s %s\n", title, _snt(curr), _snt(prev), _snt(sum)) ; } }
static void _report_mem_itemC (hzChain& Z, const char* title, uint32_t A, uint32_t B, uint32_t C, uint32_t D, uint32_t curr, uint32_t prev, uint32_t sum, bool bHtml) { // Reports 7 values
if (bHtml) { Z << "<tr align=\"right\"><td align=\"left\">" ; if (!title) Z << "untitled" ; else Z << title ;
Z.Printf("</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>\n", _snh(A), _snh(B), _snh(C), _snh(D), _snh(curr), _snh(prev), _snh(sum)) ; } else { Z.Printf("%s %s %s %s %s %s %s %s\n", title, _snt(A), _snt(B), _snt(C), _snt(D), _snt(curr), _snt(prev), _snt(sum)) ; } }
static void _report_objects (hzChain& Z, const hzMeminfo& cms, const hzMeminfo& pms, uint32_t& total, bool bHtml) { // Category: Diagnostics // // Support function to ReportMemoryUsage(). Provides Report on strings and chain memory usage by the application. // // Arguments: 1) Z The hzChain instance to receive the report // 2) cms Current memory stats // 3) pms Previous memory stats // 4) total Running total RAM // // Returns: None
static int32_t prev_StrTbl = 0 ; // Previous count of strings held in string table
uint32_t U ; // Memory used by given object class
if (bHtml) { Z << "<table width=\"750\" align=\"center\" border=\"1\" cellspacing=\"0\" cellpadding=\"0\" " "style=\"text-decoration:none; font-family:verdana; font-size:11px; font-weight:normal; color:#000000;\">\n" "<tr>\n" "\t<th width=\"90\">Entity</th>\n" "\t<th width=\"90\">Assigned</th>\n" "\t<th width=\"90\">Prev</th>\n" "\t<th width=\"90\">Free</th>\n" "\t<th width=\"90\">Prev</th>\n" "\t<th width=\"90\">Total</th>\n" "\t<th width=\"90\">Prev</th>\n" "\t<th width=\"110\">Total RAM</th>\n" "</tr>\n" ; } else { Z << "Entity__________ Assigned_____ Prev_________ Free_________ Prev_________ Total________ Prev_________ Total RAM____\n" ; }
// CHAINS U = cms.m_numChain * sizeof(hzChain) ; U += (cms.m_numChainDC * 32) ; total += U ; _report_mem_itemC(Z, "Chains..........", cms.m_numChainDC, pms.m_numChainDC, cms.m_numChain - cms.m_numChainDC, pms.m_numChain - pms.m_numChainDC, cms.m_numChain, pms.m_numChain, U, bHtml) ;
// CHAIN BLOCKS. Note that count of allocated blocks is the total number of blocks created minus the count of those in the freelist U = cms.m_numChainBlks * 1480 ; total += U ; _report_mem_itemC(Z, "Chain Blocks....", cms.m_numChainBlks - cms.m_numChainBF, pms.m_numChainBlks - pms.m_numChainBF, cms.m_numChainBF, pms.m_numChainBF, cms.m_numChainBlks, pms.m_numChainBlks, U, bHtml) ;
if (bHtml) { Z << "</table>\n" "<table width=\"350\" align=\"center\" border=\"1\" cellspacing=\"0\" cellpadding=\"0\" " "style=\"text-decoration:none; font-family:verdana; font-size:11px; font-weight:normal; color:#000000;\">\n" "<tr>\n" "\t<th width=\"90\">Entity</th>\n" "\t<th width=\"90\">Assigned</th>\n" "\t<th width=\"90\">Prev</th>\n" "\t<th width=\"90\">TOTAL RAM</th>\n" "\t<th width=\"90\"></th>\n" "</tr>\n" ; } else { Z << "__________________________________________________________________________________________________________________\n\n" ; Z << "Entity__________ Assigned_____ Prev_________ Total RAM____\n" ; }
// Fixed Strings /* if (_hzGlobal_setStrings) { U = _hzGlobal_setStrings->Count() * sizeof(hzStrRepos) ; total += U ; _report_mem_itemA(Z, "Fix Str Repos :", _hzGlobal_setStrings->Count(), prev_StrTbl, U, bHtml) ;
U = _hzGlobal_setStrings->Blocks() * 65536 ; total += U ; _report_mem_itemA(Z, "Fix Str Blocks :", _hzGlobal_setStrings->Blocks(), prev_StrTbl, U, bHtml) ; }
_report_mem_itemA(Z, "Fixed Strings :", cms.m_numStrings, pms.m_numStrings, U, bHtml) ; */
// COLLECTIONS
U = cms.m_numLists * 8 ; total += U ; _report_mem_itemA(Z, "Lists :", cms.m_numLists, pms.m_numLists, U, bHtml) ; U = cms.m_numListDC * 32 ; total += U ; _report_mem_itemA(Z, "Lists (active) :", cms.m_numListDC, pms.m_numListDC, U, bHtml) ; U = cms.m_numSmaps * 64 ; total += U ; _report_mem_itemA(Z, "Maps(S) :", cms.m_numSmaps, pms.m_numSmaps, U, bHtml) ; U = cms.m_numMmaps * 64 ; total += U ; _report_mem_itemA(Z, "Maps(M) :", cms.m_numMmaps, pms.m_numMmaps, U, bHtml) ; U = cms.m_numSets * 64 ; total += U ; _report_mem_itemA(Z, "Sets :", cms.m_numSets, pms.m_numSets, U, bHtml) ; U = cms.m_numArrays * 64 ; total += U ; _report_mem_itemA(Z, "Arrays :", cms.m_numArrays, pms.m_numArrays, U, bHtml) ; U = cms.m_numVectors * 64 ; total += U ; _report_mem_itemA(Z, "Vectors :", cms.m_numVectors, pms.m_numVectors, U, bHtml) ; U = cms.m_numIsams * 64 ; total += U ; _report_mem_itemA(Z, "ISAMS :", cms.m_numIsams, pms.m_numIsams, U, bHtml) ; U = cms.m_numIsamIndx * 64 ; total += U ; _report_mem_itemA(Z, "ISAM Blk Index :", cms.m_numIsamIndx, pms.m_numIsamIndx, U, bHtml) ; U = cms.m_numIsamData * 64 ; total += U ; _report_mem_itemA(Z, "ISAM Blk Data :", cms.m_numIsamData, pms.m_numIsamData, U, bHtml) ; U = cms.m_numBitmaps * 64 ; total += U ; _report_mem_itemA(Z, "Bitmaps :", cms.m_numBitmaps, pms.m_numBitmaps, U, bHtml) ; U = cms.m_numBitmapSB * 64 ; total += U ; _report_mem_itemA(Z, "Bitmap Segments:", cms.m_numBitmapSB, pms.m_numBitmapSB, U, bHtml) ; U = cms.m_numDochtm * 64 ; total += U ; _report_mem_itemA(Z, "Doc HTML :", cms.m_numDochtm, pms.m_numDochtm, U, bHtml) ; U = cms.m_numDocxml * 64 ; total += U ; _report_mem_itemA(Z, "Doc XML :", cms.m_numDocxml, pms.m_numDocxml, U, bHtml) ; U = cms.m_numBincron * 64 ; total += U ; _report_mem_itemA(Z, "BinCrons :", cms.m_numBincron, pms.m_numBincron, U, bHtml) ; U = cms.m_numBinstore * 64 ; total += U ; _report_mem_itemA(Z, "BinStores :", cms.m_numBinstore, pms.m_numBinstore, U, bHtml) ;
// TOTAL if (bHtml) { Z << "<tr><td>TOTAL EST:</td><td></td><td></td><td></td><td></td><td></td><td></td>" ; Z.Printf("<td align=\"right\">%s</td></tr>\n", _snh(total)) ; Z << "</table>\n\n" ; } else { Z << "__________________________________________________________\n" ; Z.Printf("TOTAL EST: %s\n", _snt(total)) ; }
ReportStringAllocations(Z, bHtml) ; }
void ReportStringAllocations (hzChain& Z, bool bHtml) { // Category: Diagnostics
hzMeminfo cms ; // Current snapshot uint32_t N ; // Number of string spaces used (for a given size) uint32_t U ; // Memory used by N uint32_t F ; // Number of string spaces free (for a given size) uint32_t V ; // Memory used by F
uint32_t nss = 0 ; // Number of string spaces in use uint32_t fss = 0 ; // Number of string spaces free
uint32_t mcu ; // Total memory consumed by used string spaces uint32_t mcf ; // Total memory consumed by free string spaces //uint32_t fre ; // Total memory held in free lists
cms = _hzGlobal_Memstats ;
// Report number of string superblocks _report_mem_item3(Z, "512K Superblocks:", cms.m_numSblks, 0, cms.m_numSblks * 589832, bHtml) ;
N = cms.m_strSm_u[0] ; nss+=N ; U=N*8 ; mcu+=U ; F = cms.m_strSm_f[0] ; fss+=F ; V=F*9 ; mcf+=V ; _report_mem_item3(Z, "Strings (008)", N, F, U, bHtml) ; N = cms.m_strSm_u[1] ; nss+=N ; U=N*16 ; mcu+=U ; F = cms.m_strSm_f[1] ; fss+=F ; V=F*16 ; mcf+=V ; _report_mem_item3(Z, "Strings (016)", N, F, U, bHtml) ; N = cms.m_strSm_u[2] ; nss+=N ; U=N*24 ; mcu+=U ; F = cms.m_strSm_f[2] ; fss+=F ; V=F*24 ; mcf+=V ; _report_mem_item3(Z, "Strings (024)", N, F, U, bHtml) ; N = cms.m_strSm_u[3] ; nss+=N ; U=N*32 ; mcu+=U ; F = cms.m_strSm_f[3] ; fss+=F ; V=F*32 ; mcf+=V ; _report_mem_item3(Z, "Strings (032)", N, F, U, bHtml) ; N = cms.m_strSm_u[4] ; nss+=N ; U=N*40 ; mcu+=U ; F = cms.m_strSm_f[4] ; fss+=F ; V=F*40 ; mcf+=V ; _report_mem_item3(Z, "Strings (040)", N, F, U, bHtml) ; N = cms.m_strSm_u[5] ; nss+=N ; U=N*48 ; mcu+=U ; F = cms.m_strSm_f[5] ; fss+=F ; V=F*48 ; mcf+=V ; _report_mem_item3(Z, "Strings (048)", N, F, U, bHtml) ; N = cms.m_strSm_u[6] ; nss+=N ; U=N*56 ; mcu+=U ; F = cms.m_strSm_f[6] ; fss+=F ; V=F*56 ; mcf+=V ; _report_mem_item3(Z, "Strings (056)", N, F, U, bHtml) ; N = cms.m_strSm_u[7] ; nss+=N ; U=N*64 ; mcu+=U ; F = cms.m_strSm_f[7] ; fss+=F ; V=F*64 ; mcf+=V ; _report_mem_item3(Z, "Strings (064)", N, F, U, bHtml) ; N = cms.m_strSm_u[8] ; nss+=N ; U=N*72 ; mcu+=U ; F = cms.m_strSm_f[8] ; fss+=F ; V=F*72 ; mcf+=V ; _report_mem_item3(Z, "Strings (072)", N, F, U, bHtml) ; N = cms.m_strSm_u[9] ; nss+=N ; U=N*80 ; mcu+=U ; F = cms.m_strSm_f[9] ; fss+=F ; V=F*80 ; mcf+=V ; _report_mem_item3(Z, "Strings (080)", N, F, U, bHtml) ; N = cms.m_strSm_u[10] ; nss+=N ; U=N*88 ; mcu+=U ; F = cms.m_strSm_f[10] ; fss+=F ; V=F*88 ; mcf+=V ; _report_mem_item3(Z, "Strings (088)", N, F, U, bHtml) ; N = cms.m_strSm_u[11] ; nss+=N ; U=N*96 ; mcu+=U ; F = cms.m_strSm_f[11] ; fss+=F ; V=F*96 ; mcf+=V ; _report_mem_item3(Z, "Strings (096)", N, F, U, bHtml) ; N = cms.m_strSm_u[12] ; nss+=N ; U=N*104 ; mcu+=U ; F = cms.m_strSm_f[12] ; fss+=F ; V=F*104 ; mcf+=V ; _report_mem_item3(Z, "Strings (104)", N, F, U, bHtml) ; N = cms.m_strSm_u[13] ; nss+=N ; U=N*112 ; mcu+=U ; F = cms.m_strSm_f[13] ; fss+=F ; V=F*112 ; mcf+=V ; _report_mem_item3(Z, "Strings (112)", N, F, U, bHtml) ; N = cms.m_strSm_u[14] ; nss+=N ; U=N*120 ; mcu+=U ; F = cms.m_strSm_f[14] ; fss+=F ; V=F*120 ; mcf+=V ; _report_mem_item3(Z, "Strings (120)", N, F, U, bHtml) ; N = cms.m_strSm_u[15] ; nss+=N ; U=N*128 ; mcu+=U ; F = cms.m_strSm_f[15] ; fss+=F ; V=F*128 ; mcf+=V ; _report_mem_item3(Z, "Strings (128)", N, F, U, bHtml) ; N = cms.m_strSm_u[16] ; nss+=N ; U=N*136 ; mcu+=U ; F = cms.m_strSm_f[16] ; fss+=F ; V=F*136 ; mcf+=V ; _report_mem_item3(Z, "Strings (136)", N, F, U, bHtml) ; N = cms.m_strSm_u[17] ; nss+=N ; U=N*144 ; mcu+=U ; F = cms.m_strSm_f[17] ; fss+=F ; V=F*144 ; mcf+=V ; _report_mem_item3(Z, "Strings (144)", N, F, U, bHtml) ; N = cms.m_strSm_u[18] ; nss+=N ; U=N*152 ; mcu+=U ; F = cms.m_strSm_f[18] ; fss+=F ; V=F*152 ; mcf+=V ; _report_mem_item3(Z, "Strings (152)", N, F, U, bHtml) ; N = cms.m_strSm_u[19] ; nss+=N ; U=N*160 ; mcu+=U ; F = cms.m_strSm_f[19] ; fss+=F ; V=F*160 ; mcf+=V ; _report_mem_item3(Z, "Strings (160)", N, F, U, bHtml) ; N = cms.m_strSm_u[20] ; nss+=N ; U=N*168 ; mcu+=U ; F = cms.m_strSm_f[20] ; fss+=F ; V=F*168 ; mcf+=V ; _report_mem_item3(Z, "Strings (168)", N, F, U, bHtml) ; N = cms.m_strSm_u[21] ; nss+=N ; U=N*176 ; mcu+=U ; F = cms.m_strSm_f[21] ; fss+=F ; V=F*176 ; mcf+=V ; _report_mem_item3(Z, "Strings (176)", N, F, U, bHtml) ; N = cms.m_strSm_u[22] ; nss+=N ; U=N*184 ; mcu+=U ; F = cms.m_strSm_f[22] ; fss+=F ; V=F*184 ; mcf+=V ; _report_mem_item3(Z, "Strings (184)", N, F, U, bHtml) ; N = cms.m_strSm_u[23] ; nss+=N ; U=N*192 ; mcu+=U ; F = cms.m_strSm_f[23] ; fss+=F ; V=F*192 ; mcf+=V ; _report_mem_item3(Z, "Strings (192)", N, F, U, bHtml) ; N = cms.m_strSm_u[24] ; nss+=N ; U=N*200 ; mcu+=U ; F = cms.m_strSm_f[24] ; fss+=F ; V=F*200 ; mcf+=V ; _report_mem_item3(Z, "Strings (200)", N, F, U, bHtml) ; N = cms.m_strSm_u[25] ; nss+=N ; U=N*208 ; mcu+=U ; F = cms.m_strSm_f[25] ; fss+=F ; V=F*208 ; mcf+=V ; _report_mem_item3(Z, "Strings (208)", N, F, U, bHtml) ; N = cms.m_strSm_u[26] ; nss+=N ; U=N*216 ; mcu+=U ; F = cms.m_strSm_f[26] ; fss+=F ; V=F*216 ; mcf+=V ; _report_mem_item3(Z, "Strings (216)", N, F, U, bHtml) ; N = cms.m_strSm_u[27] ; nss+=N ; U=N*224 ; mcu+=U ; F = cms.m_strSm_f[27] ; fss+=F ; V=F*224 ; mcf+=V ; _report_mem_item3(Z, "Strings (224)", N, F, U, bHtml) ; N = cms.m_strSm_u[28] ; nss+=N ; U=N*232 ; mcu+=U ; F = cms.m_strSm_f[28] ; fss+=F ; V=F*232 ; mcf+=V ; _report_mem_item3(Z, "Strings (232)", N, F, U, bHtml) ; N = cms.m_strSm_u[29] ; nss+=N ; U=N*240 ; mcu+=U ; F = cms.m_strSm_f[29] ; fss+=F ; V=F*240 ; mcf+=V ; _report_mem_item3(Z, "Strings (240)", N, F, U, bHtml) ; N = cms.m_strSm_u[30] ; nss+=N ; U=N*248 ; mcu+=U ; F = cms.m_strSm_f[30] ; fss+=F ; V=F*248 ; mcf+=V ; _report_mem_item3(Z, "Strings (248)", N, F, U, bHtml) ; N = cms.m_strSm_u[31] ; nss+=N ; U=N*256 ; mcu+=U ; F = cms.m_strSm_f[31] ; fss+=F ; V=F*256 ; mcf+=V ; _report_mem_item3(Z, "Strings (256)", N, F, U, bHtml) ;
// Oversized strings U = cms.m_ramStrOver ; mcu += U ; _report_mem_item3(Z, "Strings (ovr)", cms.m_numStrOver, 0, U, bHtml) ;
// Report number of string (use) nss += cms.m_numStrings ; U = cms.m_numStrings * sizeof(hzString) ; mcu += U ; _report_mem_item3(Z, "Population (incl copies)", cms.m_numStrings, 0, U, bHtml) ; _report_mem_item3(Z, "Total string spaces", nss, fss, mcu, bHtml) ; }
static void _report_heap (hzChain& Z, const hzMeminfo& cms, const hzMeminfo& pms, uint32_t& total, bool bHtml) { // Category: Diagnostics // // Support function to ReportMemoryUsage(). Provides Report on global new/delete override by the application, if applicable // // Argument: Z The hzChain instance to receive the report // Returns: None
uint32_t usage ; // Memory used by given object class uint32_t nA ; // Iterator for all sizes
Z << "<div class=\"stdpg\">\n" "<table width=\"506\" align=\"center\" border=\"1\" cellspacing=\"0\" cellpadding=\"0\" " "style=\"text-decoration:none; font-family:verdana; font-size:11px; font-weight:normal; color:#000000;\">\n" "<tr>\n" "\t<th width=\"120\">HadronZoo Entity</th>\n" "\t<th width=\"90\">Assigned</th>\n" "\t<th width=\"90\">Free</th>\n" "\t<th width=\"90\">Total</th>\n" "\t<th width=\"110\">Total RAM</th>\n" "</tr>\n" ;
// Managed Z << "<tr align=\"right\"><td align=\"left\">Heap 8-byte objects</td>" ; usage = (s_Heap->nU008 + s_Heap->nF008) * 8 ; Z.Printf("<td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>\n", _snh(s_Heap->nU008), _snh(s_Heap->nF008), _snh(s_Heap->nU008 + s_Heap->nF008), _snh(usage)) ;
Z << "<tr align=\"right\"><td align=\"left\">Heap 16-byte objects</td>" ; usage = (s_Heap->nU016 + s_Heap->nF016) * 16 ; Z.Printf("<td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>\n", _snh(s_Heap->nU016), _snh(s_Heap->nF016), _snh(s_Heap->nU016 + s_Heap->nF016), _snh(usage)) ;
Z << "<tr align=\"right\"><td align=\"left\">Heap 24-byte objects</td>" ; usage = (s_Heap->nU024 + s_Heap->nF024) * 24 ; Z.Printf("<td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>\n", _snh(s_Heap->nU024), _snh(s_Heap->nF024), _snh(s_Heap->nU024 + s_Heap->nF024), _snh(usage)) ;
Z << "<tr align=\"right\"><td align=\"left\">Heap 32-byte objects</td>" ; usage = (s_Heap->nU032 + s_Heap->nF032) * 32 ; Z.Printf("<td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>\n", _snh(s_Heap->nU032), _snh(s_Heap->nF032), _snh(s_Heap->nU032 + s_Heap->nF032), _snh(usage)) ;
Z << "<tr align=\"right\"><td align=\"left\">Heap 40-byte objects</td>" ; usage = (s_Heap->nU040 + s_Heap->nF040) * 40 ; Z.Printf("<td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>\n", _snh(s_Heap->nU040), _snh(s_Heap->nF040), _snh(s_Heap->nU040 + s_Heap->nF040), _snh(usage)) ;
// Print report for oversized objects for each multiple of 8 bytes for which there are outstanding allocatons Z << "<tr><td>Oversize (48+ bytes)</td><td></td><td></td><td></td></tr>\n" ;
for (nA = 0 ; nA < 1001 ; nA++) { if (!s_Heap->m_allSizes[nA] && !s_Heap->m_allSizeF[nA]) continue ;
Z.Printf("<tr align=\"right\"><td align=\"left\">Obj size %d</td>", nA * 8) ; usage = s_Heap->m_allSizes[nA] * nA * 8 ; Z.Printf("<td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>\n", _snh(s_Heap->m_allSizeA[nA]), _snh(s_Heap->m_allSizeF[nA]), _snh(s_Heap->m_allSizes[nA]), _snh(usage)) ; }
Z << "<tr align=\"right\"><td align=\"left\">All objects</td>" ; Z.Printf("<td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>\n", _snh(s_Heap->nCallsMalloc), _snh(s_Heap->nCallsFree), _snh(s_Heap->nCallsMalloc - s_Heap->nCallsFree), _snh(s_Heap->nOverSize)) ;
Z << "</table>\n" "</div>\n" ; }
void ReportMemoryUsage (hzChain& Z, bool bHtml) { // Category: Diagnostics // // Provides Report on memory usage by the application. // // Please note, the HTML generated by this function is partial and comprises only the table of interest. // // Argument: Z The hzChain instance to receive the report // Returns: None
static hzMeminfo pms = _hzGlobal_Memstats ; // Previous/initial memory statistics
hzMeminfo cms ; // Current memory stats hzXDate now ; // Current date and time uint32_t total = 0 ; // Total memory usage
now.SysDateTime() ;
// Take current snapshot cms = _hzGlobal_Memstats ;
if (bHtml) Z.Printf("<p><center>Memory Report at %s</center></p>\n", *now) ; else Z.Printf("Memory Report at %s\n", *now) ;
// If no heap, this is presented as collections on the LHS and string/chains on the RHS in one 'row'. If there is a heap we have string/chains on the top // LHS, collections on the bottom LHS and the heap activity on the RHS if (s_Heap) { // Two reports on LHS if (bHtml) { Z << "<table width=\"1400\" slign=\"center\">\n" "<tr>\n" " <td valign=\"top\">\n" " <table width=\"750\" slign=\"center\">\n" " <tr>\n" " <td>\n" ;
_report_objects(Z, cms, pms, total, bHtml) ;
Z << " </td>\n" " </tr>\n" " </table>\n" " </td>\n" " <td width=\"15\"> </td>\n" " <td>\n" " <table width=\"550\" slign=\"center\">\n" " <tr>\n" " <td>\n" ;
// Heap on RHS _report_heap(Z, cms, pms, total, bHtml) ;
Z << " </td>\n" " </tr>\n" " </table>\n" " </td>\n" "</tr>\n" "</table>\n" ; } else { _report_objects(Z, cms, pms, total, bHtml) ; _report_heap(Z, cms, pms, total, bHtml) ; } } else { // collection on LHS, string chans on RHS if (!bHtml) _report_objects(Z, cms, pms, total, bHtml) ; else { Z << "<table width=\"1400\" slign=\"center\">\n" "<tr>\n" " <td valign=\"top\">\n" ;
_report_objects(Z, cms, pms, total, bHtml) ;
Z << " </td>\n" "</tr>\n" "</table>\n" ; } }
pms = cms ; }
void RecordMemoryUsage (bool bHtml) { // Category: Diagnostics // // Provides Report on memory usage by the application and writes it to the logfile for the current thread // // Arguments: None // Returns: None
hzChain Z ; // Report output chain hzLogger* pLog ; // Current thread logger
pLog = GetThreadLogger() ; if (!pLog) return ;
pLog->Out("Start of Memory Report\n") ; ReportMemoryUsage(Z, bHtml) ; pLog->Out(Z) ; }
void ReportIsamUsage (hzChain& Z) { // Category: Diagnostics // // Provides Report on ISAM usage by the application. // // Argument: Z The hzChain instance to receive the report // Returns: None
hzXDate now ; // Current date and time
now.SysDateTime() ;
Z.Clear() ; Z.Printf("<p><center>ISAM Report at %s</center></p>\n", *now) ;
Z << "<html>\n" "<head>\n" "<style>\n" ".main { text-decoration:none; font-family:verdana; font-size:11px; font-weight:normal; color:#000000; }\n" ".stdpg { height:600px; border:0px; margin-left:5px; overflow-x:auto; overflow-y:auto; }\n" "</style>\n" "</head>\n" "<body>\n" "<div class=\"stdpg\">\n" "<table width=\"70%\" align=\"center\" border=\"1\" cellspacing=\"0\" cellpadding=\"0\" " "style=\"text-decoration:none; font-family:verdana; font-size:11px; font-weight:normal; color:#000000;\">\n" "<tr>\n" "\t<th>Type</th>\n" "\t<th>Name</th>\n" "\t<th>Blocks</th>\n" "\t<th>Object Size</th>\n" "\t<th>Objects</th>\n" "\t<th>Total RAM</th>\n" "</tr>\n" ;
Z << "<tr align=\"right\"><td align=\"left\">hzMapS</td><td align=\"left\">_hzGlobal_BinDataCrons</td>" ; Z << "<tr align=\"right\"><td align=\"left\">hzMapS</td><td align=\"left\">_hzGlobal_BinDataStores</td>" ; Z << "<tr align=\"right\"><td align=\"left\">hzMapS</td><td align=\"left\">_hzGlobal_Datatypes</td>" ; Z << "<tr align=\"right\"><td align=\"left\">hzMapS</td><td align=\"left\">_hzGlobal_Enums</td>" ; Z << "<tr align=\"right\"><td align=\"left\">hzMapS</td><td align=\"left\">_hzGlobal_Classes</td>" ; Z << "<tr align=\"right\"><td align=\"left\">hzMapS</td><td align=\"left\">_hzGlobal_setitories</td>" ; Z << "<tr align=\"right\"><td align=\"left\">hzMapS</td><td align=\"left\">s_SSIncludes</td>" ; Z << "<tr align=\"right\"><td align=\"left\">hzMapS</td><td align=\"left\">s_PageStore</td>" ; Z << "<tr align=\"right\"><td align=\"left\">hzMapS</td><td align=\"left\">_hzGlobal_blockedIPs</td>" ; Z << "<tr align=\"right\"><td align=\"left\">hzMapS</td><td align=\"left\">s_mimesFile</td>" ; Z << "<tr align=\"right\"><td align=\"left\">hzMapS</td><td align=\"left\">s_mimesDesc</td>" ; Z << "<tr align=\"right\"><td align=\"left\">hzMapS</td><td align=\"left\">s_mimesEnum</td>" ; Z << "<tr align=\"right\"><td align=\"left\">hzMapS</td><td align=\"left\">_hzGlobal_Userlist</td>" ; Z << "<tr align=\"right\"><td align=\"left\">hzMapS</td><td align=\"left\">_hzGlobal_Grouplist</td>" ;
Z << "</table>\n" "</div>\n" "</body>\n" "</html>\n" ; }