/*
* Copyright (c) 1994, University of Kansas, All Rights Reserved
*
* This code will be used only if LY_FIND_LEAKS is defined.
*/
/*
* Disable the overriding of the memory routines for this file.
*/
#define NO_MEMORY_TRACKING
#include "HTUtils.h"
#include "tcp.h"
#include "LYexit.h"
#include "LYLeaks.h"
#include <ctype.h>
/*#include <stdio.h> included by HTUtils.h -- FM */
#define FREE(x) if (x) {free(x); x = NULL;}
PRIVATE AllocationList *ALp_RunTimeAllocations = NULL;
PRIVATE void AddToList PARAMS((AllocationList *ALp_new));
PRIVATE AllocationList *FindInList PARAMS((void *vp_find));
PRIVATE void RemoveFromList PARAMS((AllocationList *ALp_del));
PUBLIC void LYLeaks NOARGS {
/*
* 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).
* Revision History:
* 05-26-94 created Lynx 2-3-1 Garrett Arch Blythe
*/
AllocationList *ALp_head;
size_t st_total = (size_t)0;
FILE *Fp_leakagesink;
/*
* Open the leakage sink to take all the output.
* Recreate the file each time.
* Do nothing if unable to open the file.
*/
Fp_leakagesink = fopen(LEAKAGE_SINK, "w");
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.
* Free off 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, "Invalid pointer detected.\n");
fprintf(Fp_leakagesink, "Pointer:\t%p\n", 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, "FileName:\t%s\n",
ALp_head->SL_realloc.cp_FileName);
fprintf(Fp_leakagesink, "LineCount:\t%d\n",
ALp_head->SL_realloc.ssi_LineNumber);
}
else {
fprintf(Fp_leakagesink, "FileName:\t%s\n",
ALp_head->SL_memory.cp_FileName);
fprintf(Fp_leakagesink, "LineCount:\t%d\n",
ALp_head->SL_memory.ssi_LineNumber);
}
}
else {
int i_counter;
/*
* Increment the count of total memory lost and
* then print the information.
*/
st_total += ALp_head->st_Bytes;
fprintf(Fp_leakagesink, "Memory leak detected.\n");
fprintf(Fp_leakagesink, "Pointer:\t%p\n", ALp_head->
vp_Alloced);
fprintf(Fp_leakagesink, "Contains:\t");
for(i_counter = 0; i_counter < ALp_head->st_Bytes &&
i_counter < MAX_CONTENT_LENGTH; i_counter++)
{
if(isprint(((char *)(ALp_head->vp_Alloced))[
i_counter])) {
fprintf(Fp_leakagesink, "%c", ((char *)
(ALp_head->vp_Alloced))[
i_counter]);
}
else {
fprintf(Fp_leakagesink, "|");
}
}
fprintf(Fp_leakagesink, "\n");
FREE(ALp_head->vp_Alloced);
fprintf(Fp_leakagesink, "ByteSize:\t%d\n", (int)
(ALp_head->st_Bytes));
fprintf(Fp_leakagesink, "FileName:\t%s\n", ALp_head->
SL_memory.cp_FileName);
fprintf(Fp_leakagesink, "LineCount:\t%d\n", 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, "realloced:\t%s\n",
ALp_head->SL_realloc.cp_FileName);
fprintf(Fp_leakagesink, "LineCount:\t%d\n",
ALp_head->SL_realloc.ssi_LineNumber);
}
}
/*
* 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, "\nTotal memory leakage this run:\t%u\n",
(unsigned)st_total);
fclose(Fp_leakagesink);
#ifdef VMS
{
char VMSfilename[256];
/* purge lower versions of the file */
sprintf(VMSfilename, "%s;-1", LEAKAGE_SINK);
while (remove(VMSfilename) == 0) ;
/* reset version number */
sprintf(VMSfilename, "%s;1", LEAKAGE_SINK);
rename(LEAKAGE_SINK, VMSfilename);
}
#endif /* VMS */
}
PUBLIC void *LYLeakMalloc ARGS3(size_t, st_bytes, CONST char *, cp_File, CONST
short, ssi_Line) {
/*
* 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
*/
/*
* Do the actual allocation.
*/
void *vp_malloc = (void *)malloc(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 = (AllocationList *)calloc(1,
sizeof(AllocationList));
if(ALp_new == NULL) {
return(vp_malloc);
}
/*
* Copy over the relevant information.
* There is no need to allocate more memory for the
* file name as it is a static string anyhow.
*/
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);
}
PUBLIC void *LYLeakCalloc ARGS4(size_t, st_number, size_t, st_bytes, CONST char
*, cp_File, CONST short, ssi_Line) {
/*
* 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
*/
/*
* Allocate the requested memory.
*/
void *vp_calloc = (void *)calloc(st_number, st_bytes);
/*
* 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 = (AllocationList *)calloc(1,
sizeof(AllocationList));
if(ALp_new == NULL) {
return(vp_calloc);
}
/*
* 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->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);
}
PUBLIC void *LYLeakRealloc ARGS4(void *, vp_Alloced, size_t, st_newBytes, CONST
char *, cp_File, CONST short, ssi_Line) {
/*
* 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 contianing 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 *vp_realloc;
AllocationList *ALp_renew;
/*
* If we are asked to resize a NULL pointer, this is just a
* malloc call.
*/
if(vp_Alloced == NULL) {
return(LYLeakMalloc(st_newBytes, cp_File, ssi_Line));
}
/*
* 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.
*/
auto AllocationList *ALp_new = (AllocationList *)calloc(1,
sizeof(AllocationList));
if(ALp_new == NULL) {
exit(-1);
}
/*
* Set the information up; no need to allocate file name
* since 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(-1);
}
/*
* Perform the resize.
* If not NULL, record the information.
*/
vp_realloc = (void *)realloc(vp_Alloced, st_newBytes);
if(vp_realloc != NULL) {
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);
}
PUBLIC void LYLeakFree ARGS3(void *, vp_Alloced, CONST char *, cp_File, CONST
short, ssi_Line) {
/*
* 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
*/
AllocationList *ALp_free;
/*
* Find the pointer in the allocated list.
* If not found, bad pointer.
* If found, free list item and vp_Allloced.
*/
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 = (AllocationList *)calloc(1,
sizeof(AllocationList));
if(ALp_new == NULL) {
exit(-1);
}
/*
* 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);
return;
}
else {
/*
* Free off the memory.
* Take entry out of allocation list.
*/
RemoveFromList(ALp_free);
FREE(ALp_free);
FREE(vp_Alloced);
}
}
PRIVATE void AddToList ARGS1(AllocationList *, ALp_new) {
/*
* 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
*/
/*
* Just make this the first item in the list.
*/
ALp_new->ALp_Next = ALp_RunTimeAllocations;
ALp_RunTimeAllocations = ALp_new;
}
PRIVATE AllocationList *FindInList ARGS1(void *, vp_find) {
/*
* 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
*/
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);
}
PRIVATE void RemoveFromList ARGS1(AllocationList *, ALp_del) {
/*
* 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 reusabel 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
*/
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;
return;
}
/*
* 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;
}