diff options
Diffstat (limited to 'clib/src/memory_leak_detection.c')
-rw-r--r-- | clib/src/memory_leak_detection.c | 1277 |
1 files changed, 1277 insertions, 0 deletions
diff --git a/clib/src/memory_leak_detection.c b/clib/src/memory_leak_detection.c new file mode 100644 index 0000000..79dc6fb --- /dev/null +++ b/clib/src/memory_leak_detection.c @@ -0,0 +1,1277 @@ +/* + * Copyright (c) International Business Machines Corp., 2011 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: B. Rafanello + * + * Module: memory_leak_detection.c + * + * Implements a simple memory leak and buffer overwrite detection + * system. + * + * Version 0.0.0.1 + */ + +#define PROGRAM_VERSION "0.0.0.1" + +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +#include <string.h> +#include <errno.h> +#include <execinfo.h> +#include "bb_trace.h" + +/*-------------------------------------------------- + * Private Constants + --------------------------------------------------*/ + +#define LEAK_SIGNATURE 8283471 +#define SENTINEL_VALUE 0xD9B79573 + +/*-------------------------------------------------- + * Private Type definitions + --------------------------------------------------*/ + +typedef unsigned int Sentinel_t; + +struct _Memory_Leak_Data { + unsigned int Signature; /* Indentify this structure in memory */ + void *Mem_Address; /* The address actually returned by malloc. */ + unsigned int User_Size; /* The size of the allocation as requested by the user. */ + unsigned int Alignment; /* The alignment requested by the user. */ + const char *Module_Name; /* The name of the module containing the function which allocated this memory. */ + const char *Function_Name; /* The name of the function which allocated this memory. */ + unsigned int Line_Number; /* The line number of the MALLOC call in the function which allocated this memory. */ + struct _Memory_Leak_Data *Next; /* The next allocated block of memory (if any). */ + struct _Memory_Leak_Data *Previous; /* The previous allocated block of memory (if any). */ + Sentinel_t Starting_Sentinel; /* The starting sentinel used to detect buffer overrun/memory overwrite errors. */ +}; + +typedef struct _Memory_Leak_Data Memory_Leak_Data_t; + +/*-------------------------------------------------- + Private global variables. +--------------------------------------------------*/ + +static pthread_mutex_t Memory_Leak_Lock = PTHREAD_MUTEX_INITIALIZER; /* Mutex to serialize access to the chain of allocations. */ +static Memory_Leak_Data_t *Memory_Chain = NULL; /* The start of our chain of allocations. */ +static unsigned int Memory_Chain_Entries = 0; /* The number of items in our chain. */ + +/*-------------------------------------------------- + Local functions +--------------------------------------------------*/ + +void print_backtrace(void) +{ +#define BT_SIZE 20 + void *bt[BT_SIZE]; + int nr = backtrace(&bt[0], BT_SIZE); + + fprintf(stderr, "========== backtrace ==========\n"); + backtrace_symbols_fd(bt, nr, fileno(stderr)); + fprintf(stderr, "========== backtrace ==========\n"); + + return; +} + +/*********************************************************************/ +/* */ +/* Function Name: Check_Leak_List */ +/* */ +/* Descriptive Name: */ +/* */ +/* Input: */ +/* */ +/* Output: If Success : */ +/* */ +/* If Failure : */ +/* */ +/* Error Handling: */ +/* */ +/* Side Effects: */ +/* */ +/* Notes: */ +/* */ +/*********************************************************************/ +static int Check_Leak_List(const void *p) +{ + Memory_Leak_Data_t *Current_Item; /* Used to walk the memory allocation chain. */ + Memory_Leak_Data_t *Previous_Item; /* Used to check the memory allocation chain. */ + Sentinel_t *Ending_Sentinel; /* To allow access to the sentinel at the end of the allocated memory. */ + unsigned int Data_Start; /* The address of the first byte of the data area of the current allocation. */ + unsigned int Data_End; /* The first byte after the data area of the current allocation. */ + unsigned int Address_To_Test; /* The address we are looking for. */ + unsigned int Address_Found = 2; /* Used to track whether the address was found in the allocation list. + If 2, then the address was not found. If 0, then the address was found and + it is the start of the data area of an allocation. If 1, then the address + was found and it lies within the data area of an allocation. */ + int rc = 0; /* Holds the return code. Assume success. */ + unsigned int Count = 0; + + FUNCTION_ENTRY() + + PRINT_FUNCTION_PARAMETER("Parameters:\n") + PRINT_FUNCTION_PARAMETER(" p = %p\n", p) + + USER3_PRINT_LINE("Memory_Chain = %p", Memory_Chain) + Current_Item = Memory_Chain; + Previous_Item = NULL; + + if (p != NULL) { + USER3_PRINT_LINE + ("Request: Check the memory allocation list for p = %p.\n", + p); + } else { + USER3_PRINT_LINE + ("Request: Check the integrity of the memory allocation list.\n"); + USER3_PRINT_LINE(" Items expected in list: %d\n", + Memory_Chain_Entries); + } + + while (Current_Item != NULL) { + /* Perform some simple checks on the items in the memory allocation list. */ + if (Current_Item->Signature == LEAK_SIGNATURE) { + + if (Current_Item->Starting_Sentinel == SENTINEL_VALUE) { + + /* Check the ending sentinel. */ + Ending_Sentinel = + (Sentinel_t *) ((unsigned int)Current_Item + + sizeof(Memory_Leak_Data_t) + + Current_Item->User_Size); + if (*Ending_Sentinel == SENTINEL_VALUE) { + + /* Check the previous pointer. */ + if (Current_Item->Previous == + Previous_Item) { + + if (p != NULL) { + /* We have an address to check. Does this address lie within this memory allocation? */ + Data_End = + (unsigned long) + Ending_Sentinel; + Data_Start = + (unsigned + long)((unsigned + int) + Current_Item + + + sizeof + (Memory_Leak_Data_t)); + Address_To_Test = + (unsigned long)p; + + if (Address_To_Test == + Data_Start) { + /* This address was returned to the user by MALLOC_func. */ + Address_Found = + 0; + break; + } else { + + if ((Address_To_Test > Data_Start) && (Address_To_Test < Data_End)) { + /* The address lies within the data area of the current allocation. */ + Address_Found + = 1; + break; + } + + } + + } + + } else { + /* We have a pointer problem! The previous pointer field does not point to the previous entry in the list! */ + rc = 3; + fprintf(stderr, + "Link Error! The previous pointer of the current item does not point to the previous item in the allocation list!\n"); + fprintf(stderr, + "Current_Item->Previous = %p, Actual Previous Item is %p.\n", + Current_Item->Previous, + Previous_Item); + fprintf(stderr, + "Item position in list: %d\n", + Count); + print_backtrace(); + } + + } else { + rc = 3; + fprintf(stderr, + "Possible overrun error -- ending sentinel corrupted!\n" + " Header Address: %p\n" + " User Address: %p\n" + " Size: %d\n" + " Allocated in module %s, function %s, at line %d\n", + Current_Item, + (void *)((unsigned int) + Current_Item + + sizeof + (Memory_Leak_Data_t)), + Current_Item->User_Size, + Current_Item->Module_Name, + Current_Item->Function_Name, + Current_Item->Line_Number); + print_backtrace(); + } + + } else { + rc = 3; + fprintf(stderr, + "Memory Overwrite Error! Starting Sentinel is incorrect!\n Following data may be incorrect!\n" + " Header Address: %p\n" + " Address: %p\n" " Size: %d\n" + " Allocated in module %s, function %s, at line %d\n", + Current_Item, + (void *)((unsigned int)Current_Item + + sizeof(Memory_Leak_Data_t)), + Current_Item->User_Size, + Current_Item->Module_Name, + Current_Item->Function_Name, + Current_Item->Line_Number); + print_backtrace(); + } + + Count += 1; + + } else { + rc = 3; + fprintf(stderr, + "Memory Overwrite Error! Memory management data signature is incorrect! Data Dump:\n" + " Header Address: %p\n" + " Signature: Actual[%u, 0x%x], Expected[%u, 0x%x]\n" + " User Address: %p\n" + " Size: [%u, 0x%x]\n" + " Starting Sentinel: Actual[%u, 0x%x], Expected[%u, 0x%x]\n" + " Next: %p\n" " Previous: %p\n" + " Module: %p\n" " Function: %p\n" + " Line [%u, ox%x]\n", Current_Item, + Current_Item->Signature, + Current_Item->Signature, LEAK_SIGNATURE, + LEAK_SIGNATURE, + (void *)((unsigned int)Current_Item + + sizeof(Memory_Leak_Data_t)), + Current_Item->User_Size, + Current_Item->User_Size, + Current_Item->Starting_Sentinel, + Current_Item->Starting_Sentinel, SENTINEL_VALUE, + SENTINEL_VALUE, Current_Item->Next, + Current_Item->Previous, + Current_Item->Module_Name, + Current_Item->Function_Name, + Current_Item->Line_Number, + Current_Item->Line_Number); + fprintf(stderr, + "Memory_Chain = %p\nMemory_Chain_Entries = %u\nCurrent_Item = %p, Count = %u\n\n", + Memory_Chain, Memory_Chain_Entries, + Current_Item, Count); + print_backtrace(); + + break; + } + + Previous_Item = Current_Item; + Current_Item = Current_Item->Next; + } + + if (p != NULL) { + + if (rc == 0) { + rc = Address_Found; + } + + } else { + + if ((Count != Memory_Chain_Entries) && (rc == 0)) { + fprintf(stderr, + "The number of entries found in the allocation list [%u] != list count [%u]!!\n\n", + Count, Memory_Chain_Entries); + print_backtrace(); + rc = 3; + } + + } + + FUNCTION_RETURN("%i", rc) +} + +/*********************************************************************/ +/* */ +/* Function Name: Add_Memory_Allocation_To_Chain */ +/* */ +/* Descriptive Name: Adds a memory tracking object to the list of */ +/* memory tracking objects which represents all */ +/* memory allocations being tracked by this */ +/* module. */ +/* */ +/* Input: Memory_Leak_Data_t * Memory_Leak_Data - The memory */ +/* tracking object to add. */ +/* */ +/* Output: If Success : None. */ +/* */ +/* If Failure : None. */ +/* */ +/* Error Handling: None. */ +/* */ +/* Side Effects: Other memory tracking objects may be modified. */ +/* The count of objects being tracked will be */ +/* incremented. */ +/* */ +/* Notes: */ +/* */ +/*********************************************************************/ +static void Add_Memory_Allocation_To_Chain(Memory_Leak_Data_t * + Memory_Leak_Data) +{ + + FUNCTION_ENTRY() + + PRINT_FUNCTION_PARAMETER("Parameters:\n") + PRINT_FUNCTION_PARAMETER(" Memory_Leak_Data = %p", + Memory_Leak_Data) + + if (Memory_Chain) { + /* Other allocations exist. Insert at the head of the chain. */ + Memory_Leak_Data->Next = Memory_Chain; + Memory_Chain->Previous = Memory_Leak_Data; + } else { + /* This is the first allocation. */ + Memory_Leak_Data->Next = NULL; + } + + /* Update the pointer to the start of the allocation list. */ + Memory_Chain = Memory_Leak_Data; + + /* Update the count of entries in the memory chain. */ + Memory_Chain_Entries += 1; + + FUNCTION_EXIT() +} + +/*********************************************************************/ +/* */ +/* Function Name: Remove_Memory_Allocation_From_Chain */ +/* */ +/* Descriptive Name: Removes a memory tracking object from */ +/* the linked list of memory tracking */ +/* objects representing current memory */ +/* allocations. */ +/* */ +/* Input: Memory_Leak_Data_t * Memory_Leak_Data - the memory */ +/* tracking object to remove. */ +/* */ +/* Output: If Success : None. */ +/* */ +/* If Failure : None. */ +/* */ +/* Error Handling: None. */ +/* */ +/* Side Effects: Other memory tracking objects may be modified. */ +/* The memory tracking object being removed will be */ +/* zeroed out and freed. */ +/* */ +/* Notes: */ +/* */ +/*********************************************************************/ +static void Remove_Memory_Allocation_From_Chain(Memory_Leak_Data_t * + Memory_Leak_Data) +{ + Memory_Leak_Data_t *Temp; /* Used when adjusting the memory allocation chain. */ + Sentinel_t *Ending_Sentinel; /* To allow access to the sentinel at the end of the allocated memory. */ + + FUNCTION_ENTRY() + + /* Are we the first item in the chain? */ + if (Memory_Leak_Data->Previous == NULL) { + + /* We are at the head of the chain, which means that Memory_Chain points to us! + * Point Memory_Chain to the Memory_Leak_Data_t that follows us (if any). + */ + Memory_Chain = Memory_Leak_Data->Next; + + /* Is there another item in the chain? If so, update it. */ + if (Memory_Chain != NULL) + Memory_Chain->Previous = NULL; + + } else { + + /* Are we the last item in the chain? */ + if (Memory_Leak_Data->Next == NULL) { + + /* We are at the end of the memory chain with at least 1 entry before us. */ + Temp = Memory_Leak_Data->Previous; + Temp->Next = NULL; + } else { + /* We are in the middle of the chain. */ + Temp = Memory_Leak_Data->Previous; + Temp->Next = Memory_Leak_Data->Next; + Temp = Memory_Leak_Data->Next; + Temp->Previous = Memory_Leak_Data->Previous; + } + + } + + /* Adjust the count of entries in the memory chain. */ + Memory_Chain_Entries -= 1; + + /* Calculate the address of the ending sentinel so that we can access it. */ + Ending_Sentinel = + (Sentinel_t *) ((unsigned int)Memory_Leak_Data + + sizeof(Memory_Leak_Data_t) + + Memory_Leak_Data->User_Size); + + /* Clean out the memory leak data. */ + Memory_Leak_Data->Function_Name = NULL; + Memory_Leak_Data->Line_Number = 0; + Memory_Leak_Data->Module_Name = NULL; + Memory_Leak_Data->Signature = 0; + Memory_Leak_Data->Starting_Sentinel = 0; + Memory_Leak_Data->User_Size = 0; + Memory_Leak_Data->Next = NULL; + Memory_Leak_Data->Previous = NULL; + *Ending_Sentinel = 0; + + free(Memory_Leak_Data->Mem_Address); + + FUNCTION_EXIT() +} + +/*-------------------------------------------------- + API Functions +--------------------------------------------------*/ + +/*********************************************************************/ +/* */ +/* Function Name: Print_Leak_List */ +/* */ +/* Descriptive Name: This function walks the list of allocated */ +/* memory blocks and prints information about */ +/* each one. If this is done at program exit, */ +/* the resulting list of memory blocks most */ +/* likely represents leaked memory. */ +/* */ +/* Input: None. */ +/* */ +/* Output: If Success : If there are any memory blocks being */ +/* tracked by this module, information about */ +/* block still being tracked will be sent to */ +/* stderr. */ +/* */ +/* If Failure : Error messages may be sent to stderr. */ +/* */ +/* Error Handling: If errors are detected, then error messages are */ +/* output on stderr. */ +/* */ +/* Side Effects: The internal structures of this module are checked*/ +/* for errors with any errors being reported on */ +/* stderr. */ +/* */ +/* Notes: */ +/* */ +/*********************************************************************/ +void Print_Leak_List(void) +{ + Memory_Leak_Data_t *Current_Item; /* Used to walk the memory allocation chain. */ + Sentinel_t *Ending_Sentinel; /* To allow access to the sentinel at the end of the allocated memory. */ + + API_FUNCTION_ENTRY() + + /* Get lock. */ + pthread_mutex_lock(&Memory_Leak_Lock); + + Current_Item = Memory_Chain; + + fprintf(stderr, + "\n\nMemory_Chain is %p, and Memory_Chain_Entries is %u.\n", + Memory_Chain, Memory_Chain_Entries); + fprintf(stderr, "\nCurrent Memory Allocation List\n\n"); + + while (Current_Item != NULL) { + /* Perform some simple checks on the data before we print it. */ + if (Current_Item->Signature == LEAK_SIGNATURE) { + + if (Current_Item->Starting_Sentinel == SENTINEL_VALUE) { + + /* Check the ending sentinel. */ + Ending_Sentinel = + (Sentinel_t *) ((unsigned int)Current_Item + + sizeof(Memory_Leak_Data_t) + + Current_Item->User_Size); + if (*Ending_Sentinel == SENTINEL_VALUE) { + fprintf(stderr, + "Memory Allocation Record\n"); + } else { + fprintf(stderr, + "\nPossible overrun error -- ending sentinel corrupted!\n"); + } + + fprintf(stderr, + " Allocation Block: %p\n" + " Header Address: %p\n" + " User Address: %p\n" + " Alignment: %u\n" + " Size: %d\n" + " Allocated in module %s, function %s, at line %d\n\n", + Current_Item->Mem_Address, + Current_Item, + (void *)((unsigned int)Current_Item + + sizeof(Memory_Leak_Data_t)), + Current_Item->Alignment, + Current_Item->User_Size, + Current_Item->Module_Name, + Current_Item->Function_Name, + Current_Item->Line_Number); + } else { + fprintf(stderr, + "\nMemory Overwrite Error! Starting Sentinel is incorrect!\n Following data may be incorrect!\n"); + fprintf(stderr, + " Allocation Block: %p\n" + " Header Address: %p\n" + " User Address: %p\n" + " Alignment: %u\n" " Size: %d\n" + " Allocated in module %s, function %s, at line %d\n\n", + Current_Item->Mem_Address, Current_Item, + (void *)((unsigned int)Current_Item + + sizeof(Memory_Leak_Data_t)), + Current_Item->Alignment, + Current_Item->User_Size, + Current_Item->Module_Name, + Current_Item->Function_Name, + Current_Item->Line_Number); + } + + } else { + fprintf(stderr, + "\n\nMemory Overwrite Error! Memory management data signature is incorrect! Data Dump:\n"); + fprintf(stderr, + " Allocation Block: %p\n" + " Header Address: %p\n" + " Signature: Actual[%u, 0x%x], Expected[%u, 0x%x]\n" + " Address: %p\n" " Alignment: %u\n" + " Size: [%u, 0x%x]\n" + " Starting Sentinel: Actual[%u, 0x%x], Expected[%u, 0x%x]\n" + " Next: %p\n" " Previous: %p\n" + " Module: %p\n" " Function: %p\n" + " Line [%u, ox%x]\n", + Current_Item->Mem_Address, Current_Item, + Current_Item->Signature, + Current_Item->Signature, LEAK_SIGNATURE, + LEAK_SIGNATURE, + (void *)((unsigned int)Current_Item + + sizeof(Memory_Leak_Data_t)), + Current_Item->Alignment, + Current_Item->User_Size, + Current_Item->User_Size, + Current_Item->Starting_Sentinel, + Current_Item->Starting_Sentinel, SENTINEL_VALUE, + SENTINEL_VALUE, Current_Item->Next, + Current_Item->Previous, + Current_Item->Module_Name, + Current_Item->Function_Name, + Current_Item->Line_Number, + Current_Item->Line_Number); + fprintf(stderr, + "Memory_Chain = %p\nMemory_Chain_Entries = %u\nCurrent_Item = %p\n\n", + Memory_Chain, Memory_Chain_Entries, + Current_Item); + break; + } + + Current_Item = Current_Item->Next; + } + + /* Release lock. */ + pthread_mutex_unlock(&Memory_Leak_Lock); + + API_FUNCTION_EXIT() + +} + +/*********************************************************************/ +/* */ +/* Function Name: MEMORY_func */ +/* */ +/* Descriptive Name: This function acts as a replacement for */ +/* malloc. It allocates memory (using malloc) */ +/* and enters that memory into a tracking */ +/* structure so that memory leaks, if any, may */ +/* be found. */ +/* */ +/* Input: size_t sz - The number of bytes to be allocated. */ +/* unsigned int Alignment - 0 for non-aligned (normal) */ +/* malloc, > 0 to return an */ +/* address aligned on a specific */ +/* memory boundary. If > 0, then */ +/* Alignment must be a power of 2 */ +/* and a multiple of sizeof(void *)*/ +/* void ** Memory_Location - The address of a variable which*/ +/* will hold the address of the */ +/* allocated by this function. */ +/* const char * mod_name - The name of the module from which*/ +/* this function was called. */ +/* const char * func - The name of the function from which */ +/* this function was called. */ +/* const int line - The line number of the code containing */ +/* the call to this function. */ +/* */ +/* Output: If Success : The function return value will be 0. */ +/* *Memory_Location will be set to the address*/ +/* of the first byte of the user portion of */ +/* any memory that was allocated. */ +/* */ +/* If Failure : The function return value will be EINVAL or*/ +/* ENOMEM. Errors may be reported on stderr. */ +/* *Memory_Location will be set to NULL. */ +/* */ +/* Error Handling: This function will abort if an error is */ +/* is detected. Errors could be lack of memory, or*/ +/* corruption of the internal structures used to */ +/* track allocated blocks of memory. */ +/* */ +/* Side Effects: Memory may be allocated, errors may be reported */ +/* on stderr. */ +/* */ +/* Notes: */ +/* */ +/*********************************************************************/ +int MEMORY_func(size_t sz, + unsigned int Alignment, + void **Memory_Location, + const char *mod_name, const char *func, const int line) +{ + Memory_Leak_Data_t *Memory_Leak_Data; /* For accessing our memory tracking data. */ + void *ptr = NULL; /* To hold the address that will be returned to the caller. */ + size_t New_Size; /* Used to adjust the size of the memory allocation. */ + Sentinel_t *Ending_Sentinel; /* To allow access to the sentinel at the end of the allocated memory. */ + unsigned int Proposed_User_Address; /* Used to calculate the address to return to the caller. */ + unsigned int Shift_Amount; /* Used when adjusting the address returned to caller to ensure proper alignment. */ + int Error = 0; + unsigned int i = 0; /* Used when calculating powers of two to check the alignment. */ + unsigned int Power_Of_Two = 1; /* Used when calculating powers of two to check the alignment. */ + +#define MAX_POWER_OF_TWO 31 + + API_FUNCTION_ENTRY() + + PRINT_API_PARAMETER("Parameters:\n") + PRINT_API_PARAMETER(" sz = %d", sz) + PRINT_API_PARAMETER(" Alignment = %d", Alignment) + PRINT_API_PARAMETER(" Memory_Location = %p", Memory_Location) + PRINT_API_PARAMETER(" mod_name = %s", mod_name) + PRINT_API_PARAMETER(" func = %s", func) + PRINT_API_PARAMETER(" line = %i", line) + + /* Check the size of the request. */ + if (sz == 0) { + Error = EINVAL; + fprintf(stderr, + "MALLOC: request is invalid - size[%u] in module %s, function %s at line %d\n", + sz, mod_name, func, line); + print_backtrace(); + } + + /* Check the alignment, if one was specified. */ + if (Alignment > 0) { + if (Alignment % sizeof(void *)) { + /* Improper alignment! */ + Error = EINVAL; + } else { + do { + Power_Of_Two *= 2; + i++; + } while ((Power_Of_Two != Alignment) + && (i < MAX_POWER_OF_TWO)); + + if (Power_Of_Two != Alignment) { + Error = EINVAL; + } + + } + + if (Error) { + fprintf(stderr, + "MEMORY_func: request for aligned memory uses invalid alignment! size[%u], alignment [%u] in module %s, function %s at line %d\n", + sz, Alignment, mod_name, func, line); + } + + } + + if (Memory_Location == NULL) { + Error = EINVAL; + fprintf(stderr, + "MEMORY_func: Location to place address of allocated memory is NULL! size[%u], alignment [%u] in module %s, function %s at line %d\n", + sz, Alignment, mod_name, func, line); + } else + *Memory_Location = NULL; + + if (Error != 0) { + API_FUNCTION_RETURN("%i", Error); + } + + USER3_PRINT_LINE + ("MEMORY_func: processing request - size[%u], alignment[%u] from module %s, function %s at line %d\n", + sz, Alignment, mod_name, func, line); + + /* Check */ + + /* Get lock. */ + pthread_mutex_lock(&Memory_Leak_Lock); + + /* Check the memory allocation list. If the list is bad, then abort because we are already screwed ... */ + if (!Check_Leak_List(NULL)) { + + /* Adjust the size of the request to account for the additional memory we need to track this request. */ + New_Size = + sz + sizeof(Memory_Leak_Data_t) + sizeof(Sentinel_t) + + Alignment; + + /* Get the memory. */ + ptr = malloc(New_Size); + + /* Was the memory available? */ + if (ptr != NULL) { + /* Determine where to put our memory leak data. */ + if (Alignment == 0) { + /* We can place the memory leak data right at the start of the memory block we got from malloc. */ + Memory_Leak_Data = (Memory_Leak_Data_t *) ptr; + } else { + Proposed_User_Address = + (unsigned int)ptr + + sizeof(Memory_Leak_Data_t); + Shift_Amount = + Alignment - + (Proposed_User_Address % Alignment); + Memory_Leak_Data = + (Memory_Leak_Data_t *) ((unsigned int)ptr + + Shift_Amount); + } + + /* Save the address returned by malloc() for use with free(). */ + Memory_Leak_Data->Mem_Address = ptr; + + /* Create the address to return to the caller. This address should be the first byte after + our memory leak data. */ + ptr = + (void *)((unsigned int)Memory_Leak_Data + + sizeof(Memory_Leak_Data_t)); + + /* Calculate the address of the trailing sentinel. */ + Ending_Sentinel = + (Sentinel_t *) ((unsigned int)ptr + sz); + + /* Initialize our memory leak data. */ + Memory_Leak_Data->Signature = LEAK_SIGNATURE; + Memory_Leak_Data->User_Size = sz; + Memory_Leak_Data->Alignment = Alignment; + Memory_Leak_Data->Module_Name = mod_name; + Memory_Leak_Data->Function_Name = func; + Memory_Leak_Data->Line_Number = line; + Memory_Leak_Data->Previous = NULL; + Memory_Leak_Data->Next = NULL; + Memory_Leak_Data->Starting_Sentinel = SENTINEL_VALUE; + *Ending_Sentinel = SENTINEL_VALUE; + + USER3_PRINT_LINE + ("MALLOC: Allocated header at %p ( = user address %p) size[%u] in module %s, function %s at line %d\n", + Memory_Leak_Data, ptr, sz, mod_name, func, line); + Add_Memory_Allocation_To_Chain(Memory_Leak_Data); + USER3_PRINT_LINE + ("MALLOC: Memory_Chain is %p, and Memory_Chain_Entries is %u.\n", + Memory_Chain, Memory_Chain_Entries); + + /* Set up return value. */ + *Memory_Location = ptr; + } else { + + fprintf(stderr, + "MALLOC: Memory allocation failed - no more memory available at this time!\n"); + print_backtrace(); + + USER3_PRINT_LINE + ("MALLOC: request was issued from module %s, function %s at line %d\n", + mod_name, func, line); + + } + + } else { + fprintf(stderr, + "MALLOC: Found the memory leak data to be corrupted! Aborting!\n"); + print_backtrace(); + } + + /* Release lock. */ + pthread_mutex_unlock(&Memory_Leak_Lock); + + API_FUNCTION_RETURN("%i", Error) + +} + +/*********************************************************************/ +/* */ +/* Function Name: MALLOC_func */ +/* */ +/* Descriptive Name: This function acts as a replacement for */ +/* malloc. It allocates memory (using malloc) */ +/* and enters that memory into a tracking */ +/* structure so that memory leaks, if any, may */ +/* be found. */ +/* */ +/* Input: size_t sz - The number of bytes to be allocated. */ +/* const char * mod_name - The name of the module from which*/ +/* this function was called. */ +/* const char * func - The name of the function from which */ +/* this function was called. */ +/* const int line - The line number of the code containing */ +/* the call to this function. */ +/* */ +/* Output: If Success : The function return value will be non-NULL.*/ +/* */ +/* If Failure : The function return value will be NULL. */ +/* Errors may be reported on stderr. */ +/* */ +/* Error Handling: This function will abort if an error is */ +/* is detected. Errors could be lack of memory, or*/ +/* corruption of the internal structures used to */ +/* track allocated blocks of memory. */ +/* */ +/* Side Effects: Memory may be allocated, errors may be reported */ +/* on stderr. */ +/* */ +/* Notes: */ +/* */ +/*********************************************************************/ +void *MALLOC_func(size_t sz, const char *mod_name, const char *func, + const int line) +{ + void *Return_Value; + int Error; + + Error = MEMORY_func(sz, 0, &Return_Value, mod_name, func, line); + + if (Error != 0) + return NULL; + else + return Return_Value; +} + +/*********************************************************************/ +/* */ +/* Function Name: FREE_func */ +/* */ +/* Descriptive Name: This function frees a block of memory being */ +/* tracked by this module and removes the block */ +/* from its tracking structures. */ +/* */ +/* Input: const void * p - The address of the block of memory to */ +/* be freed. */ +/* const char * mod_name - The name of the module requesting*/ +/* the block of memory be freed. */ +/* const char * func - The name of the function requesting */ +/* the block of memory be freed. */ +/* const int line - The line number of the line of code in */ +/* module calling this function. */ +/* */ +/* Output: If Success : None. */ +/* */ +/* If Failure : Errors may be reported to stderr. */ +/* */ +/* Error Handling: This function causes the internal structures */ +/* of this module to be checked as part of the */ +/* process of freeing the address p. This may */ +/* cause errors to be reported on stderr. If any */ +/* errors are found, then the address p may not be */ +/* freed. */ +/* */ +/* Side Effects: The block of memory associated with the address p */ +/* will be freed and available for reallocation. */ +/* Also, the memory tracking structures in this */ +/* module will undergo a series of checks. */ +/* */ +/* Notes: This function was not intended to be called directly but */ +/* rather through the macro FREE. */ +/* */ +/*********************************************************************/ +void FREE_func(const void *p, const char *mod_name, const char *func, + const int line) +{ + Memory_Leak_Data_t *Memory_Leak_Data; /* For accessing our memory tracking data. */ + + API_FUNCTION_ENTRY() + + PRINT_API_PARAMETER("Parameters:\n") + PRINT_API_PARAMETER(" p = %p", p) + PRINT_API_PARAMETER(" mod_name = %s", mod_name) + PRINT_API_PARAMETER(" func = %s", func) + PRINT_API_PARAMETER(" line = %i", line) + + /* Check the address passed to us. It must not be NULL, and must be > sizeof(Memory_Leak_Data_t) to + * prevent wrap around when we subtract sizeof(Memory_Leak_Data_t) to get the starting address of + * the memory leak data associated with p. + */ + if ((p == NULL) || ((unsigned int)p <= sizeof(Memory_Leak_Data_t))) { + fprintf(stderr, + "FREE: request has invalid user address [%p]. Request came from module %s, function %s, line %d\n", + p, mod_name, func, line); + print_backtrace(); + return; + } else { + USER3_PRINT_LINE + ("FREE: request to free [%p] received from module %s, function %s, line %d\n", + p, mod_name, func, line); + } + + /* Get lock. */ + pthread_mutex_lock(&Memory_Leak_Lock); + + /* Check the memory allocation list for errors. */ + if (!Check_Leak_List(NULL)) { + + /* Is the address given to us in the memory allocation list? */ + if (!Check_Leak_List(p)) { + /* Get access to the memory leak data. */ + Memory_Leak_Data = + (Memory_Leak_Data_t *) ((unsigned int)p - + sizeof(Memory_Leak_Data_t)); + + USER3_PRINT_LINE + ("FREE: Calling free on header address %p ( = user address %p) from module %s, function %s, line %d\n", + Memory_Leak_Data, p, mod_name, func, line); + Remove_Memory_Allocation_From_Chain(Memory_Leak_Data); + USER3_PRINT_LINE + ("FREE: Memory_Chain is %p, and Memory_Chain_Entries is %u.\n", + Memory_Chain, Memory_Chain_Entries); + + } else { + /* The address given to us was not in the memory allocation list! If we free this address, who knows what will happen! */ + fprintf(stderr, + "FREE: Invalid address! The address %p was not provided by MALLOC or has been passed to FREE more than once!\n", + p); + fprintf(stderr, + " Request came from module %s, function %s, line %d\n", + mod_name, func, line); + print_backtrace(); + } + + } else { + fprintf(stderr, + "FREE: Aborting due to errors in the memory leak data!\n"); + USER3_PRINT_LINE + ("FREE: request was submitted by module %s, function %s at line %d\n", + mod_name, func, line); + print_backtrace(); + + } + + pthread_mutex_unlock(&Memory_Leak_Lock); + + USER3_PRINT_LINE("FREE: request is complete.\n"); + + API_FUNCTION_EXIT() +} + +/*********************************************************************/ +/* */ +/* Function Name: Test_Address_Allocation */ +/* */ +/* Descriptive Name: This function tests the specified address to */ +/* to see if it lies within an allocated block */ +/* tracked by this module. */ +/* */ +/* Input: void * p - The address to be tested. */ +/* */ +/* Output: If Success : If the address p was found, then 0 will be */ +/* returned if the address is the start of */ +/* a block of allocated memory. If the */ +/* address p was found within an allocated */ +/* block of memory, then 1 is returned. */ +/* */ +/* If Failure : If the address p was NOT found, then 2 is */ +/* returned. If there was an error in the */ +/* memory tracking system then 3 will be */ +/* returned. */ +/* */ +/* Error Handling: This function relies on the error handling */ +/* built into the Check_Leak_List function and */ +/* has no error handling of its own. */ +/* */ +/* Side Effects: If the list of memory allocations contains errors */ +/* then those errors will be detected and reported */ +/* on stderr. */ +/* */ +/* Notes: If NULL is passed in as the address to test, then the */ +/* integrity of the internal tracking structures will be */ +/* checked, in which case a return value of 0 signifies */ +/* that the internal tracking structures have passed the */ +/* checks and a return value of 3 indicates that errors */ +/* were found. */ +/* */ +/*********************************************************************/ +unsigned int Test_Address_Allocation(void *p) +{ + unsigned int rc = 0; + + API_FUNCTION_ENTRY() + + PRINT_API_PARAMETER("Parameters:\n") + PRINT_API_PARAMETER(" p = %p", p) + + /* Get lock. */ + pthread_mutex_lock(&Memory_Leak_Lock); + + rc = Check_Leak_List(p); + + /* Release lock. */ + pthread_mutex_unlock(&Memory_Leak_Lock); + + API_FUNCTION_RETURN("%i", rc); +} + +/*********************************************************************/ +/* */ +/* Function Name: Duplicate_String */ +/* */ +/* Descriptive Name: This function duplicates a string. The memory*/ +/* allocated for the duplicate is allocated */ +/* using the MALLOC_func routine in this module */ +/* and is thus tracked by this module. */ +/* */ +/* Input: const char * Source - The string to be copied. */ +/* const char * mod_name - The name of the module containing*/ +/* the function which called this */ +/* function. */ +/* const char * func - The name of the function calling */ +/* this function. */ +/* const int line - The line number of the line of code in */ +/* module calling this function. */ +/* */ +/* Output: If Success : The function return value will be non-NULL */ +/* and will point to a duplicate of the */ +/* string given in Source. */ +/* */ +/* If Failure : The function return value will be NULL. */ +/* */ +/* Error Handling: Any errors detected by this function result in */ +/* a function return value of NULL. */ +/* */ +/* Side Effects: The memory tracking features of this module are */ +/* employed to allocate memory for the duplicate */ +/* string produced by this funciton. */ +/* */ +/* Notes: */ +/* */ +/*********************************************************************/ +char *Duplicate_String(const char *Source, + const char *mod_name, const char *func, const int line) +{ + char *Result; + + Result = (char *)MALLOC_func(strlen(Source) + 1, mod_name, func, line); + if (Result != NULL) + strcpy(Result, Source); + + return Result; +} + +/*********************************************************************/ +/* */ +/* Function Name: Realloc_func */ +/* */ +/* Descriptive Name: This function performs the same function as */ +/* the realloc function in the ANSI C library. */ +/* */ +/* Input: const void * p - The address of the block of memory to */ +/* be reallocated. */ +/* size_t size - The size of the memory block to return. */ +/* const char * mod_name - The name of the module requesting*/ +/* the block of memory be freed. */ +/* const char * func - The name of the function requesting */ +/* the block of memory be freed. */ +/* const int line - The line number of the line of code in */ +/* module calling this function. */ +/* */ +/* Output: If Success : The function return value will be a pointer*/ +/* to the new block of memory. */ +/* */ +/* If Failure : NULL will be returned and errno will be set*/ +/* to a non-null error code. */ +/* */ +/* Error Handling: This function causes the internal structures */ +/* of this module to be checked. This may */ +/* cause errors to be reported on stderr. If any */ +/* errors are found, then the address p may not be */ +/* freed. */ +/* */ +/* Side Effects: A new block of memory of size bytes will be */ +/* allocated, the contents of the current block will */ +/* be copied to the new block (at least as much as */ +/* will fit, and the current block will be freed. */ +/* This will cause internal structures in this module*/ +/* to be modified accordingly. */ +/* */ +/* Notes: This function was not intended to be called directly but */ +/* rather through the macro REALLOC. */ +/* */ +/* If p is NULL, then this will cause this function to */ +/* behave like malloc. */ +/* */ +/* If size is 0, then this will cause this function to */ +/* behave like free. */ +/* */ +/*********************************************************************/ +void *Realloc_func(void *p, size_t size, const char *mod_name, const char *func, + const int line) +{ + Memory_Leak_Data_t *Memory_Leak_Data; /* For accessing our memory tracking data. */ + int Error = 0; + unsigned int Copy_Size = 0; + void *Return_Value = NULL; + + API_FUNCTION_ENTRY() + + PRINT_API_PARAMETER("Parameters:\n") + PRINT_API_PARAMETER(" p = %p", p) + PRINT_API_PARAMETER(" size = %u", size) + PRINT_API_PARAMETER(" mod_name = %s", mod_name) + PRINT_API_PARAMETER(" func = %s", func) + PRINT_API_PARAMETER(" line = %i", line) + + /* Check the address passed to us. If it is NULL and size > 0, then we are merely doing a malloc. */ + if ((p == NULL) && (size > 0)) { + USER3_PRINT_LINE + ("REALLOC: p was NULL and size > 0, acting as MALLOC.\n") + Return_Value = MALLOC_func(size, mod_name, func, line); + if (Return_Value == NULL) { + errno = ENOMEM; + } + } else { + /* If size is 0 and p is not NULL, then we are doing a free. */ + if ((p != NULL) && (size == 0)) { + USER3_PRINT_LINE + ("REALLOC: p was non-NULL but size is 0, acting as FREE.\n") + FREE_func(p, mod_name, func, line); + } else { + /* Do we have real work to do? */ + if ((p != NULL) && (size != 0)) { + USER3_PRINT_LINE + ("REALLOC: p was non-NULL and size > 0, we have actual work to do!.\n") + + /* Get lock. */ + pthread_mutex_lock(&Memory_Leak_Lock); + + /* Check the memory allocation list for errors. */ + if (!Check_Leak_List(NULL)) { + + /* Is the address given to us in the memory allocation list? */ + if (!Check_Leak_List(p)) { + /* Get access to the memory leak data. */ + Memory_Leak_Data = + (Memory_Leak_Data_t + *) ((unsigned int)p - + sizeof + (Memory_Leak_Data_t)); + + /* Release the lock so that MEMORY_func can get it. */ + pthread_mutex_unlock + (&Memory_Leak_Lock); + + /* Call MEMORY_func to get the memory we need and add it to the list of memory blocks we are tracking. */ + Error = + MEMORY_func(size, + Memory_Leak_Data-> + Alignment, + &Return_Value, + mod_name, func, + line); + if (Error != 0) { + Return_Value = NULL; + errno = ENOMEM; + } else { + + /* We have the replacement memory. Now lets copy what we can from the original block to the new block. */ + if (size > + Memory_Leak_Data-> + User_Size) { + Copy_Size = + Memory_Leak_Data-> + User_Size; + } else { + Copy_Size = + size; + } + + /* Copy the data to be preserved from the original memory block to the new memory block. */ + memcpy(Return_Value, p, + Copy_Size); + + /* Zero out the original memory block ... this can catch errors as users should not assume that + * realloc will leave their original memory block intact. + */ + memset(p, 0x0, + Memory_Leak_Data-> + User_Size); + + /* Get the lock again. */ + pthread_mutex_lock + (&Memory_Leak_Lock); + + USER3_PRINT_LINE + ("REALLOC: Calling free on header address %p ( = user address %p) from module %s, function %s, line %d\n", + Memory_Leak_Data, + p, mod_name, func, + line); + Remove_Memory_Allocation_From_Chain + (Memory_Leak_Data); + USER3_PRINT_LINE + ("REALLOC: Memory_Chain is %p, and Memory_Chain_Entries is %u.\n", + Memory_Chain, + Memory_Chain_Entries); + + } + + } else { + /* The address given to us was not in the memory allocation list! If we free this address, who knows what will happen! */ + fprintf(stderr, + "REALLOC: Invalid address! The address %p was not provided by MALLOC or has been passed to FREE already!\n", + p); + fprintf(stderr, + " Request came from module %s, function %s, line %d\n", + mod_name, func, line); + print_backtrace(); + } + + } else { + fprintf(stderr, + "REALLOC: Aborting due to errors in the memory leak data!\n"); + USER3_PRINT_LINE + ("REALLOC: request was submitted by module %s, function %s at line %d\n", + mod_name, func, line); + print_backtrace(); + + } + + pthread_mutex_unlock(&Memory_Leak_Lock); + + } + + } + + } + + USER3_PRINT_LINE("REALLOC: request is complete.\n"); + + API_FUNCTION_RETURN("%p", Return_Value) +} |