diff options
Diffstat (limited to '')
-rw-r--r-- | src/LYLeaks.c | 1169 |
1 files changed, 1169 insertions, 0 deletions
diff --git a/src/LYLeaks.c b/src/LYLeaks.c new file mode 100644 index 0000000..d082a77 --- /dev/null +++ b/src/LYLeaks.c @@ -0,0 +1,1169 @@ +/* + * $LynxId: LYLeaks.c,v 1.43 2018/12/27 23:48:37 Kamil.Dudka 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 <HTUtils.h> +#include <LYexit.h> +#include <LYLeaks.h> +#include <LYUtils.h> +#include <LYGlobalDefs.h> + +#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(LYLeaksPath); + 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); + } + } +} + +/* + * Check for leaked strdup() results -TD + */ +char *LYLeakStrdup(const char *source, + const char *cp_File, + const short ssi_Line) +{ + size_t length = strlen(source) + 1; + char *target = (char *) LYLeakMalloc(length, cp_File, ssi_Line); + + if (target != 0) { + memcpy(target, source, length); + } + return target; +} + +/* + * 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); + } + 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 */ |