/* * $LynxId: LYLeaks.c,v 1.38 2016/11/24 15:35:29 tom Exp $ * * Copyright (c) 1994, University of Kansas, All Rights Reserved * (this file was rewritten twice - 1998/1999 and 2003/2004) * * This code will be used only if LY_FIND_LEAKS is defined. * * Revision History: * 05-26-94 created Lynx 2-3-1 Garrett Arch Blythe * 10-30-97 modified to handle StrAllocCopy() and * StrAllocCat(). - KW & FM * 07-23-07 free leaks of THIS module too -TD * 02-09-12 add bstring functions -TD */ /* * Disable the overriding of the memory routines for this file. */ #define NO_MEMORY_TRACKING #include #include #include #include #include #ifdef LY_FIND_LEAKS static AllocationList *ALp_RunTimeAllocations = NULL; #define LEAK_SUMMARY #ifdef LEAK_SUMMARY static size_t now_allocated = 0; static size_t peak_alloced = 0; static size_t total_alloced = 0; static size_t total_freed = 0; static long count_mallocs = 0; static long count_frees = 0; static void CountMallocs(size_t size) { ++count_mallocs; total_alloced += size; now_allocated += size; if (peak_alloced < now_allocated) peak_alloced = now_allocated; } static void CountFrees(size_t size) { ++count_frees; total_freed += size; now_allocated -= size; } #else #define CountMallocs(size) ++count_mallocs #define CountFrees(size) /* nothing */ #endif /* * Purpose: Add a new allocation item to the list. * Arguments: ALp_new The new item to add. * Return Value: void * Remarks/Portability/Dependencies/Restrictions: * Static function made to make code reusable in projects beyond * Lynx (some might ask why not use HTList). * Revision History: * 05-26-94 created Lynx 2-3-1 Garrett Arch Blythe */ static void AddToList(AllocationList * ALp_new) { /* * Just make this the first item in the list. */ ALp_new->ALp_Next = ALp_RunTimeAllocations; ALp_RunTimeAllocations = ALp_new; } /* * Purpose: Find the place in the list where vp_find is currently * tracked. * Arguments: vp_find A pointer to look for in the list. * Return Value: AllocationList * Either vp_find's place in the * list or NULL if not found. * Remarks/Portability/Dependencies/Restrictions: * Static function made to make code reusable in projects outside * of Lynx (some might ask why not use HTList). * Revision History: * 05-26-94 created Lynx 2-3-1 Garrett Arch Blythe */ static AllocationList *FindInList(void *vp_find) { AllocationList *ALp_find = ALp_RunTimeAllocations; /* * Go through the list of allocated pointers until end of list or vp_find * is found. */ while (ALp_find != NULL) { if (ALp_find->vp_Alloced == vp_find) { break; } ALp_find = ALp_find->ALp_Next; } return (ALp_find); } /* * Purpose: Remove the specified item from the list. * Arguments: ALp_del The item to remove from the list. * Return Value: void * Remarks/Portability/Dependencies/Restrictions: * Static function made to make code reusable in projects outside * of Lynx (some might ask why not use HTList). * Revision History: * 05-26-94 created Lynx 2-3-1 Garrett Arch Blythe */ static void RemoveFromList(AllocationList * ALp_del) { AllocationList *ALp_findbefore = ALp_RunTimeAllocations; /* * There is one special case, where the item to remove is the first in the * list. */ if (ALp_del == ALp_findbefore) { ALp_RunTimeAllocations = ALp_del->ALp_Next; } else { /* * Loop through checking all of the next values, if a match don't * continue. Always assume the item will be found. */ while (ALp_findbefore->ALp_Next != ALp_del) { ALp_findbefore = ALp_findbefore->ALp_Next; } /* * We are one item before the one to get rid of. Get rid of it. */ ALp_findbefore->ALp_Next = ALp_del->ALp_Next; } } /* * Make the malloc-sequence available for debugging/tracing. */ #ifndef LYLeakSequence long LYLeakSequence(void) { return count_mallocs; } #endif /* * Purpose: Print a report of all memory left unallocated by * Lynx code or attempted unallocations on * pointers that are not valid and then free * all unfreed memory. * Arguments: void * Return Value: void * Remarks/Portability/Dependencies/Restrictions: * This function should be registered for execution with the * atexit (stdlib.h) function as the first statement * in main. * All output of this function is sent to the file defined in * the header LYLeaks.h (LEAKAGE_SINK). */ void LYLeaks(void) { AllocationList *ALp_head; size_t st_total = (size_t) 0; FILE *Fp_leakagesink; CTRACE((tfp, "entering LYLeaks, flag=%d\n", LYfind_leaks)); if (LYfind_leaks == FALSE) { /* * Free MY leaks too, in case someone else is watching. */ while (ALp_RunTimeAllocations != NULL) { ALp_head = ALp_RunTimeAllocations; ALp_RunTimeAllocations = ALp_head->ALp_Next; free(ALp_head); } return; } /* * Open the leakage sink to take all the output. Recreate the file each * time. Do nothing if unable to open the file. */ Fp_leakagesink = LYNewTxtFile(LEAKAGE_SINK); if (Fp_leakagesink == NULL) { return; } while (ALp_RunTimeAllocations != NULL) { /* * Take the head off of the run time allocation list. */ ALp_head = ALp_RunTimeAllocations; ALp_RunTimeAllocations = ALp_head->ALp_Next; /* * Print the type of leak/error. Release memory when we no longer * need it. */ if (ALp_head->vp_Alloced == NULL) { /* * If there is realloc information on the bad request, then it was * a bad pointer value in a realloc statement. */ fprintf(Fp_leakagesink, "%s.\n", gettext("Invalid pointer detected.")); fprintf(Fp_leakagesink, "%s\t%ld\n", gettext("Sequence:"), ALp_head->st_Sequence); fprintf(Fp_leakagesink, "%s\t%p\n", gettext("Pointer:"), ALp_head->vp_BadRequest); /* * Don't free the bad request, it is an invalid pointer. If the * free source information is empty, we should check the realloc * information too since it can get passed bad pointer values also. */ if (ALp_head->SL_memory.cp_FileName == NULL) { fprintf(Fp_leakagesink, "%s\t%s\n", gettext("FileName:"), ALp_head->SL_realloc.cp_FileName); fprintf(Fp_leakagesink, "%s\t%d\n", gettext("LineCount:"), ALp_head->SL_realloc.ssi_LineNumber); } else { fprintf(Fp_leakagesink, "%s\t%s\n", gettext("FileName:"), ALp_head->SL_memory.cp_FileName); fprintf(Fp_leakagesink, "%s\t%d\n", gettext("LineCount:"), ALp_head->SL_memory.ssi_LineNumber); } } else { size_t i_counter; char *value = (char *) (ALp_head->vp_Alloced); /* * Increment the count of total memory lost and then print the * information. */ st_total += ALp_head->st_Bytes; fprintf(Fp_leakagesink, "%s\n", gettext("Memory leak detected.")); fprintf(Fp_leakagesink, "%s\t%ld\n", gettext("Sequence:"), ALp_head->st_Sequence); fprintf(Fp_leakagesink, "%s\t%p\n", gettext("Pointer:"), ALp_head->vp_Alloced); fprintf(Fp_leakagesink, "%s\t", gettext("Contains:")); for (i_counter = 0; i_counter < ALp_head->st_Bytes && i_counter < MAX_CONTENT_LENGTH; i_counter++) { if (isprint(UCH(value[i_counter]))) { fprintf(Fp_leakagesink, "%c", value[i_counter]); } else { fprintf(Fp_leakagesink, "|"); } } fprintf(Fp_leakagesink, "\n"); fprintf(Fp_leakagesink, "%s\t%d\n", gettext("ByteSize:"), (int) (ALp_head->st_Bytes)); fprintf(Fp_leakagesink, "%s\t%s\n", gettext("FileName:"), ALp_head->SL_memory.cp_FileName); fprintf(Fp_leakagesink, "%s\t%d\n", gettext("LineCount:"), ALp_head->SL_memory.ssi_LineNumber); /* * Give the last time the pointer was realloced if it happened * also. */ if (ALp_head->SL_realloc.cp_FileName != NULL) { fprintf(Fp_leakagesink, "%s\t%s\n", gettext("realloced:"), ALp_head->SL_realloc.cp_FileName); fprintf(Fp_leakagesink, "%s\t%d\n", gettext("LineCount:"), ALp_head->SL_realloc.ssi_LineNumber); } fflush(Fp_leakagesink); FREE(ALp_head->vp_Alloced); } /* * Create a blank line and release the memory held by the item. */ fprintf(Fp_leakagesink, "\n"); FREE(ALp_head); } /* * Give a grand total of the leakage. Close the output file. */ fprintf(Fp_leakagesink, "%s\t%u\n", gettext("Total memory leakage this run:"), (unsigned) st_total); #ifdef LEAK_SUMMARY fprintf(Fp_leakagesink, "%s\t%lu\n", gettext("Peak allocation"), (unsigned long) peak_alloced); fprintf(Fp_leakagesink, "%s\t%lu\n", gettext("Bytes allocated"), (unsigned long) total_alloced); fprintf(Fp_leakagesink, "%s\t%ld\n", gettext("Total mallocs"), count_mallocs); fprintf(Fp_leakagesink, "%s\t%ld\n", gettext("Total frees"), count_frees); #endif fclose(Fp_leakagesink); HTSYS_purge(LEAKAGE_SINK); } /* * Purpose: Capture allocations using malloc (stdlib.h) and track * the information in a list. * Arguments: st_bytes The size of the allocation requested * in bytes. * cp_File The file from which the request for * allocation came from. * ssi_Line The line number in cp_File where the * allocation request came from. * Return Value: void * A pointer to the allocated memory or NULL on * failure as per malloc (stdlib.h) * Remarks/Portability/Dependencies/Restrictions: * If no memory is allocated, then no entry is added to the * allocation list. * Revision History: * 05-26-94 created Lynx 2-3-1 Garrett Arch Blythe */ void *LYLeakMalloc(size_t st_bytes, const char *cp_File, const short ssi_Line) { void *vp_malloc; if (LYfind_leaks == FALSE) { vp_malloc = (void *) malloc(st_bytes); } else { /* * Do the actual allocation. */ vp_malloc = (void *) malloc(st_bytes); CountMallocs(st_bytes); /* * Only on successful allocation do we track any information. */ if (vp_malloc != NULL) { /* * Further allocate memory to store the information. Just return * on failure to allocate more. */ AllocationList *ALp_new = typecalloc(AllocationList); if (ALp_new != NULL) { /* * Copy over the relevant information. There is no need to * allocate more memory for the file name as it is a static * string anyway. */ ALp_new->st_Sequence = count_mallocs; ALp_new->vp_Alloced = vp_malloc; ALp_new->st_Bytes = st_bytes; ALp_new->SL_memory.cp_FileName = cp_File; ALp_new->SL_memory.ssi_LineNumber = ssi_Line; /* * Add the new item to the allocation list. */ AddToList(ALp_new); } } } return (vp_malloc); } /* * Purpose: Add information about new allocation to the list, * after a call to malloc or calloc or an equivalent * function which may or may not have already created * a list entry. * Arguments: vp_malloc The pointer to newly allocated memory. * Arguments: st_bytes The size of the allocation requested * in bytes. * cp_File The file from which the request for * allocation came from. * ssi_Line The line number in cp_File where the * allocation request came from. * Return Value: void * A pointer to the allocated memory or NULL on * failure. * Remarks/Portability/Dependencies/Restrictions: * If no memory is allocated, then no entry is added to the * allocation list. * Revision History: * 1999-02-08 created, modelled after LYLeakMalloc - kw */ AllocationList *LYLeak_mark_malloced(void *vp_malloced, size_t st_bytes, const char *cp_File, const short ssi_Line) { AllocationList *ALp_new = NULL; if (LYfind_leaks != FALSE) { /* * The actual allocation has already been done! * * Only on successful allocation do we track any information. */ if (vp_malloced != NULL) { /* * See if there is already an entry. If so, just update the source * location info. */ ALp_new = FindInList(vp_malloced); if (ALp_new) { ALp_new->SL_memory.cp_FileName = cp_File; ALp_new->SL_memory.ssi_LineNumber = ssi_Line; } else { /* * Further allocate memory to store the information. Just * return on failure to allocate more. */ ALp_new = typecalloc(AllocationList); if (ALp_new != NULL) { /* * Copy over the relevant information. */ ALp_new->vp_Alloced = vp_malloced; ALp_new->st_Bytes = st_bytes; ALp_new->SL_memory.cp_FileName = cp_File; ALp_new->SL_memory.ssi_LineNumber = ssi_Line; /* * Add the new item to the allocation list. */ AddToList(ALp_new); CountMallocs(st_bytes); } } } } return (ALp_new); } /* * Purpose: Capture allocations by calloc (stdlib.h) and * save relevant information in a list. * Arguments: st_number The number of items to allocate. * st_bytes The size of each item. * cp_File The file which wants to allocation. * ssi_Line The line number in cp_File requesting * the allocation. * Return Value: void * The allocated memory, or NULL on failure as * per calloc (stdlib.h) * Remarks/Portability/Dependencies/Restrictions: * If no memory can be allocated, then no entry will be added * to the list. * Revision History: * 05-26-94 created Lynx 2-3-1 Garrett Arch Blythe */ void *LYLeakCalloc(size_t st_number, size_t st_bytes, const char *cp_File, const short ssi_Line) { void *vp_calloc; if (LYfind_leaks == FALSE) { vp_calloc = (void *) calloc(st_number, st_bytes); } else { /* * Allocate the requested memory. */ vp_calloc = (void *) calloc(st_number, st_bytes); CountMallocs(st_bytes * st_number); /* * Only if the allocation was a success do we track information. */ if (vp_calloc != NULL) { /* * Allocate memory for the item to be in the list. If unable, just * return. */ AllocationList *ALp_new = typecalloc(AllocationList); if (ALp_new != NULL) { /* * Copy over the relevant information. There is no need to * allocate memory for the file name as it is a static string * anyway. */ ALp_new->st_Sequence = count_mallocs; ALp_new->vp_Alloced = vp_calloc; ALp_new->st_Bytes = (st_number * st_bytes); ALp_new->SL_memory.cp_FileName = cp_File; ALp_new->SL_memory.ssi_LineNumber = ssi_Line; /* * Add the item to the allocation list. */ AddToList(ALp_new); } } } return (vp_calloc); } /* * Purpose: Capture any realloc (stdlib.h) calls in order to * properly keep track of our run time allocation * table. * Arguments: vp_Alloced The previously allocated block of * memory to resize. If NULL, * realloc works just like * malloc. * st_newBytes The new size of the chunk of memory. * cp_File The file containing the realloc. * ssi_Line The line containing the realloc in cp_File. * Return Value: void * The new pointer value (could be the same) or * NULL if unable to resize (old block * still exists). * Remarks/Portability/Dependencies/Restrictions: * If unable to resize vp_Alloced, then no change in the * allocation list will be made. * If vp_Alloced is an invalid pointer value, the program will * exit after one last entry is added to the allocation list. * Revision History: * 05-26-94 created Lynx 2-3-1 Garrett Arch Blythe */ void *LYLeakRealloc(void *vp_Alloced, size_t st_newBytes, const char *cp_File, const short ssi_Line) { void *vp_realloc; AllocationList *ALp_renew; if (LYfind_leaks == FALSE) { vp_realloc = (void *) realloc(vp_Alloced, st_newBytes); } else if (vp_Alloced == NULL) { /* * If we are asked to resize a NULL pointer, this is just a malloc * call. */ vp_realloc = LYLeakMalloc(st_newBytes, cp_File, ssi_Line); } else { /* * Find the current vp_Alloced block in the list. If NULL, this is an * invalid pointer value. */ ALp_renew = FindInList(vp_Alloced); if (ALp_renew == NULL) { /* * Track the invalid pointer value and then exit. If unable to * allocate, just exit. */ AllocationList *ALp_new = typecalloc(AllocationList); if (ALp_new == NULL) { exit_immediately(EXIT_FAILURE); } /* * Set the information up; no need to allocate file name since it is a * static string. */ ALp_new->vp_Alloced = NULL; ALp_new->vp_BadRequest = vp_Alloced; ALp_new->SL_realloc.cp_FileName = cp_File; ALp_new->SL_realloc.ssi_LineNumber = ssi_Line; /* * Add the item to the list. Exit. */ AddToList(ALp_new); exit_immediately(EXIT_FAILURE); } /* * Perform the resize. If not NULL, record the information. */ vp_realloc = (void *) realloc(vp_Alloced, st_newBytes); CountFrees(ALp_renew->st_Bytes); CountMallocs(st_newBytes); if (vp_realloc != NULL) { ALp_renew->st_Sequence = count_mallocs; ALp_renew->vp_Alloced = vp_realloc; ALp_renew->st_Bytes = st_newBytes; /* * Update the realloc information, too. No need to allocate file name, * static string. */ ALp_renew->SL_realloc.cp_FileName = cp_File; ALp_renew->SL_realloc.ssi_LineNumber = ssi_Line; } } return (vp_realloc); } /* * Purpose: Add information about reallocated memory to the list, * after a call to realloc or an equivalent * function which has not already created or updated * a list entry. * Arguments: ALp_old List entry for previously allocated * block of memory to resize. If NULL, * mark_realloced works just like * mark_malloced. * vp_realloced The new pointer, after resizing. * st_newBytes The new size of the chunk of memory. * cp_File The file to record. * ssi_Line The line to record. * Return Value: Pointer to new or updated list entry * for this memory block. * NULL on allocation error. * Revision History: * 1999-02-11 created kw */ #if defined(LY_FIND_LEAKS) && defined(LY_FIND_LEAKS_EXTENDED) static AllocationList *mark_realloced(AllocationList * ALp_old, void *vp_realloced, size_t st_newBytes, const char *cp_File, const short ssi_Line) { /* * If there is no list entry for the old allocation, treat this as if a new * allocation had happened. */ if (ALp_old == NULL) { return (LYLeak_mark_malloced(vp_realloced, st_newBytes, cp_File, ssi_Line)); } /* * ALp_old represents the memory block before reallocation. Assume that if * we get here, there isn't yet a list entry for the new, possibly * different, address after realloc, that is our list hasn't been updated - * so we're going to do that now. */ if (vp_realloced != NULL) { ALp_old->vp_Alloced = vp_realloced; ALp_old->st_Bytes = st_newBytes; ALp_old->SL_realloc.cp_FileName = cp_File; ALp_old->SL_realloc.ssi_LineNumber = ssi_Line; } return (ALp_old); } #endif /* not LY_FIND_LEAKS and LY_FIND_LEAKS_EXTENDED */ /* * Purpose: Capture all requests to free information and also * remove items from the allocation list. * Arguments: vp_Alloced The memory to free. * cp_File The file calling free. * ssi_Line The line of cp_File calling free. * Return Value: void * Remarks/Portability/Dependencies/Restrictions: * If the pointer value is invalid, then an item will be added * to the list and nothing else is done. * I really like the name of this function and one day hope * that Lynx is Leak Free. * Revision History: * 05-26-94 created Lynx 2-3-1 Garrett Arch Blythe */ void LYLeakFree(void *vp_Alloced, const char *cp_File, const short ssi_Line) { AllocationList *ALp_free; if (LYfind_leaks == FALSE) { free(vp_Alloced); } else { /* * Find the pointer in the allocated list. If not found, bad pointer. * If found, free list item and vp_Alloced. */ ALp_free = FindInList(vp_Alloced); if (ALp_free == NULL) { /* * Create the final entry before exiting marking this error. If * unable to allocate more memory just exit. */ AllocationList *ALp_new = typecalloc(AllocationList); if (ALp_new == NULL) { exit_immediately(EXIT_FAILURE); } /* * Set up the information, no memory need be allocated for the file * name since it is a static string. */ ALp_new->vp_Alloced = NULL; ALp_new->vp_BadRequest = vp_Alloced; ALp_new->SL_memory.cp_FileName = cp_File; ALp_new->SL_memory.ssi_LineNumber = ssi_Line; /* * Add the entry to the list and then return. */ AddToList(ALp_new); } else { /* * Free off the memory. Take entry out of allocation list. */ CountFrees(ALp_free->st_Bytes); RemoveFromList(ALp_free); FREE(ALp_free); free(vp_Alloced); } } } /* * Allocates a new copy of a string, and returns it. * Tracks allocations by using other LYLeakFoo functions. * Equivalent to HTSACopy in HTString.c - KW */ char *LYLeakSACopy(char **dest, const char *src, const char *cp_File, const short ssi_Line) { if (src != NULL && src == *dest) { CTRACE((tfp, "LYLeakSACopy: *dest equals src, contains \"%s\"\n", src)); } else { if (*dest) { LYLeakFree(*dest, cp_File, ssi_Line); *dest = NULL; } if (src) { *dest = (char *) LYLeakMalloc(strlen(src) + 1, cp_File, ssi_Line); if (*dest == NULL) outofmem(__FILE__, "LYLeakSACopy"); strcpy(*dest, src); } } return *dest; } /* * String Allocate and Concatenate. * Tracks allocations by using other LYLeakFoo functions. * Equivalent to HTSACat in HTUtils.c - KW */ char *LYLeakSACat(char **dest, const char *src, const char *cp_File, const short ssi_Line) { if (src && *src) { if (src == *dest) { CTRACE((tfp, "LYLeakSACat: *dest equals src, contains \"%s\"\n", src)); } else if (*dest) { size_t length = strlen(*dest); *dest = (char *) LYLeakRealloc(*dest, (length + strlen(src) + 1), cp_File, ssi_Line); if (*dest == NULL) outofmem(__FILE__, "LYLeakSACat"); strcpy(*dest + length, src); } else { *dest = (char *) LYLeakMalloc((strlen(src) + 1), cp_File, ssi_Line); if (*dest == NULL) outofmem(__FILE__, "LYLeakSACat"); strcpy(*dest, src); } } return *dest; } /******************************************************************************/ /* * Equivalents for bstring functions in HTString.c -TD */ /* same as HTSABAlloc */ void LYLeakSABAlloc(bstring **dest, int len, const char *cp_File, const short ssi_Line) { if (*dest == 0) { *dest = LYLeakCalloc(1, sizeof(bstring), cp_File, ssi_Line); } if ((*dest)->len != len) { (*dest)->str = (char *) LYLeakRealloc((*dest)->str, (size_t) len, cp_File, ssi_Line); if ((*dest)->str == NULL) outofmem(__FILE__, "LYLeakSABalloc"); (*dest)->len = len; } } /* same as HTSABCopy */ void LYLeakSABCopy(bstring **dest, const char *src, int len, const char *cp_File, const short ssi_Line) { bstring *t; unsigned need = (unsigned) (len + 1); CTRACE2(TRACE_BSTRING, (tfp, "HTSABCopy(%p, %p, %d)\n", (void *) dest, (const void *) src, len)); LYLeakSABFree(dest, cp_File, ssi_Line); if (src) { if (TRACE_BSTRING) { CTRACE((tfp, "=== %4d:", len)); trace_bstring2(src, len); CTRACE((tfp, "\n")); } if ((t = (bstring *) LYLeakMalloc(sizeof(bstring), cp_File, ssi_Line)) == NULL) outofmem(__FILE__, "HTSABCopy"); if ((t->str = (char *) LYLeakMalloc(need, cp_File, ssi_Line)) == NULL) outofmem(__FILE__, "HTSABCopy"); MemCpy(t->str, src, len); t->len = len; t->str[t->len] = '\0'; *dest = t; } if (TRACE_BSTRING) { CTRACE((tfp, "=> %4d:", BStrLen(*dest))); trace_bstring(*dest); CTRACE((tfp, "\n")); } } /* same as HTSABCopy0 */ void LYLeakSABCopy0(bstring **dest, const char *src, const char *cp_File, const short ssi_Line) { LYLeakSABCopy(dest, src, (int) strlen(src), cp_File, ssi_Line); } /* same as HTSABCat */ void LYLeakSABCat(bstring **dest, const char *src, int len, const char *cp_File, const short ssi_Line) { bstring *t = *dest; CTRACE2(TRACE_BSTRING, (tfp, "HTSABCat(%p, %p, %d)\n", (void *) dest, (const void *) src, len)); if (src) { unsigned need = (unsigned) (len + 1); if (TRACE_BSTRING) { CTRACE((tfp, "=== %4d:", len)); trace_bstring2(src, len); CTRACE((tfp, "\n")); } if (t) { unsigned length = (unsigned) t->len + need; t->str = (char *) LYLeakRealloc(t->str, length, cp_File, ssi_Line); } else { if ((t = (bstring *) LYLeakCalloc(1, sizeof(bstring), cp_File, ssi_Line)) == NULL) outofmem(__FILE__, "HTSACat"); t->str = (char *) LYLeakMalloc(need, cp_File, ssi_Line); } if (t->str == NULL) outofmem(__FILE__, "HTSACat"); MemCpy(t->str + t->len, src, len); t->len += len; t->str[t->len] = '\0'; *dest = t; } if (TRACE_BSTRING) { CTRACE((tfp, "=> %4d:", BStrLen(*dest))); trace_bstring(*dest); CTRACE((tfp, "\n")); } } /* same as HTSABCat0 */ void LYLeakSABCat0(bstring **dest, const char *src, const char *cp_File, const short ssi_Line) { LYLeakSABCat(dest, src, (int) strlen(src), cp_File, ssi_Line); } /* same as HTSABFree */ void LYLeakSABFree(bstring **ptr, const char *cp_File, const short ssi_Line) { if (*ptr != NULL) { if ((*ptr)->str) LYLeakFree((*ptr)->str, cp_File, ssi_Line); LYLeakFree(*ptr, cp_File, ssi_Line); *ptr = NULL; } } /******************************************************************************/ #if defined(LY_FIND_LEAKS) && defined(LY_FIND_LEAKS_EXTENDED) const char *leak_cp_File_hack = __FILE__; short leak_ssi_Line_hack = __LINE__; /* * Purpose: A wrapper around StrAllocVsprintf (the workhorse of * HTSprintf/HTSprintf0, implemented in HTString.c) that * tries to make sure that our allocation list is always * properly updated, whether StrAllocVsprintf itself was * compiled with memory tracking or not (or even a mixture, * like tracking the freeing but not the new allocation). * Some source files can be compiled with LY_FIND_LEAKS_EXTENDED * in effect while others only have LY_FIND_LEAKS in effect, * and as long as HTString.c is complied with memory tracking * (of either kind) string objects allocated by HTSprintf/ * HTSprintf0 (or otherwise) can be passed around among them and * manipulated both ways. * Arguments: dest As for StrAllocVsprintf. * cp_File The source file of the caller (i.e. the * caller of HTSprintf/HTSprintf0, hopefully). * ssi_Line The line of cp_File calling. * inuse,fmt,ap As for StrAllocVsprintf. * Return Value: The char pointer to resulting string, as set * by StrAllocVsprintf, or * NULL if dest==0 (wrong use!). * Remarks/Portability/Dependencies/Restrictions: * The price for generality is severe inefficiency: several * list lookups are done to be on the safe side. * We don't get the real allocation size, only a minimum based * on the string length of the result. So the amount of memory * leakage may get underestimated. * If *dest is an invalid pointer value on entry (i.e. was not * tracked), the program will exit after one last entry is added * to the allocation list. * If StrAllocVsprintf fails to return a valid string via the * indirect string pointer (its first parameter), invalid memory * access will result and the program will probably terminate * with a signal. This can happen if, on entry, *dest is NULL * and fmt is empty or NULL, so just Don't Do That. * Revision History: * 1999-02-11 created kw * 1999-10-15 added comments kw */ static char *LYLeakSAVsprintf(char **dest, const char *cp_File, const short ssi_Line, size_t inuse, const char *fmt, va_list * ap) { AllocationList *ALp_old; void *vp_oldAlloced; const char *old_cp_File = __FILE__; short old_ssi_Line = __LINE__; if (!dest) return NULL; if (LYfind_leaks == FALSE) { StrAllocVsprintf(dest, inuse, fmt, ap); return (*dest); } vp_oldAlloced = *dest; if (!vp_oldAlloced) { StrAllocVsprintf(dest, inuse, fmt, ap); LYLeak_mark_malloced(*dest, strlen(*dest) + 1, cp_File, ssi_Line); return (*dest); } else { void *vp_realloced; ALp_old = FindInList(vp_oldAlloced); if (ALp_old == NULL) { /* * Track the invalid pointer value and then exit. If unable to * allocate, just exit. */ AllocationList *ALp_new = typecalloc(AllocationList); if (ALp_new == NULL) { exit_immediately(EXIT_FAILURE); } /* * Set the information up; no need to allocate file name since it * is a static string. */ ALp_new->vp_Alloced = NULL; ALp_new->vp_BadRequest = vp_oldAlloced; ALp_new->SL_realloc.cp_FileName = cp_File; ALp_new->SL_realloc.ssi_LineNumber = ssi_Line; /* * Add the item to the list. Exit. */ AddToList(ALp_new); exit_immediately(EXIT_FAILURE); } old_cp_File = ALp_old->SL_memory.cp_FileName; old_ssi_Line = ALp_old->SL_memory.ssi_LineNumber; /* * DO THE REAL WORK, by calling StrAllocVsprintf. If result is not * NULL, record the information. */ StrAllocVsprintf(dest, inuse, fmt, ap); vp_realloced = (void *) *dest; if (vp_realloced != NULL) { AllocationList *ALp_new = FindInList(vp_realloced); if (!ALp_new) { /* Look up again, list may have changed! - kw */ ALp_old = FindInList(vp_oldAlloced); if (ALp_old == NULL) { LYLeak_mark_malloced(*dest, strlen(*dest) + 1, cp_File, ssi_Line); return (*dest); } mark_realloced(ALp_old, *dest, strlen(*dest) + 1, cp_File, ssi_Line); return (*dest); } if (vp_realloced == vp_oldAlloced) { ALp_new->SL_memory.cp_FileName = old_cp_File; ALp_new->SL_memory.ssi_LineNumber = old_ssi_Line; ALp_new->SL_realloc.cp_FileName = cp_File; ALp_new->SL_realloc.ssi_LineNumber = ssi_Line; return (*dest); } /* Look up again, list may have changed! - kw */ ALp_old = FindInList(vp_oldAlloced); if (ALp_old == NULL) { ALp_new->SL_memory.cp_FileName = old_cp_File; ALp_new->SL_memory.ssi_LineNumber = old_ssi_Line; ALp_new->SL_realloc.cp_FileName = cp_File; ALp_new->SL_realloc.ssi_LineNumber = ssi_Line; } else { ALp_new->SL_memory.cp_FileName = old_cp_File; ALp_new->SL_memory.ssi_LineNumber = old_ssi_Line; ALp_new->SL_realloc.cp_FileName = cp_File; ALp_new->SL_realloc.ssi_LineNumber = ssi_Line; } } return (*dest); } } /* Note: the following may need updating if HTSprintf in HTString.c * is changed. - kw */ static char *LYLeakHTSprintf(char **pstr, const char *fmt,...) { char *str; size_t inuse = 0; va_list ap; LYva_start(ap, fmt); if (pstr != 0 && *pstr != 0) inuse = strlen(*pstr); str = LYLeakSAVsprintf(pstr, leak_cp_File_hack, leak_ssi_Line_hack, inuse, fmt, &ap); va_end(ap); return str; } /* Note: the following may need updating if HTSprintf0 in HTString.c * is changed. - kw */ static char *LYLeakHTSprintf0(char **pstr, const char *fmt,...) { char *str; va_list ap; LYva_start(ap, fmt); str = LYLeakSAVsprintf(pstr, leak_cp_File_hack, leak_ssi_Line_hack, 0, fmt, &ap); va_end(ap); return str; } /* * HTSprintf and HTSprintf0 will be defined such that they effectively call one * of the following two functions that store away a copy to the File & Line * info in temporary hack variables, and then call the real function (which is * returned here as a function pointer) to the regular HTSprintf/HTSprintf0 * arguments. It's probably a bit inefficient, but that shouldn't be * noticeable compared to all the time that memory tracking takes up for list * traversal. - kw */ HTSprintflike *Get_htsprintf_fn(const char *cp_File, const short ssi_Line) { leak_cp_File_hack = cp_File; leak_ssi_Line_hack = ssi_Line; return &LYLeakHTSprintf; } HTSprintflike *Get_htsprintf0_fn(const char *cp_File, const short ssi_Line) { leak_cp_File_hack = cp_File; leak_ssi_Line_hack = ssi_Line; return &LYLeakHTSprintf0; } #endif /* LY_FIND_LEAKS and LY_FIND_LEAKS_EXTENDED */ #else /* Standard C forbids an empty file */ void no_leak_checking(void); void no_leak_checking(void) { } #endif /* LY_FIND_LEAKS */