/* * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef CYTOPLASM_MEMORY_H #define CYTOPLASM_MEMORY_H /*** * @Nm Memory * @Nd Smart memory management. * @Dd January 9 2023 * * .Nm * is an API that allows for smart memory management and profiling. It * wraps the standard library functions * .Xr malloc 3 , * .Xr realloc 3 , * and * .Xr free 3 , * and offers identical semantics, while providing functionality that * the standard library doesn't have, such as getting statistics on the * total memory allocated on the heap, and getting the size of a block * given a pointer. Additionally, thanks to preprocessor macros, the * exact file and line number at which an allocation, re-allocation, or * free occured can be obtained given a pointer. Finally, all the * blocks allocated on the heap can be iterated and evaluated, and a * callback function can be executed every time a memory operation * occurs. * .Pp * In the future, this API could include a garbage collector that * automatically frees memory it detects as being no longer in use. * However, this feature does not yet exist. * .Pp * A number of macros are available, which make the * .Nm * API much easier to use. They are as follows: * .Bl -bullet -offset indent * .It * .Fn Malloc "x" * .It * .Fn Realloc "x" "y" * .It * .Fn Free "x" * .El * .Pp * These macros expand to * .Fn MemoryAllocate , * .Fn MemoryReallocate , * and * .Fn MemoryFree * with the second and third parameters set to __FILE__ and __LINE__. * This allows * .Nm * to be used exactly how the standard library functions would be * used. In fact, the functions to which these macros expand are not * intended to be used directly; for the best results, use these * macros. */ #include /** * These values are passed into the memory hook function to indicate * the action that just happened. */ typedef enum MemoryAction { MEMORY_ALLOCATE, MEMORY_REALLOCATE, MEMORY_FREE, MEMORY_BAD_POINTER } MemoryAction; #define Malloc(x) MemoryAllocate(x, __FILE__, __LINE__) #define Realloc(x, s) MemoryReallocate(x, s, __FILE__, __LINE__) #define Free(x) MemoryFree(x, __FILE__, __LINE__) /** * The memory information is opaque, but can be accessed using the * functions defined by this API. */ typedef struct MemoryInfo MemoryInfo; /** * Allocate the specified number of bytes on the heap. This function * has the same semantics as * .Xr malloc 3 , * except that it takes the file name and line number at which the * allocation occurred. */ extern void * MemoryAllocate(size_t, const char *, int); /** * Change the size of the object pointed to by the given pointer * to the given number of bytes. This function has the same semantics * as * .Xr realloc 3 , * except that it takes the file name and line number at which the * reallocation occurred. */ extern void * MemoryReallocate(void *, size_t, const char *, int); /** * Free the memory at the given pointer. This function has the same * semantics as * .Xr free 3 , * except that it takes the file name and line number at which the * free occurred. */ extern void MemoryFree(void *, const char *, int); /** * Get the total number of bytes that the program has allocated on the * heap. This operation iterates over all heap allocations made with * .Fn MemoryAllocate * and then returns a total count, in bytes. */ extern size_t MemoryAllocated(void); /** * Iterate over all heap allocations made with * .Fn MemoryAllocate * and call * .Fn MemoryFree * on them. This function immediately invalidates all pointers to * blocks on the heap, and any subsequent attempt to read or write to * data on the heap will result in undefined behavior. This is * typically called at the end of the program, just before exit. */ extern void MemoryFreeAll(void); /** * Fetch information about an allocation. This function takes a raw * pointer, and if * . Nm * knows about the pointer, it returns a structure that can be used * to obtain information about the block of memory that the pointer * points to. */ extern MemoryInfo * MemoryInfoGet(void *); /** * Get the size in bytes of the block of memory represented by the * specified memory info structure. */ extern size_t MemoryInfoGetSize(MemoryInfo *); /** * Get the file name in which the block of memory represented by the * specified memory info structure was allocated. */ extern const char * MemoryInfoGetFile(MemoryInfo *); /** * Get the line number on which the block of memory represented by the * specified memory info structure was allocated. */ extern int MemoryInfoGetLine(MemoryInfo *); /** * Get a pointer to the block of memory represented by the specified * memory info structure. */ extern void * MemoryInfoGetPointer(MemoryInfo *); /** * This function takes a pointer to a function that takes the memory * info structure, as well as a void pointer for caller-provided * arguments. It iterates over all the heap memory currently allocated * at the time of calling, executing the function on each allocation. */ extern void MemoryIterate(void (*) (MemoryInfo *, void *), void *); /** * Specify a function to be executed whenever a memory operation * occurs. The MemoryAction argument specifies the operation that * occurred on the block of memory represented by the memory info * structure. The function also takes a void pointer to caller-provided * arguments. */ extern void MemoryHook(void (*) (MemoryAction, MemoryInfo *, void *), void *); /** * Read over the block of memory represented by the given memory info * structure and generate a hexadecimal and ASCII string for each * chunk of the block. This function takes a callback function that * takes the following parameters in order: * .Bl -bullet -offset indent * .It * The current offset from the beginning of the block of memory in * bytes. * .It * A null-terminated string containing the next 16 bytes of the block * encoded as space-separated hex values. * .It * A null-terminated string containing the ASCII representation of the * same 16 bytes of memory. This ASCII representation is safe to print * to a terminal or other text device, because non-printable characters * are encoded as a . (period). * .It * Caller-passed pointer. * .El */ extern void MemoryHexDump(MemoryInfo *, void (*) (size_t, char *, char *, void *), void *); #endif