#if defined(macintosh) #include <types.h> #else #include <sys/types.h> #endif #include <stdio.h> #include <time.h> #include <string.h> #include <assert.h> #include <stdlib.h> #include <stdarg.h> #include "merc.h" #include "db.h" /********************************************************* * settings */ #define STRSPACE_DEFAULT (2*1028) // unit is kB // purposely set too low, so that you'll see how it works #define STRSPACE_INCREMENT 299200 // unit is kB #define MPROGS_INSTALLED // comment out if you do not have Ivan's (or compatible) mob programs #define DOUBLE_LOGGING // I have stdout redirected to a log file and stderr writing normally // to the console. (That helps with debugging.) All log strings are // sent once to each stream if DOUBLE_LOGGING is defined, otherwise // only to stderr. #define SHOW_ALL_RGSIZES // Comment out if you want memory report to show only used block sizes #define ALLOC_PERM_ALIGN sizeof(int) // use powers of two only // higher values may improve speed significantly on some machines, at the expense of memory. // 32 should be the fastest value on most machines // I don't recommend values below machine word length, but feel free to experiment // setting it to 1 won't save much, but slow things down considerably #define MAX_MEM_LIST 11 const size_t rgSizeList [MAX_MEM_LIST] = { 16, 32, 64, 128, 256, 1024, 2048, 4096, 8192, 16384, 32768-64 }; /********************************************************* * macros from mkw.h */ #ifndef __mkwMUD #define MAGIC_CHECKING // comment out disable internal assertion of memory allocation/recycling #define chwrite( ch, str ) send_to_char( str, ch ) // I find send_to_char()'s syntax counter-intuitive #define object_free obj_free // easy fix for name inconsistency in merc #define OK 0 #define ERR 1 #endif /********************************************************* * imports */ extern char * string_space; extern int mobile_count; extern OBJ_DATA *obj_free; extern CHAR_DATA *char_free; extern DESCRIPTOR_DATA *descriptor_free; extern PC_DATA *pcdata_free; extern AFFECT_DATA *affect_free; extern NOTE_DATA *note_list,*idea_list,*penalty_list,*news_list,*changes_list; /********************************************************* * locals */ int has_system_auth( CHAR_DATA* ch ); int strspace_size_read(); int strspace_size_set( int newsize ); void strspace_size_default(); extern const size_t rgSizeList [MAX_MEM_LIST]; extern void * rgFreeList [MAX_MEM_LIST]; /**************************************************************** * Implementation section */ const char ver_mkwSys[]="mkwSys Version 2.01"; unsigned strspace_size; void* rgFreeList [MAX_MEM_LIST]; unsigned long nAllocGP[MAX_MEM_LIST] = { 0,0,0,0,0,0,0,0,0,0,0}; int nAllocPerm; size_t sAllocPerm = 0; int nPermBlocks = 0; void boot_fail(char* module_name) { logf( "********************* Reboot by %-11s *********************", module_name ); exit( EXIT_FAILURE ); } char* strspace_alloc() { strspace_size_read(); if ( ( string_space = calloc( 1, strspace_size * 1024 ) ) == NULL ) { logf( "mkwSys.c: Unable to allocate %d kB string space from system.", strspace_size ); if( strspace_size > STRSPACE_DEFAULT ) strspace_size_default(); else logf( "Increase your system's virtual memory size.", strspace_size ); boot_fail( "mkwSys.c" ); } return string_space; } void strspace_size_default() { strspace_size_set( STRSPACE_DEFAULT ); logf( "mkwSys.c: String space size reset to program default of %d kB.", strspace_size ); } void strspace_size_increment() { logf( "mkwSys.c: String space size of %d kB did not suffice.", strspace_size ); strspace_size_set( strspace_size + STRSPACE_INCREMENT ); logf( "mkwSys.c: Incrementing string space size to %d kB and rebooting.", strspace_size ); boot_fail( "mkwSys.c" ); } int strspace_size_set( int newsize ) { if( newsize > 0 ) { FILE* memini= fopen( DATPATH "mkwSys.ini", "w" ); if( memini ) { fprintf( memini, "stringspace = %u\n", newsize ); fclose( memini ); return OK; } else { logf( "Unable to open mkwSys.ini for writing" ); } } return ERR; } int strspace_size_read() { FILE* memini= fopen( DATPATH "mkwSys.ini", "r" ); if( memini ) { strspace_size = 0; while( str_cmp( fread_word( memini ), "stringspace" ) ) ; fscanf( memini, " = %u", &strspace_size ); fclose( memini ); if( !strspace_size ) { logf( "mkwSys.c: String space size not set in mkwSys.ini, using default." ); strspace_size_default(); } else return OK; } else { logf( "mkwSys.c: Unable to open memsize.ini for reading, using default string space size." ); strspace_size_default(); } return ERR; } int has_system_auth( CHAR_DATA* ch ) { if( get_trust( ch ) < CREATOR ) { send_to_char( "You are not authorized to issue system commands.\n\r", ch ); return FALSE; } else return TRUE; } #define IMP_COUNTER( type, start, px) \ int px##type() { type * p = start; int n = 0; while( p!=NULL ) { p= p->next; n++; } return n; } #define IMP_LIST_COUNTERS( type, prefix ) \ IMP_COUNTER( type, prefix##list, n ) IMP_COUNTER( type, prefix##free, f ) // these object types have no global list IMP_COUNTER ( PC_DATA, pcdata_free, f) IMP_COUNTER ( AFFECT_DATA, affect_free, f) // these do IMP_LIST_COUNTERS ( OBJ_DATA, object_ ); IMP_LIST_COUNTERS ( CHAR_DATA, char_ ); IMP_LIST_COUNTERS ( DESCRIPTOR_DATA, descriptor_ ); #ifdef MPROGS_INSTALLED IMP_COUNTER ( MPROG_CODE, mprog_list, n ); //IMP_LIST_COUNTER ( MPROG_LIST, ??? ); // not currently implemented // (There is no global list of MPROG_LISTs to count from) #endif int nNOTE_DATA() { NOTE_DATA* p; int n = 0; for( p=note_list;p!=NULL; p=p->next) n++; for( p=idea_list;p!=NULL; p=p->next) n++; for( p=changes_list;p!=NULL; p=p->next) n++; for( p=penalty_list;p!=NULL; p=p->next) n++; for( p=news_list;p!=NULL; p=p->next) n++; return n; } #define rpt( name, n, obj ) chprintf( ch, "%5d %-14s *%4d Bytes =%6d kB\n\r", \ n, name, sizeof(obj), (n) * sizeof(obj)/1024 ) // report on an object type #define rpt_dy( name, type ) { int n= n##type(); rpt( name, n, type ); } // report object with dynamic counting of list - // calls rpt() without evaluating n twice #define rptsd( st, sz ) chprintf( ch, "%-34s %5d kB\n\r", st, sz ) // report string and decimal #define rptdsd(n,st,sz) chprintf( ch, "%5d %-28s %5d kB\n\r", n, st, sz ) // report decimal, string, decimal const char separator[] = "-------------------------------------------\n\r"; void do_memory_perms( CHAR_DATA *ch, char* argument ) { int i; chwrite( ch, separator ); chwrite( ch, "Permanent memory objects in use\n\r"); chwrite( ch, separator ); rpt_dy( "Characters", CHAR_DATA ); rpt( "PCs", nCHAR_DATA() - mobile_count /* should be an accurate shortcut */, PC_DATA ); rpt_dy( "Descriptors", DESCRIPTOR_DATA ); rpt( "Mob Prototypes", top_mob_index, MOB_INDEX_DATA ); rpt_dy( "Objects", OBJ_DATA); rpt( "Obj Prototypes", top_obj_index, OBJ_INDEX_DATA); rpt( "Extra Descs", top_ed, EXTRA_DESCR_DATA ); rpt( "Affects", top_affect, AFFECT_DATA ); rpt( "Rooms", top_room, ROOM_INDEX_DATA ); rpt( "Exits", top_exit, EXIT_DATA ); rpt( "Shops", top_shop, SHOP_DATA ); rpt( "Areas", top_area, AREA_DATA ); rpt( "Resets", top_reset, RESET_DATA ); rpt( "Help entries", top_help, HELP_DATA ); rpt_dy( "Notes", NOTE_DATA); rpt( "Socials", social_count, struct social_type ); #ifdef MPROGS_INSTALLED rpt_dy( "Mob progs", MPROG_CODE ) #endif for( i= 0; i<MAX_MEM_LIST; i++) #ifndef SHOW_ALL_RGSIZES if( nAllocGP[i] ) #endif chprintf( ch, "%5d GP blocks of %6d Bytes =%6d kB\n\r", nAllocGP[i], rgSizeList[i], nAllocGP[i] * rgSizeList[i] / 1024); chwrite( ch, separator ); } #define rptf( name, type ) { int n= f##type(); rpt( name, n, type ); } void do_memory_freelists( CHAR_DATA *ch, char* argument ) { int i; chwrite( ch, separator ); chwrite( ch, "Memory objects waiting to be recycled\n\r"); chwrite( ch, separator ); rptf( "Characters", CHAR_DATA ); rptf( "PCs", PC_DATA ); rptf( "Descriptors", DESCRIPTOR_DATA ); rptf( "Objects", OBJ_DATA); rptf( "Affects", AFFECT_DATA ); for( i= 0; i<MAX_MEM_LIST; i++) #ifndef SHOW_ALL_RGSIZES if( rgFreeList[i] ) #endif { void* p; int n = 0; for(p= rgFreeList[i]; p != NULL; p= * ((void **) p ) ) n++; chprintf( ch, "%5d GP blocks of %6d Bytes =%6d kB\n\r", n, rgSizeList[i], n * rgSizeList[i] / 1024); } chwrite( ch, separator ); } void do_memory_heap( CHAR_DATA *ch, char* argument ) { chwrite( ch, separator ); chprintf( ch, "Perm Block size is set to %d kB\n\r", MAX_PERM_BLOCK/1024 ); chprintf( ch, "Object alignment is set to %d Bytes\n\r", ALLOC_PERM_ALIGN ); rptdsd( nPermBlocks , "Blocks allocated from heap", nPermBlocks * MAX_PERM_BLOCK/1024 ); rptdsd( nAllocPerm, "Permanent objects occupy", sAllocPerm/1024 ); rptsd( "Current alloc_perm() overhead", ( nPermBlocks * MAX_PERM_BLOCK - sAllocPerm ) / 1024 ); chwrite( ch, separator ); rptsd( "String Space allocated at DB boot", strspace_size ); rptdsd( nAllocString, "Strings in string space", sAllocString/1024 ); rptsd( "Excess string space", strspace_size - sAllocString/1024 ); chwrite( ch, separator ); rptsd( "Total managed heap memory", nPermBlocks * MAX_PERM_BLOCK/1024 + strspace_size ); chwrite( ch, separator ); } void do_memory_formats( CHAR_DATA *ch, char* argument ) { chwrite( ch, separator ); chprintf( ch, "Old format mobs: %d\n\rOld format objects: %d\n\r", top_mob_index - newmobs, top_obj_index - newobjs ); chwrite( ch, separator ); } #undef rpt #undef rpt_dy #undef rptsd #undef rptdsd /* Those macros are not for general use */ void do_memory_stringspace( CHAR_DATA *ch, char *argument ) { if( has_system_auth(ch) ) { char arg[MAX_INPUT_LENGTH]; int sz = 0; argument= one_argument( argument, arg ); if( is_number( arg ) ) sz= atoi( arg ); if( sz <= 0 || sz > 32000 ) { chwrite( ch, "Invalid parameter.\n\r" ); return; } if( strspace_size_set( sz ) == OK ) chprintf( ch, "String space size for next DB boot set to %d kB.\n\r", sz ); else chwrite( ch, "Error writing new setting to file.\n\r" ); } } void do_memory_help( CHAR_DATA *ch, char *argument ) { chwrite(ch, ver_mkwSys ); chwrite(ch, " copyright (c) 1998 Michael K. Weise\n\r" "See home.att.net/~mkw/mudframe.html for more information\n\n\r" "usage: memory <command>\n\r\ravailable commands:\n\r" "heap Report heap memory allocated by the memory manager\n\r" "perms Report managed memory objects in use by other modules\n\r" "freelists Report memory objects waiting to be recycled\n\r" "stringspace <size> Set string space size in kB for next reboot\n\r" "formats Report old format prototypes\n\r" "? This message\n\r" ); } void do_memory( CHAR_DATA *ch, char *argument ) { vinterpret( ch, argument, "perms", do_memory_perms, "heap", do_memory_heap, "freelists", do_memory_freelists, "stringspace", do_memory_stringspace, "formats", do_memory_formats, NULL, do_memory_help ); } int strswitch( const char* arg, ... ) { int i = 0; char* p; va_list caselist; if( arg[0] ) { va_start( caselist, arg ); while( (p = va_arg( caselist, char* )) != NULL ) { i++; if( !str_prefix( arg, p ) ) return i; } } return 0; } void vinterpret( CHAR_DATA *ch, char *argument, ... ) { char arg[MAX_INPUT_LENGTH]; char* iStr; DO_FUN *iFun; va_list caselist; va_start( caselist, argument ); argument= one_argument( argument, arg ); do { iStr = va_arg( caselist, char* ); iFun = va_arg( caselist, DO_FUN* ); } while( iStr != NULL && ( !arg[0] || str_prefix( arg, iStr ) ) ); if( iFun != NULL ) (*iFun)( ch, argument ); } size_t rgSize_lookup( size_t size ) { int iList; size += sizeof( int ); // room needed for integrity marker for ( iList = 0; iList < MAX_MEM_LIST; iList++ ) { if ( size <= rgSizeList[iList] ) return rgSizeList[iList] - sizeof( int ); } return 0; // size won't fit into any alloc_mem() block } /* * Request a GP memory block. * GP blocks must be returned using free_mem() when no longer needed. * Checks freelist for block of appropriate size, * allocates heap memory if none available */ void *alloc_mem( size_t sMem ) { void *pMem; int iList; #ifdef MAGIC_CHECKING int *magic; sMem += sizeof(*magic); #endif for ( iList = 0; iList < MAX_MEM_LIST; iList++ ) { if ( sMem <= rgSizeList[iList] ) break; } if ( iList == MAX_MEM_LIST ) { bug( "Alloc_mem: size %d too large.", sMem ); exit( EXIT_FAILURE ); } if ( rgFreeList[iList] == NULL ) { pMem = alloc_perm( rgSizeList[iList] ); nAllocGP[iList]++; } else { pMem = rgFreeList[iList]; rgFreeList[iList] = * ((void **) rgFreeList[iList]); } #ifdef MAGIC_CHECKING magic = (int *) pMem; *magic = MAGIC_NUM; pMem = (void*) (((char*)pMem) + sizeof (*magic)); #endif return pMem; } /* * This is the memory manager's interface to the system heap. * All memory allocated here is either permanently typecast * and managed by the object recycler, or managed by the GP * block manager. */ void *alloc_perm( size_t sMem ) { static int iMemPerm; static char *pMemPerm; void *pMem; while ( sMem % ALLOC_PERM_ALIGN != 0 ) sMem++; if ( sMem > MAX_PERM_BLOCK ) { bug( "Alloc_perm: %d too large.", sMem ); exit( EXIT_FAILURE ); } if ( pMemPerm == NULL || iMemPerm + sMem > MAX_PERM_BLOCK ) { iMemPerm = 0; nPermBlocks++; if ( ( pMemPerm = calloc( 1, MAX_PERM_BLOCK ) ) == NULL ) { perror( "Alloc_perm" ); exit( EXIT_FAILURE ); } } pMem = pMemPerm + iMemPerm; iMemPerm += sMem; nAllocPerm += 1; sAllocPerm += sMem; return pMem; } /* * Give a GP block back to the memory manager for recycling. */ void free_mem( void *pMem, size_t sMem ) { int iList; #ifdef MAGIC_CHECKING int *magic; pMem = (void*) (((char*)pMem) - sizeof (*magic)); magic = (int *) pMem; if (*magic != MAGIC_NUM) { bug("Attempt to recyle invalid memory of size %d.",sMem); bug((char*) pMem + sizeof(*magic),0); return; } *magic = 0; sMem += sizeof(*magic); #endif for ( iList = 0; iList < MAX_MEM_LIST; iList++ ) { if ( sMem <= rgSizeList[iList] ) break; } if ( iList == MAX_MEM_LIST ) { bug( "Free_mem: size %d too large.", sMem ); exit( EXIT_FAILURE ); } * ((void **) pMem) = rgFreeList[iList]; rgFreeList[iList] = pMem; return; } /* * All ANSI C compilers aught to have stdarg.h. * If not, #include <varargs.h> * and implement logf() in traditional C style */ int logf( const char *format, ... ) { va_list arglist; char *strtime; int status; va_start( arglist, format ); strtime = ctime( ¤t_time ); strtime[strlen(strtime)-1] = '\0'; fprintf( stderr, "%s :: ", strtime ); #ifdef DOUBLE_LOGGING fprintf( stdout, "%s :: ", strtime ); vfprintf( stdout, format, arglist ); fprintf( stdout, "\n" ); fflush( stdout ); #endif status = vfprintf( stderr, format, arglist ); va_end( arglist ); fprintf( stderr, "\n" ); fflush( stderr ); return status; } int chprintf( CHAR_DATA* ch, const char *format, ... ) { static char buf[MAX_STRING_LENGTH+1]; va_list arglist; int nChars= 0; va_start( arglist, format ); if ( format != NULL && format[0] && ch->desc != NULL ) { nChars = vsprintf( buf, format, arglist ); assert( nChars <= MAX_STRING_LENGTH ); write_to_buffer( ch->desc, buf, nChars ); } va_end( arglist ); return nChars; }