diff --git a/CMakeLists.txt b/CMakeLists.txt index 1fe14701..4a680b18 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,6 +48,7 @@ endif() ## Add subdirectories containing CMakeLists.txt # to configure and compile files in the respective folders +add_subdirectory(developer_tools) add_subdirectory(hash) add_subdirectory(misc) add_subdirectory(games) diff --git a/developer_tools/CMakeLists.txt b/developer_tools/CMakeLists.txt new file mode 100644 index 00000000..848250b8 --- /dev/null +++ b/developer_tools/CMakeLists.txt @@ -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} ) diff --git a/developer_tools/malloc_dbg.c b/developer_tools/malloc_dbg.c new file mode 100644 index 00000000..83b01cfd --- /dev/null +++ b/developer_tools/malloc_dbg.c @@ -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 /// For the malloc, calloc and free functions. +#include /// For IO operations (printf). +#include /// 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); +} diff --git a/developer_tools/malloc_dbg.h b/developer_tools/malloc_dbg.h new file mode 100644 index 00000000..5ff11962 --- /dev/null +++ b/developer_tools/malloc_dbg.h @@ -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 */ diff --git a/developer_tools/test_malloc_dbg.c b/developer_tools/test_malloc_dbg.c new file mode 100644 index 00000000..830ada04 --- /dev/null +++ b/developer_tools/test_malloc_dbg.c @@ -0,0 +1,31 @@ +/** + * @file + * @brief File used to test the malloc_dbg, calloc_dbg and free_dbg functions. + * @details + * This file only have a main function that calls malloc, calloc and free. + * When the program exits, memory leaks must be printed. + * @author [tinouduart33](https://github.com/tinouduart33) + * @see malloc_dbg.c, malloc_dbg.h + */ + +#include /// For IO operations if needed. +#include /// For the EXIT_SUCCESS macro and the "real" malloc, calloc and free functions. +#include "malloc_dbg.h" /// For the macros malloc, calloc and free and the malloc_dbg, calloc_dbg and free_dbg functions. + + +/** + * @brief Main function + * @param argc number of arguments (not used) + * @param argv list of arguments (not used) + * @returns 0 on exit + */ +int main(int argc, char* argv[]) +{ + int* iptr = malloc(10 * sizeof(int)); + char* cptr = calloc(256, sizeof(char)); + + free(iptr); + // free(cptr); + + return 0; +}