forked from TheAlgorithms/C
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Get a trace of all of the allocations, using a doubly linked li…
…st and wrappers for malloc/calloc and free. (TheAlgorithms#782) * Add malloc, calloc and free wrappers to trace all of the allocations * Improve memory leak reporting when multiple allocations occurs in the same file at the same line. * Change directory name from 'debugging' to 'developer_tools' and added CMakeLists.txt, also change an include name to match a file name * Edit root CMakeLists.Txt * Update developer_tools/malloc_dbg.h Co-authored-by: David Leal <[email protected]> * Edit CMakeLists.txt to create a static library from malloc_dbg.c * Add comments for the includes * Change test.c name to test_malloc_dbg.c, also edit CMakeLists.txt to link malloc_dbg.a only to test_malloc_dbg. * Change comment style and change EXIT_SUCCESS to 0 * Fix typo in doxygen comments * Enhance comments * Apply suggestions from code review Co-authored-by: David Leal <[email protected]>
- Loading branch information
1 parent
e81bc16
commit 3682694
Showing
5 changed files
with
392 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# If necessary, use the RELATIVE flag, otherwise each source file may be listed | ||
# with full pathname. RELATIVE may makes it easier to extract an executable name | ||
# automatically. | ||
file( GLOB APP_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.c ) | ||
# file( GLOB APP_SOURCES ${CMAKE_SOURCE_DIR}/*.c ) | ||
# AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} APP_SOURCES) | ||
|
||
add_library(malloc_dbg malloc_dbg.c) # Create a static library from malloc_dbg.c file | ||
|
||
foreach( testsourcefile ${APP_SOURCES} ) | ||
string( REPLACE ".c" "" testname ${testsourcefile} ) | ||
string( REPLACE ".C" "" testname ${testname} ) | ||
string( REPLACE " " "_" testname ${testname} ) | ||
|
||
if ("${testsourcefile}" STREQUAL "malloc_dbg.c") | ||
continue() | ||
endif() | ||
|
||
add_executable( ${testname} ${testsourcefile} ) | ||
|
||
if ("${testname}" STREQUAL "test_malloc_dbg") | ||
target_link_libraries(${testname} malloc_dbg) | ||
endif() | ||
|
||
if(OpenMP_C_FOUND) | ||
target_link_libraries(${testname} OpenMP::OpenMP_C) | ||
endif() | ||
|
||
if(MATH_LIBRARY) | ||
target_link_libraries(${testname} ${MATH_LIBRARY}) | ||
endif() | ||
# target_link_libraries(${testname} malloc_dbg) # Link the malloc_dbg library | ||
install(TARGETS ${testname} DESTINATION "bin/developer_tools") | ||
|
||
endforeach( testsourcefile ${APP_SOURCES} ) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,289 @@ | ||
/** | ||
* @file | ||
* @brief This file contains malloc_dbg, calloc_dbg, free_dbg and printLeaks implementations. | ||
* @author [tinouduart33](https://github.com/tinouduart33) | ||
*/ | ||
|
||
#include <stdlib.h> /// For the malloc, calloc and free functions. | ||
#include <stdio.h> /// For IO operations (printf). | ||
#include <string.h> /// For the memcmp function. | ||
#include "malloc_dbg.h" /// Header file which contains the prototypes of malloc_dbg, calloc_dbg and fre_dbg. | ||
|
||
/* We must undef these macros in order to use the real malloc / calloc and free functions */ | ||
#undef malloc | ||
#undef calloc | ||
#undef free | ||
|
||
/** | ||
* @brief Structure used to save an allocated pointer | ||
*/ | ||
typedef struct MEMORY_INFORMATION | ||
{ | ||
void* ptr; ///< Pointer returned by malloc / calloc | ||
const char* fileName; ///< File in which malloc or calloc has been called | ||
const char* functionName; ///< Function in which malloc or calloc has been called | ||
size_t bytes; ///< Number of bytes allocated | ||
int line; ///< Line number (in file) corresponding to the malloc / calloc call | ||
struct MEMORY_INFORMATION* next; ///< Next element in the list | ||
struct MEMORY_INFORMATION* previous; ///< Previous element in the list | ||
} mem_info; | ||
|
||
/** We use a global variable for the list so we can use it at any time. | ||
* */ | ||
mem_info* memoryInformation = NULL; | ||
|
||
/** Another global variable. This one is used to know if we already call the atexit function. | ||
* */ | ||
int atexitCalled = 0; | ||
|
||
|
||
/** | ||
* @brief addMemInfo function add a memory allocation in the memoryInfo list. | ||
* @details This function creates a new element and add it on top of the list | ||
* @param memoryInfo Pointer to the doubly linked list used to store all of the allocations | ||
* @param ptrToreturn Pointer returned by malloc or calloc | ||
* @param bytes Size in bytes of the allocation | ||
* @param line Line where the allocation has been called | ||
* @param filename File where the allocation has been called | ||
* @param functionName Name of the function where the allocation has been called | ||
* @returns mem_info pointer if it succeeds, NULL otherwise | ||
*/ | ||
mem_info* addMemInfo(mem_info* memoryInfo, void* ptrToReturn, size_t bytes, int line, const char* filename, const char* functionName) | ||
{ | ||
mem_info* newMemInfo = (mem_info*)malloc(sizeof(mem_info)); | ||
if (!newMemInfo) | ||
{ | ||
return NULL; | ||
} | ||
|
||
newMemInfo->ptr = ptrToReturn; | ||
newMemInfo->bytes = bytes; | ||
newMemInfo->line = line; | ||
newMemInfo->fileName = filename; | ||
newMemInfo->functionName = functionName; | ||
newMemInfo->next = memoryInfo; | ||
newMemInfo->previous = NULL; | ||
if (memoryInformation) | ||
memoryInformation->previous = newMemInfo; | ||
|
||
return newMemInfo; | ||
} | ||
|
||
/** | ||
* @brief inList function is used to know if an element is already in the memoryInfo list. | ||
* @details This function is used to know if an allocation in a specific file at a specific line already exists in the list. | ||
* @param filename File in which malloc or calloc has been called | ||
* @param line Line number in the file in which malloc or calloc has been called | ||
* @returns Position of the element in the list if the element is found, -1 otherwise. | ||
*/ | ||
int inList(const char* filename, int line) | ||
{ | ||
mem_info* tmp = memoryInformation; | ||
int counter = 0; | ||
int len = strlen(filename); | ||
|
||
while (tmp) | ||
{ | ||
if (len == strlen(tmp->fileName)) | ||
{ | ||
if (!memcmp(filename, tmp->fileName, len) && tmp->line == line) | ||
{ | ||
return counter; | ||
} | ||
} | ||
tmp = tmp->next; | ||
counter++; | ||
} | ||
return -1; | ||
} | ||
|
||
/** | ||
* @brief editInfo function is used to edit an element in the memoryInfo list. | ||
* @details This function is used to edit the number of bytes allocated at a specific line. | ||
* @param elemPos Position of an element in the doubly linked list memoryInfo | ||
* @param bytes Size of the allocation in bytes | ||
* @returns Nothing. | ||
*/ | ||
void editInfo(int elemPos, size_t bytes) | ||
{ | ||
int counter = 0; | ||
mem_info* tmp = memoryInformation; | ||
|
||
while (counter != elemPos) | ||
{ | ||
tmp = tmp->next; | ||
counter++; | ||
} | ||
tmp->bytes += bytes; | ||
} | ||
|
||
/** | ||
* @brief malloc_dbg function is a wrapper around the malloc function. | ||
* @details This function calls malloc and allocates the number of bytes passed in the parameters. | ||
* If the allocation succeeds then it add the pointer returned by malloc in the mem_info list. | ||
* @param bytes Size of the allocation in bytes | ||
* @param filename Caller file | ||
* @param functionName Caller function | ||
* @returns Pointer returned by malloc if it's valid, NULL otherwhise. | ||
*/ | ||
void* malloc_dbg(size_t bytes, int line, const char* filename, const char* functionName) | ||
{ | ||
void* ptrToReturn = malloc(bytes); | ||
int pos = 0; | ||
if (!ptrToReturn) | ||
{ | ||
return NULL; | ||
} | ||
|
||
// We must check atexitCalled value to know if we already called the function | ||
if (!atexitCalled) | ||
{ | ||
atexit(printLeaks); // Used to call printLeaks when the program exit | ||
atexitCalled = 1; | ||
} | ||
|
||
pos = inList(filename, line); | ||
if (pos == -1) | ||
{ | ||
// Add a new element in the mem_info list | ||
memoryInformation = addMemInfo(memoryInformation, ptrToReturn, bytes, line, filename, functionName); | ||
if (!memoryInformation) | ||
{ | ||
free(ptrToReturn); | ||
return NULL; | ||
} | ||
} | ||
else | ||
{ | ||
editInfo(pos, bytes); | ||
} | ||
return ptrToReturn; | ||
} | ||
|
||
/** | ||
* @brief calloc_dbg function is a wrapper around the calloc function. | ||
* @details This function calls calloc and allocates the number of bytes passed in the parameters. | ||
* If the allocation succeeds then it add the pointer returned by malloc in the mem_info list. | ||
* @param elementCount number of element to allocate | ||
* @param elementSize Size of each element | ||
* @param line Line number in the caller file | ||
* @param filename Caller file | ||
* @param functionName Caller function | ||
* @returns Pointer returned by calloc if it's valid, NULL otherwhise. | ||
*/ | ||
void* calloc_dbg(size_t elementCount, size_t elementSize, int line, const char* filename, const char* functionName) | ||
{ | ||
void* ptrToReturn = calloc(elementCount, elementSize); | ||
if (!ptrToReturn) | ||
{ | ||
return NULL; | ||
} | ||
|
||
// We must check atexitCalled value to know if we already called the function | ||
if (!atexitCalled) | ||
{ | ||
atexit(printLeaks); // Used to call printLeaks when the program exit | ||
atexitCalled = 1; | ||
} | ||
|
||
// Add a new element in the mem_info list | ||
memoryInformation = addMemInfo(memoryInformation, ptrToReturn, elementCount * elementSize, line, filename, functionName); | ||
if (!memoryInformation) | ||
{ | ||
free(ptrToReturn); | ||
return NULL; | ||
} | ||
|
||
return ptrToReturn; | ||
} | ||
|
||
/** | ||
* @brief free_dbg function is used to free the memory allocated to a pointer. | ||
* @details This function free the memory pointed by the pointer passed in parameter. | ||
* To free this pointer, we loop through the mem_info list and check if we find the pointer. | ||
* Once it's found, the pointer is freed and the element is deleted from the list. | ||
* @param ptrToFree Pointer that must be freed | ||
* @returns Nothing. | ||
*/ | ||
void free_dbg(void* ptrToFree) | ||
{ | ||
mem_info* tmp = memoryInformation; | ||
mem_info* toFree = NULL; | ||
mem_info* previous = NULL; | ||
|
||
// Check if the head contains the pointer to free | ||
if (tmp->ptr == ptrToFree) | ||
{ | ||
toFree = tmp; | ||
memoryInformation = tmp->next; | ||
free(toFree->ptr); | ||
free(toFree); | ||
if (memoryInformation) | ||
{ | ||
memoryInformation->previous = NULL; | ||
} | ||
return; | ||
} | ||
|
||
// We can loop through the list without any problems, the head is not the pointer | ||
while (tmp) | ||
{ | ||
if (tmp->ptr == ptrToFree) // If we found the pointer that must be freed | ||
{ | ||
toFree = tmp; | ||
tmp = tmp->next; | ||
previous = toFree->previous; | ||
|
||
if (previous) | ||
{ | ||
previous->next = tmp; | ||
} | ||
if (tmp) | ||
{ | ||
tmp->previous = previous; | ||
} | ||
|
||
free(toFree->ptr); | ||
if (toFree == memoryInformation) | ||
{ | ||
memoryInformation = NULL; | ||
} | ||
free(toFree); | ||
return; | ||
} | ||
tmp = tmp->next; | ||
} | ||
} | ||
|
||
/** | ||
* @brief printLeaks function is used to print all the memory leaks. | ||
* @details This function is called when the program exits. It loop through the mem_info list and if it's not empty, | ||
* it prints the memory leaks. | ||
* @returns Nothing. | ||
*/ | ||
void printLeaks() | ||
{ | ||
mem_info* tmp = memoryInformation; | ||
mem_info* previous = NULL; | ||
size_t sum = 0; | ||
int nbBlocks = 0; | ||
|
||
if (tmp) | ||
{ | ||
printf("Memory Leaks detected.\n"); | ||
} | ||
|
||
while (tmp) | ||
{ | ||
previous = tmp; | ||
printf("\n%ld bytes lost\n", tmp->bytes); | ||
printf("address : 0x%p in %s\t%s:%d\n", tmp->ptr, tmp->functionName, tmp->fileName, tmp->line); | ||
printf("\n====================================\n"); | ||
sum += tmp->bytes; | ||
tmp = tmp->next; | ||
free(previous); | ||
nbBlocks++; | ||
} | ||
|
||
printf("SUMMARY :\n%ld bytes lost in %d blocks\n", sum, nbBlocks); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
/** | ||
* @file | ||
* @brief Header file that contains macros used to replace malloc/calloc and free. | ||
* @details | ||
* Macros malloc, calloc and free respectively calls malloc_dbg, calloc_dbg and free_dbg. | ||
* malloc_dbg and calloc_dbg allocates memory using the "real" malloc and calloc and store | ||
* the pointer returned (with additional informations) in a linked list. | ||
* Thanks to this linked list, it is possible to check memory leaks. | ||
* @author [tinouduart33](https://github.com/tinouduart33) | ||
* @see malloc_dbg.c | ||
*/ | ||
|
||
#ifndef MALLOC_DBG_H | ||
#define MALLOC_DBG_H | ||
|
||
/** This macro replace the standard malloc function with malloc_dbg. | ||
* */ | ||
#define malloc(bytes) malloc_dbg(bytes, __LINE__, __FILE__, __FUNCTION__) | ||
|
||
/** This macro replace the standard calloc function with calloc_dbg. | ||
* */ | ||
#define calloc(elemCount, elemSize) calloc_dbg(elemCount, elemSize, __LINE__, __FILE__, __FUNCTION__) | ||
|
||
/** This macro replace the standard free function with free_dbg. | ||
* */ | ||
#define free(ptr) free_dbg(ptr) | ||
|
||
void* malloc_dbg(size_t bytes, int line, const char* filename, const char* functionName); | ||
|
||
void* calloc_dbg(size_t elementCount, size_t elementSize, int line, const char* filename, const char* functionName); | ||
|
||
void free_dbg(void* ptrToFree); | ||
|
||
void printLeaks(void); | ||
|
||
#endif /* MALLOC_DBG_H */ |
Oops, something went wrong.