/********************************************************************************/ /* REMARK: set tab width to 4 spaces for best format */ /********************************************************************************/ /********************************************************************************/ /* */ /* Copyright (C) 1992 */ /* Centre de Recherche Public Henri Tudor (CRP-HT) */ /* 6, rue Coudenhove-Kalergi */ /* L1359 Luxembourg-Kirchberg */ /* */ /* Authors : Schmit Rene */ /* */ /* This software may be copied, distributed, ported and modified in source or */ /* object format as long as : */ /* */ /* 1) No distribution for commercial purposes is made. */ /* 2) No third-party copyrights (such as runtime licenses) are involved */ /* 3) This copyright notice is not removed or changed. */ /* */ /* No responsibility is assumed for any damages that may result */ /* from any defect in this software. */ /* */ /********************************************************************************/ /* CURRENT IMPLEMENTATION LIMITS AND WISH-LIST: * Restrictions with VAX C, as DECs pre-processor is not ANSI-compliant * MEMDEBUG++ * Machine/OS-specific stuff DOS: far pointers block size limits Mac: Mac-specific routines NewPtr NewHandle ... * check_ValidPointer(): pass it a pointer, returns FALSE if not inside a user_allocated block (would be cool, eh) * External option setting * register_Pointer(): no error message when pointer is freed (useful for memory allocated by non-MEMDEBUGed library) * Use own random function, so rand() is not disturbed * Change tree library to more general version ? variable sized watchdog ? Print-out of pointer value (== address) */ /* Modifications: 6/14/1993: * Check memdebug disabling on memory error (in check_XXX() functions) --> Added! * #include twice in memdebug.h file --> was already done before *limit # of chars to be printed out as string --> limited to D_MaxRawDataStringLength * open_XXXFile() -> transform to a single function!!! --> one global function open_File() replaces two old functions 6/30/1993: * Add off-by-one watchdog option --> Added! * add two underscores to check_XXX() functions --> Added! 7/1/1993: * Check for realloc() with special parameters passed -> NULL pointer : call malloc -> 0-size : call free * Raw data printing -> only up to first \0 7/8/1993: * Correction in detab_String: was moving one character too much 8/19/1993: * changed memcheck to memdebug * Added segmentation pragma for macintosh * fflush() after printout of general statistics * set error file to line buffering 10/4/93: * added C++ C linkage specification to memdebug.h * added new and delete overloaded operator to memdebug.h 10/5/93: * overloaded operators new and delete moved to file memdebug.cp * changed memdebug.h file to reflect C++ modifications 10/6/93 * added macro VarInfo for use with new and delete, as we can't get this info automatically like with malloc() et al. 1/26/94 * bug in init: compared result of atexit() to NULL, now to 0 1/27/94 * bug in set & check sentinel functions: memory alignment error on access to end sentinel applied patch provided by Wilfred Mollenvanger and John Borst * added #include's for Sun * in memdebug.h: changed macro definitions of malloc() et al. for VAX compilers applied patch provided by David M. Stone 2/1/94 * Replaced function name max() by find_MaxValue(), as some compilers define a macro called max 2/11/94 * added definition for RAND_MAX for SUNs * malloc(0)/calloc(0,0) may be a potential portability problem. Added test in __check_malloc() and __check_calloc(), call them with size 1 instead * in memdebug.h :define __MSDOS__ when _MSDOS defined 2/15/94 * added a cast to long in memory alignment patch (cf 1/27/94) to support boundary checks on machines with 2 byte integers 2/16/94 * changed portability strategy for size_t type: using now unsigned long for all internal size-related data * added Col80 symbol: if defined, MEMDEBUG will TRY to limit line length to 80 characters 3/3/94 * Close output files upon termination, if != from standard streams */ #ifdef macintosh #pragma segment memdebug #endif #ifndef MEMDEBUG #define MEMDEBUG #endif #include #include #include #include #include #ifdef sun #define atexit on_exit #include /* for memset proto */ #ifndef RAND_MAX #define RAND_MAX INT_MAX #endif #endif #include "memdebug.h" /* #define Col80 */ #ifdef Col80 #define D_RawDataMaxSize 14 #define print_80ColLineFeed(FILE) fprintf(FILE,"\n ") #else #define print_80ColLineFeed(FILE) #endif /**************************************************************************/ /**************************** Utility Include *****************************/ /**************************************************************************/ #include "memfree.h" #include "memalpha.h" /**************************************************************************/ /* We have to undefine our allocation and free macros, as well as the memdebug functions because we use or define 'The Right Stuff' in this file. */ #undef malloc #undef calloc #undef realloc #undef free #undef generate_MemdebugError #undef print_MemdebugStatistics #undef set_MemdebugOptions #undef check_MemdebugError #define D_InternalMallocError 1 #define D_InternalFreeError 2 #define D_InternalOutOfMemoryError 3 #define D_ExitNotInstallable 4 #define D_CouldNotOpenStatisticsFile 5 #define D_CouldNotOpenErrorFile 6 #define D_MaxRawDataStringLength 80 /* Here come some old macros used for VERY primitive memory checking. They were used to check whether memdebug itself is more or less memory error free. int g_malloc = 0; int g_free = 0; #define malloc(a) (fprintf(stdout,"malloc %lu\n", g_malloc++), malloc(a) ) #define free(a) (fprintf(stdout,"free %lu %p\n",g_free++,a), free(a), a=0 ) */ #define wrap_Pointer(p) p = (void *) (((long *) (p)) + 1) #define unwrap_Pointer(p) p = (void *) (((long *) (p)) - 1) /**************************************************************************/ /**************************** File global data ****************************/ /**************************************************************************/ static FILE * g_StatisticsFile; /* output text files */ static FILE * g_ErrorFile; static t_OptionRecord g_OptionRecord; /* record containing options given with set_memdebugOptions */ static unsigned long g_CurrentNumber; /* number of currently allocated blocks(using malloc() & calloc() ) */ static unsigned long g_TotalNumber; static unsigned long g_MaxNumber; static unsigned long g_MallocCalls; /* number of calls to given routine */ static unsigned long g_CallocCalls; static unsigned long g_ReallocCalls; static unsigned long g_FreeCalls; static unsigned long g_CurrentSize; /* currently allocated amount of memory */ static unsigned long g_TotalSize; /* memory allocated since program start */ static unsigned long g_MaxSize; /* max. memory allocated since program start */ static unsigned long g_UnsuccessfulMallocs; /* contain number of unsuccessful calls to given routine */ static unsigned long g_UnsuccessfulCallocs; static unsigned long g_UnsuccessfulReallocs; static unsigned long g_IllegalReallocs; /* number of reallocs of illegal pointers */ static unsigned long g_IllegalFrees; /* number of frees of unallocated pointers */ static unsigned long g_SpuriousFrees; /* number of free(NULL) calls */ static unsigned long g_NonPortableMalloc; /* number of non-portable mallocs */ static unsigned long g_NonPortableCalloc; /* number of non-portable callocs */ static unsigned long g_NonPortableRealloc; /* number of non-portable reallocs (malloc/free) */ static unsigned long g_CorruptedBlock; /* number of corrupted blocks (bound check failed) */ static unsigned int g_WatchdogSize; /* size needed by watchdog's head and tail */ static t_CallSequenceLink * g_CallSequenceList; /* sequence list descriptors */ static t_CallSequenceLink * g_CallSequenceListEnd; static t_biState g_StatisticsPrinted; /* statistics are printed at least once */ static t_biState g_memdebugDeactivated; /* package deactivated if REAL memory problems */ /*************************************************************************/ /*********************** Local function prototypes ***********************/ /*************************************************************************/ static void detab_String ( char * p_String); static void init ( void); static unsigned long find_MaxValue ( unsigned long p1, unsigned long p2 ); static void set_Watchdog ( t_BlockDescriptor * p_BlockDescriptor ); static void check_Watchdog ( t_BlockDescriptor * p_BlockDescriptor, char * p_NameString ); static void open_File ( FILE ** p_FileDescriptor, char * p_FileName, int p_ErrorCode ); static void insert_BlockDes_into_SeqList ( t_BlockDescriptor * p_BlockDescriptor, t_CallKind p_CallKind, unsigned long p_Size, char * p_SizeExpression, char * p_FileName, long p_LineNumber ); static void print_CallSequenceList ( void); static void remove_CallSequence_from_List ( t_CallSequenceLink * p_Link ); static void delete_CallSequenceList ( void); static void print_NotFreeList (void); static void print_RawData ( void * p_Data, unsigned long p_DataLength); static void account_alloc ( void * p_Pointer, unsigned long p_Size, t_CallKind p_CallKind, char * p_SizeExpression, char * p_FileName, long p_LineNumber ); static void * account_realloc ( void * p_Pointer, unsigned long p_Size, char * p_NameString, char * p_SizeExpression, char * p_FileName, long p_LineNumber ); static t_ObjectState account_free ( void * p_Pointer, char * p_NameString, char * p_FileName, long p_LineNumber ); static void print_Location ( FILE * p_TargetFile, char * p_FileName, long p_LineNumber ); static void print_GeneralStatistics(void); static void print_FinalStatistics (void); static void check_MallocFreeBalance (void); static void cleanup_Data (void); static void print_AlphaList (void); /**************************************************************************/ /**************************** Local functions *****************************/ /**************************************************************************/ int compare_BlockPointers ( t_BlockDescriptor * p_Block1, t_BlockDescriptor * p_Block2) /* DESCRIPTION: Compares the numerical value (== adress) of two pointers CLASS: Application specific ALGORITHM USED: Simple substraction. Casts used to prevent warnings or other problems... WARNING: This routine is a potential portability problem, so beware when porting! REMARK: Used to store and retrieve the pointer in the trees */ { long l_difference; l_difference = ((unsigned long) (p_Block1->f_Pointer)) - ((unsigned long) (p_Block2->f_Pointer)); if (l_difference == 0) return 0; else if (l_difference > 0) return 1; else return -1; } /**************************************************************************/ int compare_Identifiers ( t_BlockDescriptor * p_Block1, t_BlockDescriptor * p_Block2) /* DESCRIPTION: Compares the identifiers denoting two blocks CLASS: Application specific ALGORITHM USED: Trivial. Function is kind of a short notation */ { return( strcmp(p_Block1->f_Identifier,p_Block2->f_Identifier)); } /**************************************************************************/ static void detab_String(char * p_String) /* DESCRIPTION: delete all horizontal tabs in a string CLASS: String Manipulation ALGORITHM USED: Look for tabs. Move trailing sub_string forward by one character (replacing the tab) REMARK: Uses strchr() function from string.h to find tabs Uses memmove to move substring. */ { char * l_TabPosition; while ((l_TabPosition = strchr(p_String,'\t')) != NULL) memmove(l_TabPosition,l_TabPosition+1,strlen(l_TabPosition)); } /**************************************************************************/ void treat_InternalError ( int p_ErrorCode ) /* DESCRIPTION: memdebug's error treatment routine. CLASS: Application specific ALGORITHM USED: Prints a final error message to the error file, cleans up all data and sets a global variable that deactivates all calls to memdebug routines. REMARK: This routine deactivates also all further calls to itself. The first two errors are used by the tree routines. During normal operation, these errors should never occur, however. */ { if (g_memdebugDeactivated) /* once is enough */ return; switch (p_ErrorCode) { case D_InternalMallocError : fprintf(g_ErrorFile,"memdebug deactivated. Malloc returned same pointer twice.\n"); break; case D_InternalFreeError : fprintf(g_ErrorFile,"memdebug deactivated. Internal free error.\n"); break; case D_InternalOutOfMemoryError : fprintf(g_ErrorFile,"memdebug deactivated. Internal out of memory.\n"); break; case D_ExitNotInstallable : fprintf(g_ErrorFile,"memdebug deactivated. Could not install exit function.\n"); break; case D_CouldNotOpenStatisticsFile : fprintf(g_ErrorFile,"memdebug deactivated. Could not open statistics file.\n"); break; case D_CouldNotOpenErrorFile : fprintf(g_ErrorFile,"memdebug deactivated. Could not open error file.\n"); break; } print_FinalStatistics(); cleanup_Data(); g_memdebugDeactivated = c_Yes; } /**************************************************************************/ static void close_Files (void) /* DESCRIPTION: Closes all output files CLASS: Application specific ALGORITHM USED: Checks if output streams are stdout or stderr and closes them if not so */ { if (g_StatisticsFile != stdout) fclose(g_StatisticsFile); if (g_ErrorFile != stderr) fclose(g_ErrorFile); } /**************************************************************************/ static void exit_memdebug (void) /* DESCRIPTION: Calls routines to print out final statistics and to make data clean-up CLASS: Application specific ALGORITHM USED: Trivial. REMARK: This routine is installed as exit function during program initialisation. It uses one exit() slot! */ { print_FinalStatistics (); check_MallocFreeBalance (); cleanup_Data (); close_Files (); } /**************************************************************************/ static void init (void) /* DESCRIPTION: Initialises all program globals. CLASS: Application specific ALGORITHM USED: Set all variables to program default value (0, stdout, stderr....) Call initialisation routines for the two trees Install exit routine This routine is called for each user function call, but executes only once. s_AlreadyCalled is used as a flag at the first call */ { static t_biState s_AlreadyCalled = c_No; if (s_AlreadyCalled) return; s_AlreadyCalled = c_Yes; g_CurrentNumber = 0; g_TotalNumber = 0; g_MaxNumber = 0; g_MallocCalls = 0; g_CallocCalls = 0; g_ReallocCalls = 0; g_FreeCalls = 0; g_CurrentSize = 0; g_TotalSize = 0; g_MaxSize = 0; g_UnsuccessfulMallocs = 0; g_IllegalReallocs = 0; g_IllegalFrees = 0; g_SpuriousFrees = 0; g_NonPortableMalloc = 0; g_NonPortableCalloc = 0; g_NonPortableRealloc = 0; g_CorruptedBlock = 0; g_StatisticsFile = stdout; g_ErrorFile = stderr; g_StatisticsPrinted = c_No; g_memdebugDeactivated = c_No; g_OptionRecord.f_GeneralStatistics = c_Yes, g_OptionRecord.f_AlphaList = c_Yes; g_OptionRecord.f_NotFreeList = c_Yes; g_OptionRecord.f_CallSequenceList = c_No; g_OptionRecord.f_PrintContents = c_Yes; g_OptionRecord.f_DestroyContents = c_Yes; g_OptionRecord.f_SpuriousFreeList = c_Yes; g_OptionRecord.f_GenerateErrorCount = 0; g_OptionRecord.f_MaximalMemoryAvailable = LONG_MAX; g_OptionRecord.f_StatisticsFileName = "stdout"; g_OptionRecord.f_ErrorFileName = "stderr"; g_WatchdogSize = 2 * sizeof (long); g_CallSequenceList = NULL; g_CallSequenceListEnd = NULL; create_NotFreeBlockTree (); create_AlphaBlockTree (); if (atexit(exit_memdebug) != 0) treat_InternalError(D_ExitNotInstallable); } /**************************************************************************/ static void insert_BlockDes_into_SeqList ( t_BlockDescriptor * p_BlockDescriptor, t_CallKind p_CallKind, unsigned long p_Size, char * p_SizeExpression, char * p_FileName, long p_LineNumber ) /* DESCRIPTION: Add a block descriptor to the seuence cal list CLASS: Application specific ALGORITHM USED: Create and fill the new block descriptor If list empty, new block is entire list, else append it at end of existing list. The backlink in the list is updated according to the memory function called. For more informstion, see documentation (TOOL-PM-1, page 8 and 9) REMARK: An error might occur during memory allocation for the new link */ { t_CallSequenceLink * l_NewSequenceLink; l_NewSequenceLink = (t_CallSequenceLink *) malloc(sizeof(t_CallSequenceLink)); if (l_NewSequenceLink == NULL) { treat_InternalError(D_InternalOutOfMemoryError); return; } l_NewSequenceLink->f_Next = NULL; l_NewSequenceLink->f_Previous = g_CallSequenceListEnd; l_NewSequenceLink->f_BlockDescriptor = p_BlockDescriptor; l_NewSequenceLink->f_CallKind = p_CallKind; l_NewSequenceLink->f_SizeExpression = p_SizeExpression; l_NewSequenceLink->f_Size = p_Size; l_NewSequenceLink->f_FileName = p_FileName; l_NewSequenceLink->f_LineNumber = p_LineNumber; if (g_CallSequenceList == NULL) g_CallSequenceList = l_NewSequenceLink; else g_CallSequenceListEnd->f_Next = l_NewSequenceLink; g_CallSequenceListEnd = l_NewSequenceLink; switch (p_CallKind) { case c_Malloc : case c_Calloc : l_NewSequenceLink->f_MallocLink = NULL; p_BlockDescriptor->f_BackLink = l_NewSequenceLink; break; case c_Realloc : l_NewSequenceLink->f_MallocLink = p_BlockDescriptor->f_BackLink; p_BlockDescriptor->f_BackLink = l_NewSequenceLink; break; case c_Free : l_NewSequenceLink->f_MallocLink = p_BlockDescriptor->f_BackLink; p_BlockDescriptor->f_BackLink = NULL; break; } } /**************************************************************************/ static void print_CallSequenceList (void) /* DESCRIPTION: Prints the sequence list of memory function calls CLASS: Application specific ALGORITHM USED: Print a header Iterate through list, printing the following information * Source File name * Source line number * Memory function name (malloc, calloc, realloc, free) * pointer identifier (if known) * block size in bytes * size expression */ { t_CallSequenceLink * l_Link; fprintf(g_StatisticsFile,"*************************************\n"); fprintf(g_StatisticsFile,"** Malloc/Free Call Sequence List **\n"); fprintf(g_StatisticsFile,"*************************************\n"); /* 123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890*/ fprintf(g_StatisticsFile, " Call Identifier Size Size Expression\n"); l_Link = g_CallSequenceList; while (l_Link) { fprintf(g_StatisticsFile,"File %-15s; Line %-5lu # ",l_Link->f_FileName,l_Link->f_LineNumber); switch (l_Link->f_CallKind) { case c_Malloc : fprintf(g_StatisticsFile,"Malloc "); break; case c_Calloc : fprintf(g_StatisticsFile,"Calloc "); break; case c_Realloc : fprintf(g_StatisticsFile,"Realloc "); break; case c_Free : fprintf(g_StatisticsFile,"Free "); break; } if (l_Link->f_BlockDescriptor->f_Identifier) fprintf(g_StatisticsFile,"%-35.35s : ",l_Link->f_BlockDescriptor->f_Identifier); else fprintf(g_StatisticsFile,"?????? : "); /* 21 spaces! */ fprintf(g_StatisticsFile,"%6lu %s",l_Link->f_Size,l_Link->f_SizeExpression); fprintf(g_StatisticsFile,"\n"); l_Link = l_Link->f_Next; } fprintf(g_StatisticsFile, "\n"); } /**************************************************************************/ static void remove_CallSequence_from_List (t_CallSequenceLink * p_Link) /* DESCRIPTION: Removes a sequence descriptor from the call sequence list. CLASS: List manipulation ALGORITHM USED: Standard list ooperation. Deallocates memory of block when done. */ { if (p_Link->f_Previous != NULL) p_Link->f_Previous->f_Next = p_Link->f_Next; else g_CallSequenceList = p_Link->f_Next; if (p_Link->f_Next != NULL) p_Link->f_Next->f_Previous = p_Link->f_Previous; else g_CallSequenceListEnd = p_Link->f_Previous; free(p_Link); } /**************************************************************************/ static void delete_CallSequenceList (void) /* DESCRIPTION: Frees the whole call sequnece list CLASS: List manipulation ALGORITHM USED: Standard list operation. Deallocates all memory used by list. */ { t_CallSequenceLink * l_Link; t_CallSequenceLink * l_NextLink; l_Link = g_CallSequenceList; while (l_Link) { l_NextLink = l_Link->f_Next; free(l_Link); l_Link = l_NextLink; } } /**************************************************************************/ static void open_File ( FILE ** p_FileDescriptor, char * p_FileName, int p_ErrorCode ) /* DESCRIPTION: Opens a file CLASS: Application specific ALGORITHM USED: Close old file (allows for multiple such files, resetable with set_memdebugOptions) Open new file. stdout and stderr are treated separately (never opened or closed, file descriptor simply copied) REMARK: Should the open not be successful, stderr is reused for a final message... */ { int l_success; if (* p_FileDescriptor != stdout) l_success = fclose(* p_FileDescriptor); l_success++; if (!strcmp(p_FileName,"stdout")) * p_FileDescriptor = stdout; else if (!strcmp(p_FileName,"stderr")) g_ErrorFile = stderr; else * p_FileDescriptor = fopen(p_FileName,"w"); if (* p_FileDescriptor == NULL) { g_ErrorFile = stderr; treat_InternalError (p_ErrorCode); * p_FileDescriptor = stdout; } } /**************************************************************************/ static void set_Watchdog ( t_BlockDescriptor * p_BlockDescriptor ) /* DESCRIPTION: Set a blocks sentinels CLASS: Application specific ALGORITHM USED: Create a long random number store this value at: start of block end of block block-descriptor REMARK: The end sentinel value is written byte by byte to avoid memory alignment problems */ { long l_RandomNumber; long * l_Pointer; unsigned long l_Size; l_RandomNumber = rand() * RAND_MAX + rand(); l_Pointer = (long *) p_BlockDescriptor->f_Pointer; l_Size = p_BlockDescriptor->f_BackLink->f_Size; *((l_Pointer ) - 1) = l_RandomNumber; *((char *) (l_Pointer ) + l_Size ) = (unsigned char) ((l_RandomNumber >> (3*8)) & 255); *((char *) (l_Pointer ) + l_Size + 1) = (unsigned char) ((l_RandomNumber >> (2*8)) & 255); *((char *) (l_Pointer ) + l_Size + 2) = (unsigned char) ((l_RandomNumber >> (1*8)) & 255); *((char *) (l_Pointer ) + l_Size + 3) = (unsigned char) ((l_RandomNumber >> ( 0)) & 255); #if 0 *((long*) ((char *) (l_Pointer ) + l_Size)) = l_RandomNumber; #endif p_BlockDescriptor->f_WatchdogValue = l_RandomNumber; } /**************************************************************************/ static void check_Watchdog ( t_BlockDescriptor * p_BlockDescriptor, char * p_NameString ) /* DESCRIPTION: Tests via sentinels if block has not been corrupted CLASS: Application specific ALGORITHM USED: Retrieve value at begin and end of block, as well as from block_descriptor If the start or end value have changed, print out an error message REMARK: The end sentinel value is read byte by byte to avoid memory alignment problems */ { long l_WatchdogHead; long l_WatchdogTail; long l_WatchdogValue; long * l_Pointer; unsigned long l_Size; l_Pointer = (long *) p_BlockDescriptor->f_Pointer; l_Size = p_BlockDescriptor->f_BackLink->f_Size; l_WatchdogHead = *((l_Pointer) - 1); l_WatchdogTail = ((long)*((unsigned char *) (l_Pointer) + l_Size ) << (3*8)) + ((long)*((unsigned char *) (l_Pointer) + l_Size + 1) << (2*8)) + ((long)*((unsigned char *) (l_Pointer) + l_Size + 2) << (1*8)) + ((long)*((unsigned char *) (l_Pointer) + l_Size + 3) << ( 0)); #if 0 l_WatchdogTail = *((long*) ((char *) (l_Pointer ) + l_Size)); #endif l_WatchdogValue = p_BlockDescriptor->f_WatchdogValue; if (l_WatchdogHead != l_WatchdogValue) { g_CorruptedBlock++; fprintf(g_ErrorFile,"Memory block corrupted at start: %s\n", p_NameString); } if (l_WatchdogTail != l_WatchdogValue) { g_CorruptedBlock++; fprintf(g_ErrorFile,"Memory block corrupted at end : %s\n", p_NameString); } } /**************************************************************************/ static unsigned long find_MaxValue ( unsigned long p1, unsigned long p2) /* DESCRIPTION: Returns the largest of two input values CLASS: Application specific ALGORITHM USED: Trivial REMARK: If performance were important, we would (and could) use a macro... */ { return ( (p1 > p2) ? p1 : p2); } /**************************************************************************/ static void print_Location ( FILE * p_TargetFile, char * p_FileName, long p_LineNumber ) /* DESCRIPTION: Print file name and line number to target file CLASS: Application specific ALGORITHM USED: Trivial. Used for simplification. */ { fprintf(p_TargetFile,"File %-15s; Line %-5lu # ",p_FileName,p_LineNumber); } /**************************************************************************/ void print_AlphaBlock (t_BlockDescriptor * p_data) /* DESCRIPTION: Prints the content of an alphabetical block descriptor CLASS: Application specific ALGORITHM USED: Prints to the statistics file (on one line) * The pointer identifier * The number of times this identifier was used * The total number of bytes allocated for this identifier * The average size of this block REMARK: Called by the alphabetic tree iteration function */ { fprintf(g_StatisticsFile, "%-35.35s: ",p_data->f_Identifier); fprintf(g_StatisticsFile, "%6lu ",p_data->f_TimesUsed); fprintf(g_StatisticsFile, "%6lu ",p_data->f_TotalAllocated); fprintf(g_StatisticsFile, "%8.2f",(float)p_data->f_TotalAllocated / (float)p_data->f_TimesUsed); fprintf(g_StatisticsFile, "\n"); } /**************************************************************************/ static void print_AlphaList (void) /* DESCRIPTION: Print an alphabetically odered list of all blocks CLASS: Application specific ALGORITHM USED: Print a header Call the alphabetic tree iteration function */ { fprintf(g_StatisticsFile,"***************************************\n"); fprintf(g_StatisticsFile,"** Alphabetic pointer variable list **\n"); fprintf(g_StatisticsFile,"***************************************\n"); /* 123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890*/ fprintf(g_StatisticsFile, "Identifier Blocks Size Average Size\n"); print_AlphaBlockTree(); fprintf(g_StatisticsFile, "\n"); } /**************************************************************************/ static void print_RawData ( void * p_Data, unsigned long p_DataLength) /* DESCRIPTION: Prints out all printable characters up to \0 in a raw data block CLASS: Printing ALGORITHM USED: For all bytes in the data block Check whether byte is a printable ASCII character YES: print it NO: print a '.' instead */ { int l_i; char l_Character; #ifdef Col80 if (p_DataLength > D_RawDataMaxSize) p_DataLength = D_RawDataMaxSize; #endif for (l_i = 0; l_i < p_DataLength; l_i++) { if (l_i > D_MaxRawDataStringLength) break; l_Character = *((char *)p_Data + l_i); if (l_Character == '\0') break; if (isprint(l_Character)) fprintf(g_StatisticsFile,"%1c",l_Character); else fprintf(g_StatisticsFile,"."); } } /**************************************************************************/ void print_NotFreeBlock (t_BlockDescriptor * p_data) /* DESCRIPTION: Print the content of a not free block to the statistics file CLASS: Application specific ALGORITHM USED: Print to the statistics file (one per line) * the source file name & * the source line number of the allocation site of the block * the size * the size expression * the memory content as a string, terminated at the end by an \0 REMARK: Used by the notFree tree iteration function */ { fprintf(g_StatisticsFile, "File %-15s; Line %-5lu # ", p_data->f_BackLink->f_FileName, p_data->f_BackLink->f_LineNumber); fprintf(g_StatisticsFile, "%6lu %-20s", p_data->f_BackLink->f_Size, p_data->f_BackLink->f_SizeExpression); if (g_OptionRecord.f_PrintContents) print_RawData(p_data->f_Pointer,p_data->f_BackLink->f_Size); fprintf(g_StatisticsFile, "\n"); } /**************************************************************************/ static void print_NotFreeList (void) /* DESCRIPTION: Print a list of all blocks not yet freed CLASS: Application specific ALGORITHM USED: Print a header Call NotFree tree iteration function for printing NotFree blocks */ { if (g_CurrentNumber == 0) /* won't print out an empty list */ return; fprintf(g_StatisticsFile,"****************************************\n"); fprintf(g_StatisticsFile,"** List of currently allocated blocks **\n"); fprintf(g_StatisticsFile,"****************************************\n"); /* 123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890*/ fprintf(g_StatisticsFile, " Size Size Expression Content String\n"); print_NotFreeBlockTree(); fprintf(g_StatisticsFile, "\n"); } /**************************************************************************/ void delete_Descriptor (t_BlockDescriptor * p_data) /* DESCRIPTION: Deletes a block descriptor CLASS: Application specific ALGORITHM USED: Another name for free(). Used by the free iterator functions of the two trees */ { free (p_data); } /**************************************************************************/ static void account_alloc ( void * p_Pointer, unsigned long p_Size, t_CallKind p_CallKind, char * p_SizeExpression, char * p_FileName, long p_LineNumber ) /* DESCRIPTION: Registers a call to malloc() or calloc() CLASS: Application specific ALGORITHM USED: Update global statistics Create a block descriptor and fill it up Insert this block descriptor into * the call sequence list * the NotFree tree REMARK: Low real memory disables memdebug */ { t_BlockDescriptor * l_BlockDescriptor; g_TotalNumber ++; g_CurrentNumber ++; g_TotalSize += p_Size; g_CurrentSize += p_Size; g_MaxNumber = find_MaxValue(g_CurrentNumber,g_MaxNumber); g_MaxSize = find_MaxValue(g_CurrentSize, g_MaxSize); l_BlockDescriptor = (t_BlockDescriptor *) malloc(sizeof(t_BlockDescriptor)); if (l_BlockDescriptor == NULL) { treat_InternalError(D_InternalOutOfMemoryError); return; } l_BlockDescriptor->f_Pointer = p_Pointer; l_BlockDescriptor->f_Identifier = NULL; insert_BlockDes_into_SeqList ( l_BlockDescriptor, p_CallKind, p_Size, p_SizeExpression, p_FileName, p_LineNumber ); insert_NotFreeBlock_into_Tree(l_BlockDescriptor); set_Watchdog(l_BlockDescriptor); if ( check_NotFreeBlockFull() ) treat_InternalError(D_InternalOutOfMemoryError); } /**************************************************************************/ static void * account_realloc ( void * p_Pointer, unsigned long p_Size, char * p_NameString, char * p_SizeExpression, char * p_FileName, long p_LineNumber ) /* DESCRIPTION: Registers a call to realloc() CLASS: Application specific ALGORITHM USED: Check if reallocation is legal (pointer exists in NotFree tree) Test if out of memory simulation limit not reached Do a real realloc() and check if sucessful Remove old pointer from NotFree tree Update globl statistics Insert descriptor into sequence call list and NotFree tree REMARK: memdebug aborts if no internal memory available, else realloc() behaves normally */ { void * l_NewPointer; t_BlockDescriptor l_OldBlock; /* Temporary memory for search call */ t_BlockDescriptor * l_BlockDescriptor; unsigned long l_SizeDifference; l_OldBlock.f_Pointer = p_Pointer; l_BlockDescriptor = &l_OldBlock; if (!search_NotFreeBlock_in_Tree(&l_BlockDescriptor)) { print_Location(g_ErrorFile,p_FileName,p_LineNumber); fprintf(g_ErrorFile,"Reallocating unallocated block : %s\n",p_NameString); g_IllegalReallocs++; return(NULL); } else { if ((g_TotalSize - l_BlockDescriptor->f_BackLink->f_Size + p_Size) > g_OptionRecord.f_MaximalMemoryAvailable) { print_Location(g_ErrorFile,p_FileName,p_LineNumber); fprintf(g_ErrorFile,"Out of memory while reallocating %s for %s\n",p_SizeExpression,p_NameString); g_UnsuccessfulReallocs++; return (NULL); } else { check_Watchdog (l_BlockDescriptor,p_NameString); unwrap_Pointer (p_Pointer); l_NewPointer = realloc(p_Pointer,(size_t) p_Size + g_WatchdogSize); if (l_NewPointer == NULL) { print_Location(g_ErrorFile,p_FileName,p_LineNumber); fprintf(g_ErrorFile,"System out of memory while reallocating %s for %s\n",p_SizeExpression,p_NameString); g_UnsuccessfulReallocs++; } else { wrap_Pointer(l_NewPointer); remove_NotFreeBlock_from_Tree(l_BlockDescriptor); l_SizeDifference = p_Size - l_BlockDescriptor->f_BackLink->f_Size; l_BlockDescriptor->f_TotalAllocated += l_SizeDifference; g_CurrentSize += l_SizeDifference; g_MaxSize = find_MaxValue(g_CurrentSize, g_MaxSize); if ((l_SizeDifference) > 0) g_TotalSize += l_SizeDifference; l_BlockDescriptor->f_Pointer = l_NewPointer; l_BlockDescriptor->f_Identifier = NULL; insert_BlockDes_into_SeqList ( l_BlockDescriptor, c_Realloc, p_Size, p_SizeExpression, p_FileName, p_LineNumber ); insert_NotFreeBlock_into_Tree(l_BlockDescriptor); if ( check_NotFreeBlockFull() ) treat_InternalError(D_InternalOutOfMemoryError); set_Watchdog(l_BlockDescriptor); } } } return (l_NewPointer); } /**************************************************************************/ static t_ObjectState account_free ( void * p_Pointer, char * p_NameString, char * p_FileName, long p_LineNumber ) /* DESCRIPTION: Registers a call to free() CLASS: Application specific ALGORITHM USED: Search block in NotFree tree and remove it if found (else error returned) Fill in missing data Check if block already known (in alphabetic tree) yes: free block descriptor increase local statistics no: init local statistics insert block descriptor into tree Update global statistics Either remove block from sequence list, or add free sequence block to this list Fill released memory with a "@@@" string (NULL-terminated) REMARK: Out of real memory terminates memdebug */ { t_BlockDescriptor l_FreeBlock; t_BlockDescriptor * l_BlockDescriptor; t_BlockDescriptor * l_UnlinkedBlock; t_CallSequenceLink * l_MallocSequenceCall; unsigned long l_Size; l_FreeBlock.f_Pointer = p_Pointer; l_UnlinkedBlock = &l_FreeBlock; if (!search_NotFreeBlock_in_Tree(&l_UnlinkedBlock)) return(c_Bad); check_Watchdog(l_UnlinkedBlock,p_NameString); /* do it here, we need pointer */ remove_NotFreeBlock_from_Tree(l_UnlinkedBlock); l_MallocSequenceCall = l_UnlinkedBlock->f_BackLink; l_Size = l_MallocSequenceCall->f_Size; l_BlockDescriptor = l_UnlinkedBlock; l_BlockDescriptor->f_Pointer = NULL; l_BlockDescriptor->f_Identifier = p_NameString; if (! search_AlphaBlock_in_Tree(&l_BlockDescriptor)) { /* not found */ l_UnlinkedBlock->f_TimesUsed = 1; l_UnlinkedBlock->f_TotalAllocated = l_Size; insert_AlphaBlock_into_Tree(l_UnlinkedBlock); if ( check_AlphaBlockFull() ) treat_InternalError(D_InternalOutOfMemoryError); } else { /* found */ l_MallocSequenceCall->f_BlockDescriptor = l_BlockDescriptor; free(l_UnlinkedBlock); l_BlockDescriptor->f_TimesUsed ++; l_BlockDescriptor->f_TotalAllocated += l_Size; } l_BlockDescriptor->f_BackLink = NULL; g_CurrentNumber --; g_CurrentSize -= l_Size; if (g_OptionRecord.f_CallSequenceList) insert_BlockDes_into_SeqList ( l_BlockDescriptor, c_Free, l_Size, l_MallocSequenceCall->f_SizeExpression, p_FileName, p_LineNumber ); else { t_CallSequenceLink * l_DeleteSequenceCall; l_DeleteSequenceCall = l_MallocSequenceCall; while (l_DeleteSequenceCall != NULL) { l_MallocSequenceCall = l_MallocSequenceCall->f_MallocLink; remove_CallSequence_from_List(l_DeleteSequenceCall); l_DeleteSequenceCall = l_MallocSequenceCall; } } if (g_OptionRecord.f_DestroyContents) { memset(p_Pointer,'@',(size_t)l_Size-1); *( (char*) p_Pointer + l_Size - 1) = '\0'; } return(c_Ok); } /**************************************************************************/ static void print_GeneralStatistics(void) /* DESCRIPTION: Print global statistics CLASS: Application specific ALGORITHM USED: Trivial. */ { static int s_TimesCalled = 0; fprintf(g_StatisticsFile,"\n"); fprintf(g_StatisticsFile,"************************************\n"); fprintf(g_StatisticsFile,"** Memory usage statistics: %3d **\n",++s_TimesCalled); fprintf(g_StatisticsFile,"************************************\n"); fprintf(g_StatisticsFile,"\n"); fprintf (g_StatisticsFile,"Calls to Malloc %6lu\n", g_MallocCalls ); fprintf (g_StatisticsFile,"Calls to Calloc %6lu\n", g_CallocCalls ); fprintf (g_StatisticsFile,"Calls to Realloc %6lu\n", g_ReallocCalls ); fprintf (g_StatisticsFile,"Calls to Free %6lu\n", g_FreeCalls ); fprintf (g_StatisticsFile,"\n"); fprintf (g_StatisticsFile,"Total blocks allocated %6lu\n", g_TotalNumber ); fprintf (g_StatisticsFile,"Currently allocated blocks %6lu\n", g_CurrentNumber ); fprintf (g_StatisticsFile,"Same time allocated blocks %6lu\n", g_MaxNumber ); fprintf (g_StatisticsFile,"\n"); fprintf (g_StatisticsFile,"Total memory allocated %8lu\n", g_TotalSize ); fprintf (g_StatisticsFile,"Currently allocated memory %8lu\n", g_CurrentSize ); fprintf (g_StatisticsFile,"Same time allocated memory %8lu\n", g_MaxSize ); fprintf (g_StatisticsFile,"\n"); fprintf (g_StatisticsFile,"Unsuccessful malloc calls %6lu\n", g_UnsuccessfulMallocs ); fprintf (g_StatisticsFile,"Unsuccessful realloc calls %6lu\n", g_UnsuccessfulReallocs ); fprintf (g_StatisticsFile,"\n"); fprintf (g_StatisticsFile,"Realloc of unallocated blocks %6lu\n", g_IllegalReallocs ); fprintf (g_StatisticsFile,"Free of unallocated blocks %6lu\n", g_IllegalFrees ); fprintf (g_StatisticsFile,"Spurious free calls %6lu\n", g_SpuriousFrees ); fprintf (g_StatisticsFile,"Non-portable malloc calls %6lu\n", g_NonPortableMalloc ); fprintf (g_StatisticsFile,"Non-portable calloc calls %6lu\n", g_NonPortableCalloc ); fprintf (g_StatisticsFile,"Non-portable realloc calls %6lu\n", g_NonPortableRealloc ); fprintf (g_StatisticsFile,"Corrupted blocks %6lu\n", g_CorruptedBlock ); fprintf (g_StatisticsFile,"\n"); } /**************************************************************************/ static void print_FinalStatistics ( void ) /* DESCRIPTION: Print final statistics CLASS: Application specific ALGORITHM USED: If not yet printed, this function launches the printing of the statistics REMARK: Called by exit function */ { if (g_memdebugDeactivated) return; if (g_StatisticsPrinted == c_No) print_MemdebugStatistics(); } /**************************************************************************/ static void check_MallocFreeBalance ( void ) /* DESCRIPTION: Prints an erro messag eif not all memory allocations have been matched by a free CLASS: Application specific ALGORITHM USED: Trivial */ { if (g_memdebugDeactivated) return; if (g_CurrentNumber) fprintf(g_ErrorFile,"\n%lu pointers have not been freed.\n",g_CurrentNumber); } /**************************************************************************/ static void cleanup_Data ( void ) /* DESCRIPTION: Cleans up all data structures at end of program CLASS: Application specific ALGORITHM USED: Call clean-up functions of all data structures */ { if (g_memdebugDeactivated) return; delete_CallSequenceList (); free_NotFreeBlockTree (); delete_NotFreeBlockTree (); free_AlphaBlockTree (); delete_AlphaBlockTree (); } /**************************************************************************/ /**************************** Global functions ****************************/ /**************************************************************************/ void * __check_malloc( size_t p_Size, char * p_SizeExpression, char * p_FileName, long p_LineNumber ) /* DESCRIPTION: Replaces (and calls) standard malloc() CLASS: memdebug ALGORITHM USED: Init tool Update global statistics Call real malloc() adjust for watchdog account call and/or return error if not successful */ { void * l_NewBlock; init(); g_MallocCalls++; if (p_Size == 0) { g_NonPortableMalloc++; p_Size = 1; print_Location(g_ErrorFile,p_FileName,p_LineNumber); fprintf(g_ErrorFile,"Malloc used with block size 0. "); print_80ColLineFeed(g_ErrorFile); fprintf(g_ErrorFile,"Potential portability problem, calling malloc(1) instead\n"); } if (g_memdebugDeactivated) { l_NewBlock = malloc (p_Size + g_WatchdogSize); if (l_NewBlock) wrap_Pointer(l_NewBlock); return (l_NewBlock); } detab_String(p_SizeExpression); if ((g_TotalSize + p_Size) <= g_OptionRecord.f_MaximalMemoryAvailable) { l_NewBlock = malloc (p_Size + g_WatchdogSize); if (l_NewBlock) { wrap_Pointer(l_NewBlock); account_alloc(l_NewBlock,p_Size,c_Malloc,p_SizeExpression,p_FileName,p_LineNumber); return (l_NewBlock); } else { print_Location(g_ErrorFile,p_FileName,p_LineNumber); fprintf(g_ErrorFile,"System out of memory while allocating %s\n",p_SizeExpression); g_UnsuccessfulMallocs++; return (NULL); } } else { print_Location(g_ErrorFile,p_FileName,p_LineNumber); fprintf(g_ErrorFile,"Out of memory while allocating %s\n",p_SizeExpression); g_UnsuccessfulMallocs++; return (NULL); } } /**************************************************************************/ void * __check_calloc( size_t p_Number, size_t p_Size, char * p_SizeExpression, char * p_FileName, long p_LineNumber ) /* DESCRIPTION: Replaces (and calls) standard calloc() CLASS: memdebug ALGORITHM USED: Init tool Update global statistics Call malloc, set watchdog init to '0's with memset account call and/or return error if not successful */ { void * l_NewBlock; init(); g_CallocCalls++; if ( (p_Number == 0) || (p_Size == 0) ) { g_NonPortableCalloc++; p_Size = 1; p_Number = 1; print_Location(g_ErrorFile,p_FileName,p_LineNumber); fprintf(g_ErrorFile,"Calloc used to allocate 0 bytes. "); print_80ColLineFeed(g_ErrorFile); fprintf(g_ErrorFile,"Potential portability problem, calling as calloc(1,1) instead\n"); } if (g_memdebugDeactivated) { l_NewBlock = malloc((p_Number * p_Size) + g_WatchdogSize); if (l_NewBlock) { wrap_Pointer(l_NewBlock); l_NewBlock = memset(l_NewBlock,'\0',(p_Number * p_Size)); } return (l_NewBlock); } detab_String(p_SizeExpression); if ((g_TotalSize + (p_Size * p_Number) ) <= g_OptionRecord.f_MaximalMemoryAvailable) { l_NewBlock = malloc((p_Number * p_Size) + g_WatchdogSize); if (l_NewBlock) { wrap_Pointer(l_NewBlock); l_NewBlock = memset(l_NewBlock,'\0',(p_Number * p_Size)); account_alloc(l_NewBlock,(p_Size * p_Number),c_Calloc,p_SizeExpression,p_FileName,p_LineNumber); return (l_NewBlock); } else { print_Location(g_ErrorFile,p_FileName,p_LineNumber); fprintf(g_ErrorFile,"System out of memory while allocating %s\n",p_SizeExpression); g_UnsuccessfulCallocs++; return (NULL); } } else { print_Location(g_ErrorFile,p_FileName,p_LineNumber); fprintf(g_ErrorFile,"Out of memory reached while allocating %s\n",p_SizeExpression); g_UnsuccessfulCallocs++; return (NULL); } } /********************************************************************************/ void * __check_realloc ( void * p_Pointer, size_t p_Size, char * p_NameString, char * p_SizeExpression, char * p_FileName, long p_LineNumber) /* DESCRIPTION: Replaces (and calls) standard realloc() CLASS: memdebug ALGORITHM USED: Init tool Update global statistics Call account_realloc which takes over all action REMARKS: if used as malloc() or free(), an error is posted and the right function is called account_realloc does its own internal wrapping and unwrapping of the pointer */ { void * l_NewPointer; init(); /* needed if realloc used as malloc (i.e. pointer passed == NULL) */ if (p_Pointer == NULL) { g_NonPortableRealloc++; print_Location(g_ErrorFile,p_FileName,p_LineNumber); fprintf(g_ErrorFile,"Realloc used with NULL initial pointer. "); print_80ColLineFeed(g_ErrorFile); fprintf(g_ErrorFile,"Potential portability problem, calling malloc() instead\n"); return (__check_malloc(p_Size,p_SizeExpression,p_FileName,p_LineNumber)); } if (p_Size == 0) { g_NonPortableRealloc++; print_Location(g_ErrorFile,p_FileName,p_LineNumber); fprintf(g_ErrorFile,"Realloc used with new block size 0. "); print_80ColLineFeed(g_ErrorFile); fprintf(g_ErrorFile,"Potential portability problem, calling free() instead\n"); __check_free(p_Pointer,p_NameString,p_FileName,p_LineNumber); return (NULL); } g_ReallocCalls++; if (g_memdebugDeactivated) { unwrap_Pointer(p_Pointer); l_NewPointer = realloc(p_Pointer,p_Size); wrap_Pointer(l_NewPointer); return (l_NewPointer); } detab_String(p_NameString ); detab_String(p_SizeExpression ); l_NewPointer = account_realloc ( p_Pointer, p_Size, p_NameString, p_SizeExpression, p_FileName, p_LineNumber ); return (l_NewPointer); } /********************************************************************************/ void __check_free ( void * p_Pointer, char * p_NameString, char * p_FileName, long p_LineNumber) /* DESCRIPTION: Replaces (and calls) standard free() CLASS: memdebug ALGORITHM USED: Init tool Update global statistics Call real free(), account call and/or return error if not successful */ { t_ObjectState l_BlockStatus; init(); g_FreeCalls++; if ( g_memdebugDeactivated ) { unwrap_Pointer(p_Pointer); free(p_Pointer); return; } detab_String(p_NameString); if (p_Pointer != NULL) { l_BlockStatus = account_free(p_Pointer,p_NameString,p_FileName,p_LineNumber); unwrap_Pointer(p_Pointer); if (l_BlockStatus == c_Ok) free(p_Pointer); else { print_Location(g_ErrorFile,p_FileName,p_LineNumber); fprintf(g_ErrorFile,"Freeing unallocated block : %s\n",p_NameString); g_IllegalFrees++; } } else if (g_OptionRecord.f_SpuriousFreeList) { print_Location(g_ErrorFile,p_FileName,p_LineNumber); fprintf(g_ErrorFile,"Spurious free : %s\n",p_NameString); g_SpuriousFrees++; } } /********************************************************************************/ void generate_MemdebugError ( void ) /* DESCRIPTION: Enables the generation of memory allocation errors CLASS: memdebug ALGORITHM USED: Simply set available memory to 0 bytes (errors will then occur automatically...). */ { init(); if ( g_memdebugDeactivated ) return; if (g_OptionRecord.f_GenerateErrorCount-- <= 0) g_OptionRecord.f_MaximalMemoryAvailable = 0; /* it's as simple as that */ } /********************************************************************************/ void print_MemdebugStatistics ( void ) /* DESCRIPTION: Explicitly prints memory usage statistics CLASS: memdebug ALGORITHM USED: According to options used, call print functions of the different data structures REMARK: Once called, no more automatic final statistics will be printed. */ { init(); if ( g_memdebugDeactivated ) return; g_StatisticsPrinted = c_Yes; if (g_OptionRecord.f_GeneralStatistics) print_GeneralStatistics(); if (g_OptionRecord.f_AlphaList) print_AlphaList(); if (g_OptionRecord.f_NotFreeList) print_NotFreeList(); if (g_OptionRecord.f_CallSequenceList) print_CallSequenceList(); fflush(g_StatisticsFile); } /********************************************************************************/ int check_memdebugError ( void ) /* DESCRIPTION: Behind-the-scenes error check function CLASS: memdebug ALGORITHM USED: Set p_Error to sum of g_IllegalReallocs g_IllegalFrees g_SpuriousFrees REMARK: Once called, no more automatic final statistics will be printed. ATTENTION: When memdebug is not defined, the macro will always return 0 (== no error!) SUGGESTION: This function should be used for behind-the-scenes test programs. No final memory usage statistics are printed, but print_MemdebugStatistics() may be used to do so explicitly. */ { init(); if ( g_memdebugDeactivated ) return 0; g_StatisticsPrinted = c_Yes; return ( g_IllegalReallocs + g_IllegalFrees + g_SpuriousFrees + g_CorruptedBlock + g_NonPortableMalloc + g_NonPortableCalloc + g_NonPortableRealloc ); } /********************************************************************************/ void set_MemdebugOptions ( t_biState p_GeneralStatistics, t_biState p_AlphaList, t_biState p_NotFreeList, t_biState p_CallSequenceList, t_biState p_SpuriousFreeList, t_biState p_PrintContents, t_biState p_DestroyContents, long p_GenerateErrorCount, unsigned long p_MaximalMemoryAvailable, char * p_StatisticsFileName, char * p_ErrorFileName ) /* DESCRIPTION: Allows user to configure MEMCKECK CLASS: memdebug ALGORITHM USED: Copies parameters to global variables. Opens the given statistics and error file Set error file to line buffering */ { init(); if ( g_memdebugDeactivated ) return; g_OptionRecord.f_GeneralStatistics = p_GeneralStatistics, g_OptionRecord.f_AlphaList = p_AlphaList; g_OptionRecord.f_NotFreeList = p_NotFreeList, g_OptionRecord.f_CallSequenceList = p_CallSequenceList, g_OptionRecord.f_PrintContents = p_PrintContents; g_OptionRecord.f_DestroyContents = p_DestroyContents; g_OptionRecord.f_SpuriousFreeList = p_SpuriousFreeList; g_OptionRecord.f_GenerateErrorCount = p_GenerateErrorCount; if ( (p_MaximalMemoryAvailable) && (g_OptionRecord.f_MaximalMemoryAvailable == (unsigned long) LONG_MAX) ) g_OptionRecord.f_MaximalMemoryAvailable = p_MaximalMemoryAvailable; else g_OptionRecord.f_MaximalMemoryAvailable = (unsigned long) LONG_MAX; if (*p_StatisticsFileName) g_OptionRecord.f_StatisticsFileName = p_StatisticsFileName; else g_OptionRecord.f_StatisticsFileName = "stdout"; open_File(&g_StatisticsFile,g_OptionRecord.f_StatisticsFileName,D_CouldNotOpenStatisticsFile); if (*p_ErrorFileName) g_OptionRecord.f_ErrorFileName = p_ErrorFileName; else g_OptionRecord.f_ErrorFileName = "stderr"; open_File(&g_ErrorFile,g_OptionRecord.f_ErrorFileName,D_CouldNotOpenErrorFile); setvbuf(g_ErrorFile ,NULL,_IOLBF,255); } /********************************************************************************/