diff --git a/include/dmalloc.h b/include/dmalloc.h new file mode 100644 index 00000000000..38e628dbd80 --- /dev/null +++ b/include/dmalloc.h @@ -0,0 +1,1240 @@ +/* + * Defines for the dmalloc library + * + * Copyright 2020 by Gray Watson + * + * This file is part of the dmalloc package. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose and without fee is hereby granted, provided + * that the above copyright notice and this permission notice appear + * in all copies, and that the name of Gray Watson not be used in + * advertising or publicity pertaining to distribution of the document + * or software without specific, written prior permission. + * + * Gray Watson makes no representations about the suitability of the + * software described herein for any purpose. It is provided "as is" + * without express or implied warranty. + * + * The author may be contacted via http://dmalloc.com/ + */ + +#ifndef __DMALLOC_H__ +#define __DMALLOC_H__ + +/* this is dmalloc.h.2 */ +/* produced by configure, inserted into dmalloc.h */ + +/* const is available */ +/* strdup is not a macro */ +#undef DMALLOC_STRDUP_MACRO +/* strndup is not a macro */ +#undef DMALLOC_STRNDUP_MACRO + +/* + * the definition of DMALLOC_SIZE + * + * NOTE: some architectures have malloc, realloc, etc. + * using unsigned instead of unsigned long. You may + * have to edit this by hand to fix any compilation + * warnings or errors. + */ +#include +#define DMALLOC_SIZE size_t + +/* + * We use stdarg.h for the dmalloc_message and + * dmalloc_vmessage functions. + */ +#include +#define DMALLOC_STDARG 1 + +/* dmalloc version defines */ +#define DMALLOC_VERSION_MAJOR 5 /* X.0.0 */ +#define DMALLOC_VERSION_MINOR 6 /* 0.X.0 */ +#define DMALLOC_VERSION_PATCH 5 /* 0.0.X */ + +/* NOTE: start of dmalloc.h.3 */ + +/* this defines what type the standard void memory-pointer is */ +#if (defined(__STDC__) && __STDC__ == 1) || defined(__cplusplus) || defined(STDC_HEADERS) || defined(_ISO_STDLIB_ISO_H) +#define DMALLOC_PNT void * +#define DMALLOC_FREE_RET void +#else +#define DMALLOC_PNT char * +#define DMALLOC_FREE_RET int +#define DMALLOC_FREE_RET_INT +#endif + +/* + * Malloc function return codes + */ +#define CALLOC_ERROR 0L /* error from calloc */ +#define MALLOC_ERROR 0L /* error from malloc */ +#define REALLOC_ERROR 0L /* error from realloc */ + +/* NOTE: this if for non- __STDC__ systems only */ +#define FREE_ERROR 0 /* error from free */ +#define FREE_NOERROR 1 /* no error from free */ + +#define DMALLOC_ERROR 0 /* function failed */ +#define DMALLOC_NOERROR 1 /* function succeeded */ + +#define DMALLOC_VERIFY_ERROR 0 /* function failed */ +#define DMALLOC_VERIFY_NOERROR 1 /* function succeeded */ +#define MALLOC_VERIFY_ERROR DMALLOC_VERIFY_ERROR +#define MALLOC_VERIFY_NOERROR DMALLOC_VERIFY_NOERROR + +/* + * Dmalloc function IDs for the dmalloc_track_t callback function. + */ +#define DMALLOC_FUNC_MALLOC 10 /* malloc function called */ +#define DMALLOC_FUNC_CALLOC 11 /* calloc function called */ +#define DMALLOC_FUNC_REALLOC 12 /* realloc function called */ +#define DMALLOC_FUNC_RECALLOC 13 /* recalloc called */ +#define DMALLOC_FUNC_MEMALIGN 14 /* memalign function called */ +#define DMALLOC_FUNC_VALLOC 15 /* valloc function called */ +#define DMALLOC_FUNC_STRDUP 16 /* strdup function called */ +#define DMALLOC_FUNC_FREE 17 /* free function called */ +#define DMALLOC_FUNC_CFREE 18 /* cfree function called */ + +#define DMALLOC_FUNC_NEW 20 /* new function called */ +#define DMALLOC_FUNC_NEW_ARRAY 21 /* new[] function called */ +#define DMALLOC_FUNC_DELETE 22 /* delete function called */ +#define DMALLOC_FUNC_DELETE_ARRAY 23 /* delete[] function called */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Tracking function that can be registered by calling dmalloc_track(...). This callback function will + * be called each time an allocation occurs. + */ +typedef void (*dmalloc_track_t)(const char *file, const unsigned int line, + const int func_id, + const DMALLOC_SIZE byte_size, + const DMALLOC_SIZE alignment, + const DMALLOC_PNT old_addr, + const DMALLOC_PNT new_addr); + + +/* internal dmalloc error number for reference purposes only */ +extern +int dmalloc_errno; + +/* logfile for dumping dmalloc info, DMALLOC_LOGFILE env var overrides this */ +extern +char *dmalloc_logpath; + +/* + * void dmalloc_shutdown + * + * Shutdown the dmalloc library and provide statistics if necessary. + */ +extern +void dmalloc_shutdown(void); + +#if FINI_DMALLOC +/* + * void __fini_dmalloc + * + * Automatic function to close dmalloc supported by some operating + * systems. Pretty cool OS/compiler hack. By default it is not + * necessary because we use atexit() and on_exit() to register the + * close functions which are more portable. + */ +extern +void __fini_dmalloc(void); +#endif /* if FINI_DMALLOC */ + +/* + * DMALLOC_PNT dmalloc_malloc + * + * Allocate and return a memory block of a certain size. + * + * Returns a valid pointer on success or NULL on failure. + * + * ARGUMENTS: + * + * file -> File-name or return-address of the caller. + * + * line -> Line-number of the caller. + * + * size -> Number of bytes requested. + * + * func_id -> Function-id to identify the type of call. See + * dmalloc.h. + * + * alignment -> To align the new block to a certain number of bytes, + * set this to a value greater than 0. + * + * xalloc_b -> If set to 1 then print an error and exit if we run out + * of memory. + */ +extern +DMALLOC_PNT dmalloc_malloc(const char *file, const int line, + const DMALLOC_SIZE size, const int func_id, + const DMALLOC_SIZE alignment, + const int xalloc_b); + +/* + * DMALLOC_PNT dmalloc_realloc + * + * Resizes and old pointer to a new number of bytes. + * + * Returns a valid pointer on success or NULL on failure. + * + * ARGUMENTS: + * + * file -> File-name or return-address of the caller. + * + * line -> Line-number of the caller. + * + * old_pnt -> Pointer to an existing memory chunk that we are + * resizing. If this is NULL then it basically does a malloc. + * + * new_size -> New number of bytes requested for the old pointer. + * + * func_id -> Function-id to identify the type of call. See + * dmalloc.h. + * + * xalloc_b -> If set to 1 then print an error and exit if we run out + * of memory. + */ +extern +DMALLOC_PNT dmalloc_realloc(const char *file, const int line, + DMALLOC_PNT old_pnt, DMALLOC_SIZE new_size, + const int func_id, const int xalloc_b); + +/* + * int dmalloc_free + * + * Release a pointer back into the heap. + * + * Returns FREE_NOERROR on success or FREE_ERROR on failure. + * + * Note: many operating systems define free to return (void) so this + * return value may be filtered. Dumb. + * + * ARGUMENTS: + * + * file -> File-name or return-address of the caller. + * + * line -> Line-number of the caller. + * + * pnt -> Existing pointer we are freeing. + * + * func_id -> Function-id to identify the type of call. See + * dmalloc.h. + */ +extern +int dmalloc_free(const char *file, const int line, DMALLOC_PNT pnt, + const int func_id); + +/* + * DMALLOC_PNT dmalloc_strndup + * + * Allocate and return an allocated block of memory holding a copy of + * a string of a certain number of characters. + * + * Returns a valid pointer on success or NULL on failure. + * + * ARGUMENTS: + * + * file -> File-name or return-address of the caller. + * + * line -> Line-number of the caller. + * + * string -> String we are duplicating. + * + * max_len -> Max length of the string we are duplicating. Set to -1 for none. + * + * xalloc_b -> If set to 1 then print an error and exit if we run out + * of memory. + */ +extern +char *dmalloc_strndup(const char *file, const int line, + const char *string, const int max_len, + const int xalloc_b); + +/* + * DMALLOC_PNT malloc + * + * Overloading the malloc(3) function. Allocate and return a memory + * block of a certain size. + * + * Returns a valid pointer on success or NULL on failure. + * + * ARGUMENTS: + * + * size -> Number of bytes requested. + */ +extern +DMALLOC_PNT malloc(DMALLOC_SIZE size); + +/* + * DMALLOC_PNT calloc + * + * Overloading the calloc(3) function. Returns a block of zeroed memory. + * + * Returns a valid pointer on success or NULL on failure. + * + * ARGUMENTS: + * + * num_elements -> Number of elements being allocated. + * + * size -> The number of bytes in each element. + */ +extern +DMALLOC_PNT calloc(DMALLOC_SIZE num_elements, DMALLOC_SIZE size); + +/* + * DMALLOC_PNT realloc + * + * Overload of realloc(3). Resizes and old pointer to a new number of bytes. + * + * Returns a valid pointer on success or NULL on failure. + * + * ARGUMENTS: + * + * old_pnt -> Pointer to an existing memory chunk that we are + * resizing. If this is NULL then it basically does a malloc. + * + * new_size -> New number of bytes requested for the old pointer. + */ +extern +DMALLOC_PNT realloc(DMALLOC_PNT old_pnt, DMALLOC_SIZE new_size); + +/* + * DMALLOC_PNT recalloc + * + * Overload of recalloc(3) which exists on some systems. Resizes and + * old pointer to a new number of bytes. If we are expanding, then + * any new bytes will be zeroed. + * + * Returns a valid pointer on success or NULL on failure. + * + * ARGUMENTS: + * + * old_pnt -> Pointer to an existing memory chunk that we are resizing. + * + * new_size -> New number of bytes requested for the old pointer. + */ +extern +DMALLOC_PNT recalloc(DMALLOC_PNT old_pnt, DMALLOC_SIZE new_size); + +/* + * DMALLOC_PNT memalign + * + * Overloading the memalign(3) function. Allocate and return a memory + * block of a certain size which have been aligned to a certain + * alignment. + * + * Returns a valid pointer on success or NULL on failure. + * + * ARGUMENTS: + * + * alignment -> Value to which the allocation must be aligned. This + * should probably be a multiple of 2 with a maximum value equivalent + * to the block-size which is often 1k or 4k. + * + * size -> Number of bytes requested. + */ +extern +DMALLOC_PNT memalign(DMALLOC_SIZE alignment, DMALLOC_SIZE size); + +/* + * DMALLOC_PNT valloc + * + * Overloading the valloc(3) function. Allocate and return a memory + * block of a certain size which have been aligned to page boundaries + * which are often 1k or 4k. + * + * Returns a valid pointer on success or NULL on failure. + * + * ARGUMENTS: + * + * size -> Number of bytes requested. + */ +extern +DMALLOC_PNT valloc(DMALLOC_SIZE size); + +#ifndef DMALLOC_STRDUP_MACRO +/* + * DMALLOC_PNT strdup + * + * Overload of strdup(3). Allocate and return an allocated block of + * memory holding a copy of a string. + * + * Returns a valid pointer on success or NULL on failure. + * + * ARGUMENTS: + * + * string -> String we are duplicating. + */ +extern +char *strdup(const char *string); +#endif /* ifndef DMALLOC_STRDUP_MACRO */ + +#ifndef DMALLOC_STRNDUP_MACRO +/* + * DMALLOC_PNT strndup + * + * Overload of strndup(3). Allocate and return an allocated block of + * memory holding a copy of a string with a maximum length. + * + * Returns a valid pointer on success or NULL on failure. + * + * ARGUMENTS: + * + * string -> String we are duplicating. + * + * max_len -> Max length of the string to duplicate. + */ +extern +char *strndup(const char *string, const DMALLOC_SIZE max_len); +#endif /* ifndef DMALLOC_STRNDUP_MACRO */ + +/* + * DMALLOC_FREE_RET free + * + * Release a pointer back into the heap. + * + * Returns FREE_ERROR, FREE_NOERROR or void depending on whether STDC + * is defined by your compiler. + * + * ARGUMENTS: + * + * pnt -> Existing pointer we are freeing. + */ +extern +DMALLOC_FREE_RET free(DMALLOC_PNT pnt); + +/* + * DMALLOC_FREE_RET cfree + * + * Same as free. + * + * Returns FREE_ERROR, FREE_NOERROR or void depending on whether STDC + * is defined by your compiler. + * + * ARGUMENTS: + * + * pnt -> Existing pointer we are freeing. + */ +extern +DMALLOC_FREE_RET cfree(DMALLOC_PNT pnt); + +/* + * int dmalloc_verify + * + * Verify a pointer which has previously been allocated by the + * library or check the entire heap. + * + * Returns MALLOC_VERIFY_NOERROR on success or MALLOC_VERIFY_ERROR on failure. + * + * ARGUMENTS: + * + * pnt -> Pointer we are verifying. If 0L then check the entire heap. + */ +extern +int dmalloc_verify(const DMALLOC_PNT pnt); + +/* + * int malloc_verify + * + * Verify a pointer which has previously been allocated by the + * library. Same as dmalloc_verify. + * + * Returns MALLOC_VERIFY_NOERROR on success or MALLOC_VERIFY_ERROR on failure. + * + * ARGUMENTS: + * + * pnt -> Pointer we are verifying. If 0L then check the entire heap. + */ +extern +int malloc_verify(const DMALLOC_PNT pnt); + +/* + * int dmalloc_verify_pnt + * + * This function is mainly used by the arg_check.c functions to verify + * specific pointers. This can be used by users to provide more fine + * grained tests on pointers. + * + * Returns MALLOC_VERIFY_NOERROR on success or MALLOC_VERIFY_ERROR on failure. + * + * ARGUMENTS: + * + * file -> File-name or return-address of the caller. You can use + * __FILE__ for this argument or 0L for none. + * + * line -> Line-number of the caller. You can use __LINE__ for this + * argument or 0 for none. + * + * func -> Function string which is checking the pointer. 0L if none. + * + * pnt -> Pointer we are checking. + * + * exact_b -> Set to 1 if this pointer was definitely handed back from + * a memory allocation. If set to 0 then this pointer can be inside + * another allocation or outside the heap altogether. + * + * strlen_b -> Set to 1 to make sure that this pointer can handle + * strlen(pnt) + 1 bytes up to the maximum specified by min_size. If + * this is 1 and min_size > 0 then it is in effect a strnlen. + * + * min_size -> Make sure that pointer can hold at least that many + * bytes if inside of the heap. If 0 then don't check the size. + */ +extern +int dmalloc_verify_pnt_strsize(const char *file, const int line, + const char *func, const void *pnt, + const int exact_b, const int strlen_b, + const int min_size); + +/* + * int dmalloc_verify_pnt + * + * This function is mainly used by the arg_check.c functions to verify + * specific pointers. This can be used by users to provide more fine + * grained tests on pointers. + * + * Returns MALLOC_VERIFY_NOERROR on success or MALLOC_VERIFY_ERROR on failure. + * + * ARGUMENTS: + * + * file -> File-name or return-address of the caller. You can use + * __FILE__ for this argument or 0L for none. + * + * line -> Line-number of the caller. You can use __LINE__ for this + * argument or 0 for none. + * + * func -> Function string which is checking the pointer. 0L if none. + * + * pnt -> Pointer we are checking. + * + * exact_b -> Set to 1 if this pointer was definitely handed back from + * a memory allocation. If set to 0 then this pointer can be inside + * another allocation or outside the heap altogether. + * + * min_size -> Make sure that pointer can hold at least that many + * bytes if inside of the heap. If -1 then make sure it can handle + * strlen(pnt) + 1 bytes (+1 for the \0). If 0 then don't check the + * size. If you need strnlen functionality with a maximum on the + * strlen, see dmalloc_verify_pnt_strsize. + */ +extern +int dmalloc_verify_pnt(const char *file, const int line, const char *func, + const void *pnt, const int exact_b, + const int min_size); + +/* + * unsigned int dmalloc_debug + * + * Set the global debug functionality flags. You can also use + * dmalloc_debug_setup. + * + * Note: you cannot add or remove certain flags such as signal + * handlers since they are setup at initialization time only. + * + * Returns the old debug flag value. + * + * ARGUMENTS: + * + * flags -> Flag value to set. Pass in 0 to disable all debugging. + */ +extern +unsigned int dmalloc_debug(const unsigned int flags); + +/* + * char *dmalloc_debug_current + * + * Returns the current debug functionality flags. This allows you to + * save a dmalloc library state to be restored later. + */ +extern +unsigned int dmalloc_debug_current(void); + +/* + * char *dmalloc_debug_current_env + * + * Returns the current debug environment. This allows you to save a + * dmalloc library state to be restored later with a call to + * dmalloc_debug_setup(). + * + * Returns the current debug environment. + * + * ARGUMENTS: + * + * env_buf -> Buffer to use for getting the environment. + * + * env_buf_size -> Size of the buffer. + */ +extern +char *dmalloc_debug_current_env(char *env_buf, const int env_buf_size); + +/* + * void dmalloc_debug_setup + * + * Set the global debugging functionality as an option string. + * Normally this would be pased in in the DMALLOC_OPTIONS + * environmental variable. This is here to override the env or for + * circumstances where modifying the environment is not possible or + * does not apply such as servers or cgi-bin programs. + * + * ARGUMENTS: + * + * options_str -> Options string to set the library flags. + */ +extern +void dmalloc_debug_setup(const char *options_str); + +/* + * int dmalloc_examine + * + * Examine a pointer and pass back information on its allocation size + * as well as the file and line-number where it was allocated. If the + * file and line number is not available, then it will pass back the + * allocation location's return-address if available. + * + * Returns DMALLOC_NOERROR on success or DMALLOC_ERROR on failure. + * + * ARGUMENTS: + * + * pnt -> Pointer we are checking. + * + * user_size_p <- Pointer to a DMALLOC_SIZE type variable which, if + * not NULL, will be set to the size of bytes from the pointer. + * + * total_size_p <- Poiner to a DMALLOC_SIZE type variable which, if + * not NULL, will be set to the total size given for this allocation + * including administrative overhead. + * + * file_p <- Pointer to a character pointer which, if not NULL, will + * be set to the file where the pointer was allocated. + * + * line_p <- Pointer to an unsigned integer which, if not NULL, will + * be set to the line-number where the pointer was allocated. + * + * ret_attr_p <- Pointer to a void pointer, if not NULL, will be set + * to the return-address where the pointer was allocated. + * + * used_mark_p <- Poiner to an unsigned integer which, if not NULL, + * will be set to the mark of when the pointer was last "used". This + * could be when it was allocated, reallocated, or freed. + * + * seen_p <- Poiner to an unsigned long which, if not NULL, will be + * set to the number of times that this pointer has been allocated, + * realloced, or freed. NOTE: LOG_PNT_SEEN_COUNT must be set to 1 + * otherwise no seen information is available and it will be set to 0. + */ +extern +int dmalloc_examine(const DMALLOC_PNT pnt, DMALLOC_SIZE *user_size_p, + DMALLOC_SIZE *total_size_p, char **file_p, + unsigned int *line_p, DMALLOC_PNT *ret_attr_p, + void **backtrace_p, unsigned int *backtrace_depth_p, + unsigned long *used_mark_p, unsigned long *seen_p); + +/* + * void dmalloc_track + * + * Register an allocation tracking function which will be called each + * time an allocation occurs. + * + * ARGUMENTS: + * + * track_func -> Function to register as the tracking function. Set + * to NULL to disable. + */ +extern +void dmalloc_track(const dmalloc_track_t track_func); + +/* + * unsigned long dmalloc_mark + * + * Return to the caller the current "mark" which can be used later by + * dmalloc_log_changed to log the changed pointers since this point. + * Multiple marks can be saved and used. + * + * This is also the iteration number and can be logged at the front of + * each memory transaction in the logfile with the LOG_ITERATION + * define in settings.h and can be logged with each pointer with the + * LOG_PNT_ITERATION define in settings.h. + */ +extern +unsigned long dmalloc_mark(void); + +/* + * unsigned long dmalloc_memory_allocated + * + * Return the total number of bytes allocated by the program so far. + */ +extern +unsigned long dmalloc_memory_allocated(void); + +/* + * unsigned int dmalloc_page_size + * + * Get the page-size being used by dmalloc. + */ +extern +unsigned int dmalloc_page_size(void); + +/* + * unsigned long dmalloc_count_changed + * + * Count the changed memory bytes since a particular mark. + * + * Returns the number of bytes since mark. + * + * ARGUMENTS: + * + * mark -> Sets the point from which to count the changed memory. You + * can use dmalloc_mark to get the current mark value which can later + * be passed in here. Pass in 0 to report on the unfreed memory since + * the program started. + * + * not_freed_b -> Set to 1 to count the new pointers that are non-freed. + * + * free_b -> Set to 1 to count the new pointers that are freed. + */ +extern +unsigned long dmalloc_count_changed(const unsigned long mark, + const int not_freed_b, const int free_b); + +/* + * void dmalloc_log_status + * + * Dump dmalloc statistics to logfile. + */ +extern +void dmalloc_log_stats(void); + +/* + * void dmalloc_log_unfreed + * + * Dump unfreed-memory info to logfile. + */ +extern +void dmalloc_log_unfreed(void); + +/* + * void dmalloc_log_changed + * + * Dump the pointers that have changed since a point in time. + * + * ARGUMENTS: + * + * mark -> Sets the point to compare against. You can use + * dmalloc_mark to get the current mark value which can later be + * passed in here. Pass in 0 to log what has changed since the + * program started. + * + * not_freed_b -> Set to 1 to log the new pointers that are non-freed. + * + * free_b -> Set to 1 to log the new pointers that are freed. + * + * details_b -> Set to 1 to dump the individual pointers that have + * changed otherwise the summaries will be logged. + */ +extern +void dmalloc_log_changed(const unsigned long mark, const int not_freed_b, + const int free_b, const int details_b); + +/* + * void dmalloc_vmessage + * + * Message writer with vprintf like arguments which adds a line to the + * dmalloc logfile. + * + * ARGUMENTS: + * + * format -> Printf-style format statement. + * + * args -> Already converted pointer to a stdarg list. + */ +extern +void dmalloc_vmessage(const char *format, va_list *args); + +/* + * void dmalloc_message + * + * Message writer with printf like arguments which adds a line to the + * dmalloc logfile. + * + * ARGUMENTS: + * + * format -> Printf-style format statement. + * + * ... -> Variable argument list. + */ +extern +void dmalloc_message(const char *format, ...) +#ifdef __GNUC__ + __attribute__ ((format (printf, 1, 2))) +#endif +; + +/* + * void dmalloc_get_stats + * + * Get a number of statistics about the current heap. + * + * ARGUMENTS: + * + * heap_low_p <- Pointer to pointer which, if not 0L, will be set to + * the low address in the heap. + * + * heap_high_p <- Pointer to pointer which, if not 0L, will be set to + * the high address in the heap. + * + * total_space_p <- Pointer to an unsigned long which, if not 0L, will + * be set to the total space managed by the library including user + * space, administrative space, and overhead. + * + * user_space_p <- Pointer to an unsigned long which, if not 0L, will + * be set to the space given to the user process (allocated and free). + * + * current_allocated_p <- Pointer to an unsigned long which, if not + * 0L, will be set to the current allocated space given to the user + * process. + * + * current_pnt_np <- Pointer to an unsigned long which, if not 0L, + * will be set to the current number of pointers allocated by the user + * process. + * + * max_allocated_p <- Pointer to an unsigned long which, if not 0L, + * will be set to the maximum allocated space given to the user + * process. + * + * max_pnt_np <- Pointer to an unsigned long which, if not 0L, will be + * set to the maximum number of pointers allocated by the user + * process. + * + * max_one_p <- Pointer to an unsigned long which, if not 0L, will be + * set to the maximum allocated with 1 call by the user process. + */ +extern +void dmalloc_get_stats(DMALLOC_PNT *heap_low_p, + DMALLOC_PNT *heap_high_p, + unsigned long *total_space_p, + unsigned long *user_space_p, + unsigned long *current_allocated_p, + unsigned long *current_pnt_np, + unsigned long *max_allocated_p, + unsigned long *max_pnt_np, + unsigned long *max_one_p); + +/* + * const char *dmalloc_strerror + * + * Convert a dmalloc error code into its string equivalent. + * + * Returns String version of the error on success or "unknown error" on failure. + * + * ARGUMENTS: + * + * error_num -> Error number we are converting. + */ +extern +const char *dmalloc_strerror(const int error_num); + + +#ifdef __cplusplus +} +#endif + +/* + * alloc macros to provide for memory FILE/LINE debugging information. + */ + +#ifndef DMALLOC_DISABLE + +#undef malloc +#define malloc(size) \ + dmalloc_malloc(__FILE__, __LINE__, (size), DMALLOC_FUNC_MALLOC, 0, 0) +#undef calloc +#define calloc(count, size) \ + dmalloc_malloc(__FILE__, __LINE__, (count)*(size), DMALLOC_FUNC_CALLOC, 0, 0) +#undef realloc +#define realloc(ptr, size) \ + dmalloc_realloc(__FILE__, __LINE__, (ptr), (size), DMALLOC_FUNC_REALLOC, 0) +#undef recalloc +#define recalloc(ptr, size) \ + dmalloc_realloc(__FILE__, __LINE__, (ptr), (size), DMALLOC_FUNC_RECALLOC, 0) +#undef memalign +#define memalign(alignment, size) \ + dmalloc_malloc(__FILE__, __LINE__, (size), DMALLOC_FUNC_MEMALIGN, \ + (alignment), 0 /* no xalloc */) +#undef valloc +#define valloc(size) \ + dmalloc_malloc(__FILE__, __LINE__, (size), DMALLOC_FUNC_VALLOC, \ + 0 /* special case */, 0 /* no xalloc */) +#ifndef DMALLOC_STRDUP_MACRO +#undef strdup +#define strdup(str) \ + dmalloc_strndup(__FILE__, __LINE__, (str), -1, 0) +#endif +#ifndef DMALLOC_STRNDUP_MACRO +#undef strndup +#define strndup(str, len) \ + dmalloc_strndup(__FILE__, __LINE__, (str), (len), 0) +#endif +#undef free +#define free(ptr) \ + dmalloc_free(__FILE__, __LINE__, (ptr), DMALLOC_FUNC_FREE) + +#undef xmalloc +#define xmalloc(size) \ + dmalloc_malloc(__FILE__, __LINE__, (size), DMALLOC_FUNC_MALLOC, 0, 1) +#undef xcalloc +#define xcalloc(count, size) \ + dmalloc_malloc(__FILE__, __LINE__, (count)*(size), DMALLOC_FUNC_CALLOC, 0, 1) +#undef xrealloc +#define xrealloc(ptr, size) \ + dmalloc_realloc(__FILE__, __LINE__, (ptr), (size), DMALLOC_FUNC_REALLOC, 1) +#undef xrecalloc +#define xrecalloc(ptr, size) \ + dmalloc_realloc(__FILE__, __LINE__, (ptr), (size), DMALLOC_FUNC_RECALLOC, 1) +#undef xmemalign +#define xmemalign(alignment, size) \ + dmalloc_malloc(__FILE__, __LINE__, (size), DMALLOC_FUNC_MEMALIGN, \ + (alignment), 1) +#undef xvalloc +#define xvalloc(size) \ + dmalloc_malloc(__FILE__, __LINE__, (size), DMALLOC_FUNC_VALLOC, 0, 1) +#undef xstrdup +#define xstrdup(str) \ + dmalloc_strndup(__FILE__, __LINE__, (str), -1, 1) +#undef xstrndup +#define xstrndup(str, len) \ + dmalloc_strndup(__FILE__, __LINE__, (str), (len), 1) +#undef xfree +#define xfree(ptr) \ + dmalloc_free(__FILE__, __LINE__, (ptr), DMALLOC_FUNC_FREE) + +#ifdef DMALLOC_FUNC_CHECK + +/* + * do debugging on the following functions. this may cause compilation or + * other problems depending on your architecture. + */ +#undef atoi +#define atoi(str) \ + _dmalloc_atoi(__FILE__, __LINE__, (str)) +#undef atol +#define atol(str) \ + _dmalloc_atol(__FILE__, __LINE__, (str)) + +#undef bcmp +#define bcmp(b1, b2, len) \ + _dmalloc_bcmp(__FILE__, __LINE__, (b1), (b2), (len)) +#undef bcopy +#define bcopy(from, to, len) \ + _dmalloc_bcopy(__FILE__, __LINE__, (from), (to), (len)) +#undef bzero +#define bzero(buf, len) \ + _dmalloc_bzero(__FILE__, __LINE__, (buf), (len)) + +#undef memcmp +#define memcmp(b1, b2, len) \ + _dmalloc_memcmp(__FILE__, __LINE__, (b1), (b2), (len)) +#undef memcpy +#define memcpy(to, from, len) \ + _dmalloc_memcpy(__FILE__, __LINE__, (to), (from), (len)) +#undef memmove +#define memmove(to, from, len) \ + _dmalloc_memmove(__FILE__, __LINE__, (to), (from), (len)) +#undef memset +#define memset(buf, ch, len) \ + _dmalloc_memset(__FILE__, __LINE__, (buf), (ch), (len)) + +#undef index +#define index(str, ch) \ + _dmalloc_index(__FILE__, __LINE__, (str), (ch)) +#undef rindex +#define rindex(str, ch) \ + _dmalloc_rindex(__FILE__, __LINE__, (str), (ch)) + +#undef strcat +#define strcat(to, from) \ + _dmalloc_strcat(__FILE__, __LINE__, (to), (from)) +#undef strcmp +#define strcmp(s1, s2) \ + _dmalloc_strcmp(__FILE__, __LINE__, (s1), (s2)) +#undef strlen +#define strlen(str) \ + _dmalloc_strlen(__FILE__, __LINE__, (str)) +#undef strtok +#define strtok(str, sep) \ + _dmalloc_strtok(__FILE__, __LINE__, (str), (sep)) + +#undef memccpy +#define memccpy(s1, s2, ch, len) \ + _dmalloc_memccpy(__FILE__, __LINE__, (s1), (s2),(ch),(len)) +#undef memchr +#define memchr(s1, ch, len) \ + _dmalloc_memchr(__FILE__, __LINE__, (s1), (ch), (len)) + +#undef strchr +#define strchr(str, ch) \ + _dmalloc_strchr(__FILE__, __LINE__, (str), (ch)) +#undef strrchr +#define strrchr(str, ch) \ + _dmalloc_strrchr(__FILE__, __LINE__, (str), (ch)) + +#undef strcpy +#define strcpy(to, from) \ + _dmalloc_strcpy(__FILE__, __LINE__, (to), (from)) +#undef strncpy +#define strncpy(to, from, len) \ + _dmalloc_strncpy(__FILE__, __LINE__, (to), (from), (len)) +#undef strcasecmp +#define strcasecmp(s1, s2) \ + _dmalloc_strcasecmp(__FILE__, __LINE__, (s1), (s2)) +#undef strncasecmp +#define strncasecmp(s1, s2, len) \ + _dmalloc_strncasecmp(__FILE__, __LINE__, (s1), (s2), (len)) +#undef strspn +#define strspn(str, list) \ + _dmalloc_strspn(__FILE__, __LINE__, (str), (list)) +#undef strcspn +#define strcspn(str, list) \ + _dmalloc_strcspn(__FILE__, __LINE__, (str), (list)) +#undef strncat +#define strncat(to, from, len) \ + _dmalloc_strncat(__FILE__, __LINE__, (to), (from), (len)) +#undef strncmp +#define strncmp(s1, s2, len) \ + _dmalloc_strncmp(__FILE__, __LINE__, (s1), (s2), (len)) +#undef strpbrk +#define strpbrk(str, list) \ + _dmalloc_strpbrk(__FILE__, __LINE__, (str), (list)) +#undef strstr +#define strstr(str, pat) \ + _dmalloc_strstr(__FILE__, __LINE__, (str), (pat)) + +#endif /* DMALLOC_FUNC_CHECK */ +#endif /* ! DMALLOC_DISABLE */ + +/* + * feel free to add your favorite functions here and to arg_check.[ch] + */ + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * Dummy function for checking atoi's arguments. + */ +extern +int _dmalloc_atoi(const char *file, const int line, const char *str); + +/* + * Dummy function for checking atol's arguments. + */ +extern +long _dmalloc_atol(const char *file, const int line, const char *str); + +/* + * Dummy function for checking bcmp's arguments. + */ +extern +int _dmalloc_bcmp(const char *file, const int line, + const void *b1, const void *b2, const DMALLOC_SIZE len); + +/* + * Dummy function for checking bcopy's arguments. + */ +extern +void _dmalloc_bcopy(const char *file, const int line, + const void *from, void *to, const DMALLOC_SIZE len); + +/* + * Dummy function for checking bzero's arguments. + */ +extern +void _dmalloc_bzero(const char *file, const int line, + void *buf, const DMALLOC_SIZE len); + +/* + * Dummy function for checking index's arguments. + */ +extern +char *_dmalloc_index(const char *file, const int line, + const char *str, const char ch); + +/* + * Dummy function for checking memccpy's arguments. + */ +extern +void *_dmalloc_memccpy(const char *file, const int line, + void *dest, const void *src, const int ch, + const DMALLOC_SIZE len); + +/* + * Dummy function for checking memchr's arguments. + */ +extern +void *_dmalloc_memchr(const char *file, const int line, + const void *s1, const int ch, const DMALLOC_SIZE len); + +/* + * Dummy function for checking memcmp's arguments. + */ +extern +int _dmalloc_memcmp(const char *file, const int line, + const void *b1, const void *b2, const DMALLOC_SIZE len); + +/* + * Dummy function for checking memcpy's arguments. + */ +extern +void *_dmalloc_memcpy(const char *file, const int line, + void *to, const void *from, const DMALLOC_SIZE len); + +/* + * Dummy function for checking memmove's arguments. + */ +extern +void *_dmalloc_memmove(const char *file, const int line, + void *to, const void *from, const DMALLOC_SIZE len); + +/* + * Dummy function for checking memset's arguments. + */ +extern +void *_dmalloc_memset(const char *file, const int line, void *buf, + const int ch, const DMALLOC_SIZE len); + +/* + * Dummy function for checking rindex's arguments. + */ +extern +char *_dmalloc_rindex(const char *file, const int line, + const char *str, const char ch); + +/* + * Dummy function for checking strcasecmp's arguments. + */ +extern +int _dmalloc_strcasecmp(const char *file, const int line, + const char *s1, const char *s2); + +/* + * Dummy function for checking strcat's arguments. + */ +extern +char *_dmalloc_strcat(const char *file, const int line, + char *to, const char *from); + +/* + * Dummy function for checking strchr's arguments. + */ +extern +char *_dmalloc_strchr(const char *file, const int line, + const char *str, const int ch); + +/* + * Dummy function for checking strcmp's arguments. + */ +extern +int _dmalloc_strcmp(const char *file, const int line, + const char *s1, const char *s2); + +/* + * Dummy function for checking strcpy's arguments. + */ +extern +char *_dmalloc_strcpy(const char *file, const int line, + char *to, const char *from); + +/* + * Dummy function for checking strcspn's arguments. + */ +extern +int _dmalloc_strcspn(const char *file, const int line, + const char *str, const char *list); + +/* + * Dummy function for checking strlen's arguments. + */ +extern +DMALLOC_SIZE _dmalloc_strlen(const char *file, const int line, + const char *str); + +/* + * Dummy function for checking strncasecmp's arguments. + */ +extern +int _dmalloc_strncasecmp(const char *file, const int line, + const char *s1, const char *s2, + const DMALLOC_SIZE len); + +/* + * Dummy function for checking strncat's arguments. + */ +extern +char *_dmalloc_strncat(const char *file, const int line, + char *to, const char *from, const DMALLOC_SIZE len); + +/* + * Dummy function for checking strncmp's arguments. + */ +extern +int _dmalloc_strncmp(const char *file, const int line, + const char *s1, const char *s2, + const DMALLOC_SIZE len); + +/* + * Dummy function for checking strncpy's arguments. + */ +extern +char *_dmalloc_strncpy(const char *file, const int line, + char *to, const char *from, const DMALLOC_SIZE len); + +/* + * Dummy function for checking strpbrk's arguments. + */ +extern +char *_dmalloc_strpbrk(const char *file, const int line, + const char *str, const char *list); + +/* + * Dummy function for checking strrchr's arguments. + */ +extern +char *_dmalloc_strrchr(const char *file, const int line, + const char *str, const int ch); + +/* + * Dummy function for checking strspn's arguments. + */ +extern +int _dmalloc_strspn(const char *file, const int line, + const char *str, const char *list); + +/* + * Dummy function for checking strstr's arguments. + */ +extern +char *_dmalloc_strstr(const char *file, const int line, + const char *str, const char *pat); + +/* + * Dummy function for checking strtok's arguments. + */ +extern +char *_dmalloc_strtok(const char *file, const int line, + char *str, const char *sep); + + +#ifdef __cplusplus +} +#endif + +#endif /* ! __DMALLOC_H__ */ diff --git a/mm/Kconfig b/mm/Kconfig index eb737faf066..48abe027a41 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -25,6 +25,47 @@ config MM_CUSTOMIZE_MANAGER endchoice +config MM_DMALLOC + bool "dmalloc heap debug tool" + default n + ---help--- + dmalloc(https://dmalloc.com) memory manager strategy. + +if MM_DMALLOC + +config MM_DMALLOC_OPTIONS + string "dmalloc option" + default "debug=0x02f06d00" + +config MM_DMALLOC_MAX_SKIP_LEVEL + int "dmalloc max skip level" + default 8 + range 1 32 + +config MM_DMALLOC_FREED_POINTER_DELAY + int "dmalloc freed pointer delay" + default 32 + +config MM_DMALLOC_MAX_BACKTRACE_DEPTH + int "dmalloc max backtrace depth" + default 8 + depends on UNWINDER + +config MM_DMALLOC_MAX_TAB_BACKTRACE_DEPTH + int "dmalloc max table backtrace depth" + default MM_DMALLOC_MAX_BACKTRACE_DEPTH + depends on UNWINDER + +config MM_DMALLOC_MEMORY_TABLE_TOP_LOG + int "dmalloc memory table top size" + default 64 + +config MM_DMALLOC_MEMORY_TABLE_SIZE + int "dmalloc memory table size" + default MM_DMALLOC_MEMORY_TABLE_TOP_LOG + +endif + config MM_KERNEL_HEAP bool "Support a protected, kernel heap" default y diff --git a/mm/Makefile b/mm/Makefile index c94de9450cd..d25465cdcf9 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -23,7 +23,11 @@ include $(TOPDIR)/Make.defs # Sources and paths include mm_heap/Make.defs +ifeq ($(CONFIG_MM_DMALLOC),y) +include dmalloc/Make.defs +else include umm_heap/Make.defs +endif include kmm_heap/Make.defs include mm_gran/Make.defs include shm/Make.defs diff --git a/mm/dmalloc/Make.defs b/mm/dmalloc/Make.defs new file mode 100644 index 00000000000..95d46eb76d3 --- /dev/null +++ b/mm/dmalloc/Make.defs @@ -0,0 +1,49 @@ +############################################################################ +# mm/dmalloc/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +# dmalloc memory allocator + +ifeq ($(CONFIG_MM_DMALLOC),y) +CSRCS += append.c arg_check.c chunk.c compat.c dmalloc_rand.c +CSRCS += dmalloc_tab.c env.c error.c heap.c user_malloc.c +CSRCS += mm_dmalloc.c + +CFLAGS += ${shell $(DEFINE) "$(CC)" HAVE_EXECINFO_H=1} +CFLAGS += ${shell $(DEFINE) "$(CC)" HAVE_STDARG_H=1} +CFLAGS += ${shell $(DEFINE) "$(CC)" HAVE_STDIO_H=1} +CFLAGS += ${shell $(DEFINE) "$(CC)" HAVE_STDLIB_H=1} +CFLAGS += ${shell $(DEFINE) "$(CC)" HAVE_STRING_H=1} +CFLAGS += ${shell $(DEFINE) "$(CC)" HAVE_STRINGS_H=1} +CFLAGS += ${shell $(DEFINE) "$(CC)" HAVE_SIGNAL_H=1} +CFLAGS += ${shell $(DEFINE) "$(CC)" HAVE_SYS_CYGWIN_H=0} +CFLAGS += ${shell $(DEFINE) "$(CC)" HAVE_SYS_MMAN_H=1} +CFLAGS += ${shell $(DEFINE) "$(CC)" HAVE_SYS_TYPES_H=1} +CFLAGS += ${shell $(DEFINE) "$(CC)" HAVE_UNISTD_H=1} +CFLAGS += ${shell $(DEFINE) "$(CC)" HAVE_W32API_WINBASE_H=0} +CFLAGS += ${shell $(DEFINE) "$(CC)" HAVE_W32API_WINDEF_H=0} + +CFLAGS += ${shell $(INCDIR) "$(CC)" $(TOPDIR)/mm/dmalloc} +CFLAGS += ${shell $(INCDIR) "$(CC)" $(TOPDIR)/mm/dmalloc/dmalloc} + +# Add the dmalloc directory to the build + +DEPPATH += --dep-path dmalloc --dep-path dmalloc/dmalloc +VPATH += :dmalloc:dmalloc/dmalloc +endif diff --git a/mm/dmalloc/conf.h b/mm/dmalloc/conf.h new file mode 100644 index 00000000000..4bb69319ee2 --- /dev/null +++ b/mm/dmalloc/conf.h @@ -0,0 +1,293 @@ +/* conf.h. Generated from conf.h.in by configure. */ +/* + * Automatic configuration flags + * + * Copyright 2020 by Gray Watson + * + * This file is part of the dmalloc package. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose and without fee is hereby granted, provided + * that the above copyright notice and this permission notice appear + * in all copies, and that the name of Gray Watson not be used in + * advertising or publicity pertaining to distribution of the document + * or software without specific, written prior permission. + * + * Gray Watson makes no representations about the suitability of the + * software described herein for any purpose. It is provided "as is" + * without express or implied warranty. + * + * The author may be contacted via http://dmalloc.com/ + */ + +#ifndef __CONF_H__ +#define __CONF_H__ + +#include + +/* please see settings.h for manual configuration options */ + +/* + * NOTE: The following settings should not need to be tuned by hand. + */ + +/* + * Set to 1 if the mprotect function was found and the PROT_NONE, + * PROT_READ, and PROT_WRITE defines were found in sys/mman.h. This + * is so that we can restrict access to certain blocks of memory. + */ +#define PROTECT_ALLOWED 1 + +/* + * (char *)sbrk(const int incr) is the main heap-memory allocation + * routine that most systems employ. This extends the program's data + * space by INCR number of bytes. + * + * NOTE: If configure generates a 0 for this and HAVE_MMAP on your + * system, you should see the INTERNAL_MEMORY_SPACE setting in the + * settings.h file which is created from the settings.dist file. + */ +#define HAVE_SBRK 1 +void *sbrk(intptr_t incr); + +/* + * (void *)mmap(...) is another heap-memory allocation routine that + * systems employ. On newer systems it is often preferable over sbrk. + * It allocates a block of memory in the virtual-memory system. The + * USE_MMAP define is set if the standard mmap call works. + * + * NOTE: If configure generates a 0 for this and HAVE_SBRK on your + * system, you should see the INTERNAL_MEMORY_SPACE setting in the + * settings.h file which is created from the settings.dist file. + */ +#define HAVE_MMAP 1 +#define USE_MMAP 0 +#define HAVE_MUNMAP 1 + +/* + * This is the basic block size in bits. If possible, the configure + * script will set this to be the value returned by the getpagesize() + * function. If not then some sort of best guess will be necessary. + * 15 (meaning basic block size of 32k) will probably be good. + * + * NOTE: some sbrk functions round to the correct page-size. No + * problems aside from a possible small increase in the administration + * overhead should happen if this value is too high. + */ +#ifdef CONFIG_MM_PGSIZE +#define BASIC_BLOCK CONFIG_MM_PGSIZE +#else +#define BASIC_BLOCK 12 +#endif + +/* + * The alignment value of all allocations in number of bytes for + * loading admin information before an allocation. If possible, the + * configure script will set this to be the value returned by + * sizeof(long) which in most systems is the register width. + * + * NOTE: the value will never be auto-configured to be less than 8 + * because some system (like sparc for instance) report the sizeof(long) + * == 4 while the register size is 8 bytes. Certain memory needs to be of + * the same base as the register size (stack frames, code, etc.). Any + * ideas how I can determine the register size in a better (and portable) + * fashion? + * + * NOTE: larger the number the more memory may be wasted by certain + * debugging settings like fence-post checking. + */ +#define ALLOCATION_ALIGNMENT 8 + +/* + * When doing pointer arithmatic and other long value manipulation, + * what type should we use. Hopefully we get a 64-bit value here. + */ +#define PNT_ARITH_TYPE unsigned long + +/* + * This checks to see if the abort routine does extensive cleaning up + * before halting a program. If so then it may call malloc functions + * making the library go recursive. If abort is set to not okay then + * you should tune the KILL_PROCESS and SIGNAL_INCLUDE options in + * settings.h if you want the library to be able to dump core. + */ +#define ABORT_OKAY 1 + +/* + * This checks to see if we can include signal.h and get SIGHUP, + * SIGINT, and SIGTERM for the catch-signals token. With this token, + * you can have the library do an automatic shutdown if we see the + * above signals. + */ +#define SIGNAL_OKAY 0 +#define RETSIGTYPE void + +/* + * This checks to see if we can include return.h and use the assembly + * macros there to call the callers address for logging. If you do + * not want this behavior, then set the USE_RETURN_MACROS to 0 in the + * settings.h file. + */ +#define RETURN_MACROS_WORK 1 + +/* + * Why can't the compiler folks agree about this. I really hate Unix + * sometimes for its blatant disregard for anything approaching a + * standard. + */ +#define IDENT_WORKS 1 + +/* + * Which pthread include file to use. + */ +#define HAVE_PTHREAD_H 1 +#define HAVE_PTHREADS_H 0 + +/* + * What pthread functions do we have? + */ +#define HAVE_PTHREAD_MUTEX_INIT 1 +#define HAVE_PTHREAD_MUTEX_LOCK 1 +#define HAVE_PTHREAD_MUTEX_UNLOCK 1 + +/* + * What is the pthread mutex type? Usually (always?) it is + * pthread_mutex_t. + */ +#define THREAD_MUTEX_T sem_t + +/* + * On some systems, you initialize mutex variables with NULL. Others + * require various stupid non-portable incantations. The OSF 3.2 guys + * should be ashamed of themselves. This only is used if the + * LOCK_THREADS setting is enabled in the settings.h. + */ +#define THREAD_LOCK_INIT_VAL 1 + +/* + * Under the Cygwin environment, when malloc calls getenv, it core + * dumps. This is because Cygwin, as far as I know, is loading the + * shared libraries for the various system functions and goes + * recursive with a call to getenv. Ugh. + * + * So we have to delay the getenv call. This sets when we can do the + * getenv call so the environmental processing is delayed. + */ +#define GETENV_SAFE 1 + +/* + * See whether support exists for the constructor attribute which + * allows the library to run code before main() is called. I know + * that later versions of gcc have support for this and maybe other + * compilers do as well. + */ +#define CONSTRUCTOR_WORKS 1 + +/* + * See whether support exists for the destructor attribute which + * allows the library to run code after main() is exited. I know + * that later versions of gcc have support for this and maybe other + * compilers do as well. + */ +#define DESTRUCTOR_WORKS 1 + +/* + * See if we have the GetEnvironmentVariableA Cygwin function. This + * is used as a getenv replacement. + */ +#define HAVE_GETENVIRONMENTVARIABLEA 0 + +/* + * LIBRARY DEFINES: + */ + +/* + * Whether the compiler and OS has standard C headers. + */ +#define STDC_HEADERS 1 + +/* + * Some systems have functions which can register routines to be + * called by exit(3) (or when the program returns from main). This + * functionality allows the dmalloc_shutdown() routine to be called + * automatically upon program completion so that the library can log + * statistics. Use the AUTO_SHUTDOWN define above to disable this. + * Please send me mail if this functionality exists on your system but + * in another name. + * + * NOTE: If neither is available, take a look at atexit.c in the + * contrib directory which may provide this useful functionality for + * your system. + */ +#define HAVE_ATEXIT CONFIG_SCHED_ATEXIT +#define HAVE_ON_EXIT CONFIG_SCHED_ONEXIT + +/* Is the DMALLOC_SIZE type unsigned? */ +#define DMALLOC_SIZE_UNSIGNED 1 + +/* + * The dmalloc library provides its own versions of the following + * functions, or knows how to work around their absence. + */ +/* bells and whistles */ +#define HAVE_FORK 0 +#define HAVE_GETHOSTNAME 1 +#define HAVE_GETPID 1 +#define HAVE_GETUID 1 +#define HAVE_TIME 1 +#define HAVE_CTIME 1 + +#define HAVE_VPRINTF 1 +#define HAVE_SNPRINTF 1 +#define HAVE_VSNPRINTF 1 + +#define HAVE_RECALLOC 0 +#define HAVE_MEMALIGN 1 +#define HAVE_VALLOC 1 + +#ifdef CONFIG_UNWINDER +#define HAVE_BACKTRACE 1 +#else +#define HAVE_BACKTRACE 0 +#endif + +/* various functions for arg checking and/or internal use */ + +#define HAVE_ATOI 1 +#define HAVE_ATOL 1 +#define HAVE_BCMP 1 +#define HAVE_BCOPY 1 +#define HAVE_BZERO 1 +#define HAVE_INDEX 1 +#define HAVE_MEMCCPY 1 +#define HAVE_MEMCHR 1 +#define HAVE_MEMCMP 1 +#define HAVE_MEMCPY 1 +#define HAVE_MEMMOVE 1 +#define HAVE_MEMSET 1 +#define HAVE_RINDEX 1 +#define HAVE_STRCASECMP 1 +#define HAVE_STRCAT 1 +#define HAVE_STRCHR 1 +#define HAVE_STRCMP 1 +#define HAVE_STRCPY 1 +#define HAVE_STRCSPN 1 +#define HAVE_STRDUP 1 +#define HAVE_STRLEN 1 +#define HAVE_STRNLEN 1 +#define HAVE_STRNCASECMP 1 +#define HAVE_STRNCAT 1 +#define HAVE_STRNCMP 1 +#define HAVE_STRNCPY 1 +#define HAVE_STRNDUP 1 +#define HAVE_STRPBRK 1 +#define HAVE_STRRCHR 1 +#define HAVE_STRSEP 1 +#define HAVE_STRSPN 1 +#define HAVE_STRSTR 1 +#define HAVE_STRTOK 1 + +/* manual settings */ +#include "settings.h" + +#endif /* ! __CONF_H__ */ diff --git a/mm/dmalloc/mm_dmalloc.c b/mm/dmalloc/mm_dmalloc.c new file mode 100644 index 00000000000..f05a83694d7 --- /dev/null +++ b/mm/dmalloc/mm_dmalloc.c @@ -0,0 +1,978 @@ +/**************************************************************************** + * mm/dmalloc/mm_dmalloc.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#define DMALLOC_DISABLE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "error_val.h" +#include "dmalloc.h" +#include "dmalloc_loc.h" +#include "return.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct mm_delaynode_s +{ + FAR struct mm_delaynode_s *flink; +}; + +struct mm_heap_s +{ + /* Mutually exclusive access to this data set is enforced with + * the following un-named semaphore. + */ + + FAR sem_t *mm_semaphore; + + /* This is the first and last of the heap */ + + FAR void *mm_heapstart[CONFIG_MM_REGIONS]; + size_t mm_heapsize[CONFIG_MM_REGIONS]; + + size_t mm_heapnext; + +#if CONFIG_MM_REGIONS > 1 + int mm_region; + int mm_nregions; +#endif + + /* Free delay list, for some situation can't do free immdiately */ + +#ifdef CONFIG_SMP + struct mm_delaynode_s *mm_delaylist[CONFIG_SMP_NCPUS]; +#else + struct mm_delaynode_s *mm_delaylist[1]; +#endif + +#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_MEMINFO) + struct procfs_meminfo_entry_s mm_procfs; +#endif +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +FAR struct mm_heap_s *g_mmheap; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: umm_add_delaylist + ****************************************************************************/ + +static void umm_add_delaylist(FAR void *mem) +{ +#if defined(CONFIG_BUILD_FLAT) || defined(__KERNEL__) + FAR struct mm_heap_s *heap = g_mmheap; + FAR struct mm_delaynode_s *tmp = mem; + irqstate_t flags; + + /* Delay the deallocation until a more appropriate time. */ + + flags = enter_critical_section(); + + tmp->flink = heap->mm_delaylist[up_cpu_index()]; + heap->mm_delaylist[up_cpu_index()] = tmp; + + leave_critical_section(flags); +#endif +} + +/**************************************************************************** + * Name: umm_free_delaylist + ****************************************************************************/ + +static void umm_free_delaylist(void) +{ +#if defined(CONFIG_BUILD_FLAT) || defined(__KERNEL__) + FAR struct mm_heap_s *heap = g_mmheap; + FAR struct mm_delaynode_s *tmp; + irqstate_t flags; + + /* Move the delay list to local */ + + flags = enter_critical_section(); + + tmp = heap->mm_delaylist[up_cpu_index()]; + heap->mm_delaylist[up_cpu_index()] = NULL; + + leave_critical_section(flags); + + /* Test if the delayed is empty */ + + while (tmp) + { + FAR void *address; + + /* Get the first delayed deallocation */ + + address = tmp; + tmp = tmp->flink; + + /* The address should always be non-NULL since that was checked in the + * 'while' condition above. + */ + + free(address); + } +#endif +} + +/**************************************************************************** + * Name: umm_mallinfo + ****************************************************************************/ + +static void umm_mallinfo(FAR void *data, FAR struct mallinfo *info) +{ + *info = mallinfo(); +} + +/**************************************************************************** + * Name: umm_try_initialize + * + * Description: + * Allocate and initialize the user heap if not yet. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_BUILD_KERNEL +static void umm_try_initialize(void) +{ + uintptr_t allocbase; + + /* Return if the user heap is already initialized. */ + + if (g_mmheap != NULL) + { + return; + } + + /* Allocate one page. If we provide a zero brkaddr to pgalloc(), + * it will create the first block in the correct virtual address + * space and return the start address of that block. + */ + + allocbase = pgalloc(0, 1); + DEBUGASSERT(allocbase != 0); + + /* Let umm_initialize do the real work. */ + + umm_initialize((FAR void *)allocbase, CONFIG_MM_PGSIZE); +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: umm_initsemaphore + * + * Description: + * Initialize the MM mutex + * + ****************************************************************************/ + +void umm_initsemaphore(FAR sem_t *sem, int val) +{ + FAR struct mm_heap_s *heap = g_mmheap; + + /* Initialize the MM semaphore to one (to support one-at-a-time access to + * private data sets). + */ + + _SEM_INIT(sem, 0, val); + heap->mm_semaphore = sem; +} + +/**************************************************************************** + * Name: umm_takesemaphore + * + * Description: + * Take the MM mutex. This may be called from the OS in certain conditions + * when it is impossible to wait on a semaphore: + * 1.The idle process performs the memory corruption check. + * 2.The task/thread free the memory in the exiting process. + * + * Input Parameters: + * sem - semaphore to be taken + * + * Returned Value: + * true if the semaphore can be taken, otherwise false. + * + ****************************************************************************/ + +bool umm_takesemaphore(FAR sem_t *sem) +{ + if (!sem) + { + return true; + } + +#if defined(CONFIG_BUILD_FLAT) || defined(__KERNEL__) + /* Check current environment */ + + if (up_interrupt_context()) + { + /* Can't take semaphore in the interrupt handler */ + + return false; + } + else +#endif + + /* getpid() returns the task ID of the task at the head of the ready-to- + * run task list. umm_takesemaphore() may be called during context + * switches. There are certain situations during context switching when + * the OS data structures are in flux and then can't be freed immediately + * (e.g. the running thread stack). + * + * This is handled by getpid() to return the special value -ESRCH to + * indicate this special situation. + */ + + if (getpid() < 0) + { + return false; + } +#if defined(CONFIG_BUILD_FLAT) || defined(__KERNEL__) + else if (sched_idletask()) + { + /* Try to take the semaphore */ + + return _SEM_TRYWAIT(sem) >= 0; + } +#endif + else + { + int ret; + + /* Take the semaphore (perhaps waiting) */ + + do + { + ret = _SEM_WAIT(sem); + + /* The only case that an error should occur here is if the wait + * was awakened by a signal. + */ + + if (ret < 0) + { + ret = _SEM_ERRVAL(ret); + DEBUGASSERT(ret == -EINTR || ret == -ECANCELED); + } + } + while (ret < 0); + + return true; + } +} + +/**************************************************************************** + * Name: umm_givesemaphore + * + * Description: + * Release the MM mutex when it is not longer needed. + * + ****************************************************************************/ + +void umm_givesemaphore(FAR sem_t *sem) +{ + if (sem) + { + DEBUGVERIFY(_SEM_POST(sem)); + } +} + +/**************************************************************************** + * Name: umm_addregion + * + * Description: + * This function adds a region of contiguous memory to the user heap. This + * function is exported from the user-space blob so that the kernel + * can initialize the user-mode allocator. + * + * Input Parameters: + * heapstart - Address of the beginning of the memory region + * heapsize - The size (in bytes) of the memory region. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void umm_addregion(FAR void *heapstart, size_t heapsize) +{ + FAR struct mm_heap_s *heap = g_mmheap; +#if CONFIG_MM_REGIONS > 1 + int idx; + + idx = heap->mm_nregions; + + /* Writing past CONFIG_MM_REGIONS would have catastrophic consequences */ + + DEBUGASSERT(idx < CONFIG_MM_REGIONS); + if (idx >= CONFIG_MM_REGIONS) + { + return; + } + +#else +# define idx 0 +#endif + + DEBUGVERIFY(umm_takesemaphore(heap->mm_semaphore)); + + minfo("Region %d: base=%p size=%zu\n", idx + 1, heapstart, heapsize); + + /* Save the start and end of the heap */ + + heap->mm_heapstart[idx] = heapstart; + heap->mm_heapsize[idx] = heapsize; + +#undef idx + +#if CONFIG_MM_REGIONS > 1 + heap->mm_nregions++; +#endif + + umm_givesemaphore(heap->mm_semaphore); +} + +/**************************************************************************** + * Name: umm_brkaddr + * + * Description: + * Return the break address of a region in the user heap. Zero is returned + * if the memory region is not initialized. + * + ****************************************************************************/ + +FAR void *umm_brkaddr(int region) +{ + FAR struct mm_heap_s *heap = g_mmheap; + +#if CONFIG_MM_REGIONS > 1 + DEBUGASSERT(region >= 0 && region < heap->mm_nregions); +#else + DEBUGASSERT(region == 0); +#endif + + return heap->mm_heapstart[region] + heap->mm_heapsize[region]; +} + +/**************************************************************************** + * Name: calloc + * + * Description: + * calloc() calculates the size of the allocation and calls dmalloc_malloc() + * + ****************************************************************************/ + +FAR void *calloc(size_t n, size_t elem_size) +{ + FAR const char *file; + + n *= elem_size; + if (n < elem_size) + { + return NULL; + } + + /* Free the dealy list first */ + + umm_free_delaylist(); + + /* Allocate from the dmalloc */ + + GET_RET_ADDR(file); + return dmalloc_malloc(file, DMALLOC_DEFAULT_LINE, + n, DMALLOC_FUNC_CALLOC, + 0 /* no alignment */, + 0 /* no xalloc messages */); +} + +#ifdef CONFIG_DEBUG_MM +/**************************************************************************** + * Name: umm_checkcorruption + * + * Description: + * umm_checkcorruption is used to check whether memory heap is normal. + * + ****************************************************************************/ + +void umm_checkcorruption(void) +{ + dmalloc_verify(NULL); +} +#endif + +/**************************************************************************** + * Name: umm_extend + * + * Description: + * Extend a region in the user heap by add a block of (virtually) + * contiguous memory to the end of the heap. + * + ****************************************************************************/ + +void umm_extend(FAR void *mem, size_t size, int region) +{ + FAR struct mm_heap_s *heap = g_mmheap; + + /* Make sure that we were passed valid parameters */ + +#if CONFIG_MM_REGIONS > 1 + DEBUGASSERT(region >= 0 && region < heap->mm_nregions); +#else + DEBUGASSERT(region == 0); +#endif + DEBUGASSERT(mem == heap->mm_heapstart[region] + heap->mm_heapsize[region]); + + /* Take the memory manager semaphore */ + + DEBUGVERIFY(umm_takesemaphore(heap->mm_semaphore)); + + /* Save the new size */ + + heap->mm_heapsize[region] += size; + + umm_givesemaphore(heap->mm_semaphore); +} + +/**************************************************************************** + * Name: free + * + * Description: + * Returns a chunk of user memory to the list of free nodes, merging with + * adjacent free chunks if possible. + * + ****************************************************************************/ + +void free(FAR void *mem) +{ + FAR const char *file; + int ret; + + GET_RET_ADDR(file); + ret = dmalloc_free(file, DMALLOC_DEFAULT_LINE, + mem, DMALLOC_FUNC_FREE); + if (ret == FREE_ERROR && dmalloc_errno == DMALLOC_ERROR_LOCK_FAIL) + { + umm_add_delaylist(mem); + } +} + +/**************************************************************************** + * Name: umm_heapmember + * + * Description: + * Check if an address lies in the user heap. + * + * Parameters: + * mem - The address to check + * + * Return Value: + * true if the address is a member of the user heap. false if not + * not. If the address is not a member of the user heap, then it + * must be a member of the user-space heap (unchecked) + * + ****************************************************************************/ + +bool umm_heapmember(FAR void *mem) +{ + FAR struct mm_heap_s *heap = g_mmheap; +#if CONFIG_MM_REGIONS > 1 + int i; + + /* A valid address from the heap for this region would have to lie + * between the region's two guard nodes. + */ + + for (i = 0; i < heap->mm_nregions; i++) + { + if (mem >= heap->mm_heapstart[i] && + mem < heap->mm_heapstart[i] + heap->mm_heapsize[i]) + { + return true; + } + } + + /* The address does not like any any region assigned to the heap */ + + return false; + +#else + /* A valid address from the heap would have to lie between the + * two guard nodes. + */ + + if (mem >= heap->mm_heapstart[0] && + mem < heap->mm_heapstart[0] + heap->mm_heapsize[0]) + { + return true; + } + + /* Otherwise, the address does not lie in the heap */ + + return false; + +#endif +} + +/**************************************************************************** + * Name: umm_initialize + * + * Description: + * Initialize the selected heap data structures, providing the initial + * heap region. This function will initialize the user heap. + * + * CONFIG_BUILD_FLAT: + * There is only kernel mode "blob" containing both kernel and + * application code. There is only one heap that is used by both the + * kernel and application logic. + * + * In this configuration, this function is called early in nx_start() + * to initialize the common heap. + * + * CONFIG_BUILD_PROTECTED + * In this configuration, there are two "blobs", one containing + * protected kernel logic and one containing unprotected application + * logic. Depending upon the setting of CONFIG_MM_KERNEL_HEAP there + * may be only a single shared heap, much as with CONFIG_BUILD_FLAT. + * Or there may be separate protected/kernel and unprotected/user + * heaps. + * + * In either case, this function is still called early in nx_start() + * to initialize the user heap. + * + * CONFIG_BUILD_KERNEL + * In this configuration there are multiple user heaps, one for each + * user process. Furthermore, each heap is initially empty; memory + * is added to each heap dynamically via sbrk(). The heap data + * structure was set to zero when the address environment was created. + * Otherwise, the heap is uninitialized. + * + * This function is not called at all. Rather, this function is called + * when each user process is created before the first allocation is made. + * + * Input Parameters: + * heapstart - Address of the beginning of the (initial) memory region + * heapsize - The size (in bytes) if the (initial) memory region. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void umm_initialize(FAR void *heapstart, size_t heapsize) +{ + FAR struct mm_heap_s *heap; + + minfo("Heap: start=%p size=%zu\n", heapstart, heapsize); + + /* Reserve a block space for mm_heap_s context */ + + DEBUGASSERT(heapsize > sizeof(struct mm_heap_s)); + heap = (FAR struct mm_heap_s *)heapstart; + memset(heap, 0, sizeof(struct mm_heap_s)); + heapstart += sizeof(struct mm_heap_s); + heapsize -= sizeof(struct mm_heap_s); + g_mmheap = heap; + + /* Add the initial region of memory to the heap */ + + umm_addregion(heapstart, heapsize); + + /* Initialize the default options */ + + dmalloc_debug_setup(CONFIG_MM_DMALLOC_OPTIONS); + +#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_MEMINFO) +#if defined(CONFIG_BUILD_FLAT) || defined(__KERNEL__) + heap->mm_procfs.name = "Umem"; + heap->mm_procfs.mallinfo = umm_mallinfo; + heap->mm_procfs.user_data = heap; + procfs_register_meminfo(&heap->mm_procfs); +#endif +#endif +} + +/**************************************************************************** + * Name: mallinfo + * + * Description: + * mallinfo returns a copy of updated current heap information for the + * user heap. + * + ****************************************************************************/ + +struct mallinfo mallinfo(void) +{ + unsigned long total_space; + unsigned long user_space; + unsigned long current_allocated; + unsigned long current_pnt; + struct mallinfo info; + + dmalloc_get_stats(NULL, NULL, &total_space, + &user_space, ¤t_allocated, ¤t_pnt, + NULL, NULL, NULL); + + info.arena = total_space; + info.ordblks = 0; + info.aordblks = current_pnt; + info.mxordblk = 0; + info.uordblks = current_allocated; + info.fordblks = user_space - current_allocated; + + return info; +} + +/**************************************************************************** + * Name: malloc_size + ****************************************************************************/ + +size_t malloc_size(FAR void *mem) +{ + DMALLOC_SIZE total_size; + + return dmalloc_examine(mem, NULL, &total_size, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL) == DMALLOC_NOERROR ? total_size : 0; +} + +/**************************************************************************** + * Name: malloc + * + * Description: + * Allocate memory from the user heap. + * + * Input Parameters: + * size - Size (in bytes) of the memory region to be allocated. + * + * Returned Value: + * The address of the allocated memory (NULL on failure to allocate) + * + ****************************************************************************/ + +FAR void *malloc(size_t size) +{ + FAR const char *file; + + /* Free the dealy list first */ + + umm_free_delaylist(); + + /* Allocate from the dmalloc */ + + GET_RET_ADDR(file); + return dmalloc_malloc(file, DMALLOC_DEFAULT_LINE, + size, DMALLOC_FUNC_MALLOC, + 0 /* no alignment */, + 0 /* no xalloc messages */); +} + +/**************************************************************************** + * Name: memalign + * + * Description: + * memalign requests more than enough space from malloc, finds a region + * within that chunk that meets the alignment request and then frees any + * leading or trailing space. + * + * The alignment argument must be a power of two (not checked). 8-byte + * alignment is guaranteed by normal malloc calls. + * + ****************************************************************************/ + +FAR void *memalign(size_t alignment, size_t size) +{ + FAR const char *file; + + /* Free the dealy list first */ + + umm_free_delaylist(); + + /* Allocate from the dmalloc */ + + GET_RET_ADDR(file); + return dmalloc_malloc(file, DMALLOC_DEFAULT_LINE, + size, DMALLOC_FUNC_MEMALIGN, + alignment, + 0 /* no xalloc messages */); +} + +/**************************************************************************** + * Name: realloc + * + * Description: + * Re-allocate memory in the user heap. + * + * Input Parameters: + * oldmem - The old memory allocated + * newsize - Size (in bytes) of the new memory region to be re-allocated. + * + * Returned Value: + * The address of the re-allocated memory (NULL on failure to re-allocate) + * + ****************************************************************************/ + +FAR void *realloc(FAR void *oldmem, size_t newsize) +{ + FAR const char *file; + + /* Free the dealy list first */ + + umm_free_delaylist(); + + /* Allocate from the dmalloc */ + + GET_RET_ADDR(file); + return dmalloc_realloc(file, DMALLOC_DEFAULT_LINE, + oldmem, newsize, + DMALLOC_FUNC_REALLOC, + 0 /* no xalloc messages */); +} + +/**************************************************************************** + * Name: sbrk + * + * Description: + * The sbrk() function is used to change the amount of space allocated + * for the calling process. The change is made by resetting the process's + * break value and allocating the appropriate amount of space. The amount + * of allocated space increases as the break value increases. + * + * The sbrk() function adds 'incr' bytes to the break value and changes + * the allocated space accordingly. If incr is negative, the amount of + * allocated space is decreased by incr bytes. The current value of the + * program break is returned by sbrk(0). + * + * Input Parameters: + * incr - Specifies the number of bytes to add or to remove from the + * space allocated for the process. + * + * Returned Value: + * Upon successful completion, sbrk() returns the prior break value. + * Otherwise, it returns (void *)-1 and sets errno to indicate the + * error: + * + * ENOMEM - The requested change would allocate more space than + * allowed under system limits. + * EAGAIN - The total amount of system memory available for allocation + * to this process is temporarily insufficient. This may occur even + * though the space requested was less than the maximum data segment + * size. + * + ****************************************************************************/ + +FAR void *sbrk(intptr_t incr) +{ + int errcode; + + DEBUGASSERT(incr >= 0); + if (incr < 0) + { + errcode = ENOSYS; + goto errout; + } + +#ifdef CONFIG_BUILD_KERNEL + uintptr_t brkaddr; + uintptr_t allocbase; + unsigned int pgincr; + size_t bytesize; + + /* Initialize the user heap if not yet */ + + umm_try_initialize(); + + /* Get the current break address (NOTE: assumes region 0). */ + + brkaddr = (uintptr_t)umm_brkaddr(0); + if (incr > 0) + { + /* Convert the increment to multiples of the page size */ + + pgincr = MM_NPAGES(incr); + + /* Allocate the requested number of pages and map them to the + * break address. + */ + + allocbase = pgalloc(brkaddr, pgincr); + if (allocbase == 0) + { + errcode = EAGAIN; + goto errout; + } + + /* Extend the heap (region 0) */ + + bytesize = pgincr << MM_PGSHIFT; + umm_extend((FAR void *)allocbase, bytesize, 0); + } + + return (FAR void *)brkaddr; +#else + FAR struct mm_heap_s *heap = g_mmheap; + FAR void *brkaddr = NULL; + +# if CONFIG_MM_REGIONS > 1 + for (; heap->mm_region < heap->mm_nregions; heap->mm_region++) + { + if (heap->mm_heapsize[heap->mm_region] - heap->mm_heapnext >= incr) + { + brkaddr = heap->mm_heapstart[heap->mm_region]; + break; + } + + heap->mm_heapnext = 0; + } +# else + if (heap->mm_heapsize[0] - heap->mm_heapnext >= incr) + { + brkaddr = heap->mm_heapstart[0]; + } +#endif + + if (brkaddr == NULL) + { + errcode = EAGAIN; + goto errout; + } + + brkaddr += heap->mm_heapnext; + heap->mm_heapnext += incr; + return brkaddr; +#endif + +errout: + set_errno(errcode); + return (FAR void *)-1; +} + +/**************************************************************************** + * Name: zalloc + * + * Description: + * Allocate and zero memory from the user heap. + * + * Input Parameters: + * size - Size (in bytes) of the memory region to be allocated. + * + * Returned Value: + * The address of the allocated memory (NULL on failure to allocate) + * + ****************************************************************************/ + +FAR void *zalloc(size_t size) +{ + FAR const char *file; + + /* Free the dealy list first */ + + umm_free_delaylist(); + + /* Allocate from the dmalloc */ + + GET_RET_ADDR(file); + return dmalloc_malloc(file, DMALLOC_DEFAULT_LINE, + size, DMALLOC_FUNC_CALLOC, + 0 /* no alignment */, + 0 /* no xalloc messages */); +} + +/**************************************************************************** + * Name: umm_fwrite/umm_write + * + * Description: + * Redirect dmalloc output to syslog + ****************************************************************************/ + +ssize_t umm_fwrite(FAR const void *ptr, size_t size, size_t items, + FAR FILE *stream) +{ + if (stream == stdout) + { + syslog(LOG_NOTICE, "%.*s", size * items, (FAR const char *)ptr); + return size * items; + } + else if (stream == stderr) + { + syslog(LOG_ERR, "%.*s", size * items, (FAR const char *)ptr); + return size * items; + } + else + { + return fwrite(ptr, size, items, stream); + } +} + +ssize_t umm_write(int fd, FAR const void *buf, size_t nbytes) +{ + if (fd == STDOUT_FILENO) + { + syslog(LOG_NOTICE, "%.*s", nbytes, (FAR const char *)buf); + return nbytes; + } + else if (fd == STDERR_FILENO) + { + syslog(LOG_ERR, "%.*s", nbytes, (FAR const char *)buf); + return nbytes; + } + else + { + return write(fd, buf, nbytes); + } +} diff --git a/mm/dmalloc/settings.h b/mm/dmalloc/settings.h new file mode 100644 index 00000000000..144ae64ca96 --- /dev/null +++ b/mm/dmalloc/settings.h @@ -0,0 +1,521 @@ +/* + * WARNING: this file was produced from settings.dist + * by the configure program. The configure script, when + * run again, will overwrite changed made here. + */ + +/* + * Manual configuration flags + * + * Copyright 2020 by Gray Watson + * + * This file is part of the dmalloc package. + * + * Permission to use, copy, modify, and distribute this software for + * any purpose and without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies, and that the name of Gray Watson not be used in advertising + * or publicity pertaining to distribution of the document or software + * without specific, written prior permission. + * + * Gray Watson makes no representations about the suitability of the + * software described herein for any purpose. It is provided "as is" + * without express or implied warranty. + * + * The author may be contacted via http://dmalloc.com/ + */ + +/* + * PROGRAMMING NOTE: this file cannot be included before conf.h, so + * you might as well only include conf.h and never this file. + */ + +#ifndef __SETTINGS_H__ +#define __SETTINGS_H__ + +/* + * Should we allow zero length allocations? This will generate the + * smallest possible allocation. + * + * FYI: if fence post checking is requested, the top and bottom of the + * fence post information will be touching. + */ +#define ALLOW_ALLOC_ZERO_SIZE 1 + +/* + * Should we allow realloc of a NULL pointer? If set to one, this + * will call malloc when 0L is realloc-ed. This is useful when you + * are extending an array in a loop and do not want to allocate it + * specially the first time. + */ +#define ALLOW_REALLOC_NULL 1 + +/* + * Should we allow realloc to a 0 size to cause the pointer to be + * freed? If set to one, this will call free when a pointer is + * realloc-ed to a size of 0. Thanks to Stefan Froehlich + * for patiently pointing that the realloc in just about every Unix + * has this functionality. + */ +#define ALLOW_REALLOC_SIZE_ZERO 1 + +/* + * I have swayed to public pressure and allowing free(0L) is now the + * default. Sigh. :-) + * + * Should we allow the free of a NULL pointer and if it happens, + * should a message be generated to that effect. Most (if not all) + * POSIX C libraries allow you to free() or delete() a NULL pointer + * and a number of programming reference manuals mention that freeing + * of NULL is "allowed". However, I believe that it is bad form, + * promotes lazy pointer handling, and can often hide program bugs. I + * encourage you to do something like the following in your programs: + * if (pnt != NULL) { free(pnt); } + * + * Setting ALLOW_FREE_NULL to 0 will cause an exception whenever a + * free of NULL is encountered allowing the user to determine where + * s/he is freeing a possibly random pointer. I recommend at least + * logging a message with the ALLOW_FREE_NULL_MESSAGE set to 1. + * + * Even with ALLOW_FREE_NULL set to 1, you can enable the + * 'error-free-null' token at runtime for a specific program to + * generate an exception when it sees a free(0L). + */ +#define ALLOW_FREE_NULL 1 +#define ALLOW_FREE_NULL_MESSAGE 1 + +/* + * Should we use the ra-address macros in return.h. These are system + * specific macros designed to return the return-address for logging + * callers (i.e. possible offenders) of malloc routines. + * + * Please mail me if you have any questions with this functionality. + */ +#define USE_RETURN_MACROS 1 + +/* + * Write this character into memory when it is allocated and not + * calloc-ed if the alloc-blank token is enabled. It will also write + * this into realloc'd memory which you extend or reduce. You can + * verify that these sections have not been overwritten with the + * check-blank token. + * + * \332 == 0xda, 0332, decimal 218 (i.e. dmalloc-alloc) + */ +#define ALLOC_BLANK_CHAR '\332' + +/* + * Write this character into memory when it is freed if the free-blank + * token is enabled. You can verify that these sections have not been + * overwritten with the check-blank token. + * + * \337 == 0xdf, 0337, decimal 223 (i.e. dmalloc-free) + */ +#define FREE_BLANK_CHAR '\337' + +/* + * The following information sets limits on the size of the source + * file name and line numbers returned by the __FILE__ and __LINE__ + * compiler macros. You may need to tune then to fit your environment + * although I would argue that if you have filenames longer than 40 + * characters or files longer than 10,000 lines, you are doing + * something wrong. + * + * MIN_FILE_LENGTH 3 => file "[a-zA-Z].c" + * + * Set MAX_FILE_LENGTH or MAX_LINE_NUMBER to be 0 to disable checks. + * + * NOTE: if the max is changed then the dmalloc.texi file reference to + * it will need to be changed too. + */ +#define MIN_FILE_LENGTH 3 +#define MAX_FILE_LENGTH 100 +#define MAX_LINE_NUMBER 30000 + +/* + * Maximum level in the skip list. This implies that we can only + * store 2^32 entries optimally. Needless to say this is plenty. + */ +#define MAX_SKIP_LEVEL CONFIG_MM_DMALLOC_MAX_SKIP_LEVEL + +/* + * The largest allowable allocation size. This is only for + * verification purposes to control allocations of bizarre sizes. Any + * allocation larger than this will generate a ERROR_TOO_BIG error. + * + * Set to 0 to disable the test altogether. + */ +#define LARGEST_ALLOCATION 268435456UL /* 256 mb */ + +/* + * Automatically call dmalloc_shutdown if on_exit or atexit is + * available. See conf.h for whether configure found on_exit or + * atexit calls. If neither is available, your program will have to + * call dmalloc_shutdown yourself before it exits. You can also take + * a look at atexit.c in the contrib directory which may provide this + * useful functionality for your system. + * + * NOTE: If you are having problems with the library going recursive + * (see LOCK_THREADS below if you are using pthreads), you might want + * to try setting this to 0. Because the library makes a call to + * on_exit or atexit to register itself, it may cause memory + * transactions by the system causing the dreaded recursive message. + * You may then be forced to register dmalloc_shutdown yourself via + * on_exit or atexit in main() or call dmalloc_shutdown directly + * before you exit(). + */ +#define AUTO_SHUTDOWN 0 + +/* + * The ABORT_OKAY is auto-configured but may have to be adjusted by + * forcing the USE_ABORT to be 1 or 0. On some OS's, abort calls + * fclose() which may want to free memory making the library go + * recursive when it is aborting. See ABORT_OKAY in the conf.h file + * for more information. + * + * If you need to override it or if ABORT_OKAY is 0, set KILL_PROCESS + * to the proper way to stop the program. Killing the current process + * (id 0) with SIGABRT works on a number of Unix systems. You may + * have to define some include file to get the value for the signal + * that is used. + * + * Alternatives might be: + * + * #define KILL_PROCESS { int *_int_p = 0L; *_int_p = 1; } + */ +#if ABORT_OKAY +#define USE_ABORT 1 +#else +#define USE_ABORT 0 +#endif +#if SIGNAL_OKAY +#define KILL_INCLUDE +#define KILL_PROCESS (void)kill(0, SIGABRT) +#endif + +/* + * Define the signals that are to be caught by the catch-signals + * token. When caught, these signals will cause an automatic shutdown + * of the library so that the not-freed memory and other statistics will + * be displayed. Thanks Marty. + */ +#if SIGNAL_OKAY +#define SIGNAL1 SIGHUP +#define SIGNAL2 SIGINT +#define SIGNAL3 SIGTERM +#undef SIGNAL4 +#undef SIGNAL5 +#undef SIGNAL6 +#endif + +/* + * Number of bytes to write at the top of allocations (if fence-post + * checking is enabled). A larger number means more memory space used + * up but better protection against fence overruns. See the manual + * for more information. + */ +#define FENCE_TOP_SIZE 4 + +/* + * Number of bytes to write at the bottom of allocations. See the + * FENCE_TOP_SIZE setting above or the manual for more information. + * + * WARNING: this should changed with caution and probably should only be + * increased. If you need to change it, use (ALLOCATION_ALIGNMENT * + * X) or some such. For more information see ALLOCATION_ALIGNMENT in + * conf.h. + */ +#define FENCE_BOTTOM_SIZE ALLOCATION_ALIGNMENT + +/* + * Amount of space that we are to display whenever we need to dump a + * pointer's contents to a log file or stream. This should be more + * than FENCE_BOTTOM_SIZE and FENCE_TOP_SIZE. + */ +#define DUMP_SPACE 20 + +/* + * At the front of each log message, print the output from the time() + * call as a number. This requires that the time function be defined. + */ +#define LOG_TIME_NUMBER 0 +#define TIME_INCLUDE + +/* + * Write the iteration count at the start of every log entry. This is + * handy when you are using the DMALLOC_START variable and want to + * begin the tough debugging at a certain point. This is also returned + * by the dmalloc_mark() function. + */ +#define LOG_ITERATION 1 + +/* + * Write the pid number at the start of every log entry if the + * getpid() function is available. This is handy when you are using + * dmalloc with a program which forks. See LOG_REOPEN below. + */ +#define LOG_PID 0 + +/* + * If the getpid() function is available, notice when the pid of the + * process changes and reopen the logfile. This is handy when you are + * using dmalloc with a program which forks and you want the separate + * forked programs to have separate logs. + * + * NOTE: This only works if the %p string is in the logfile name + * otherwise the log might reopen and clobber the existing log. + */ +#define LOG_REOPEN 1 + +/* + * Store the number of times a pointer is "seen" being allocated or + * freed -- it shows up as a s# (for seen) in the logfile. This is + * useful for tracking of not-freed memory. See the documents for more + * information. + * + * NOTE: This creates a certain amount of memory overhead. + */ +#define LOG_PNT_SEEN_COUNT 1 + +/* + * Store the iteration count when a pointer is allocated -- it + * shows up as a i# (for iteration) in the logfile. This is to give + * you some idea when during program execution, a pointer was + * allocated but not freed. + * + * NOTE: This creates a certain amount of memory overhead. + */ +#define LOG_PNT_ITERATION 1 + +/* + * At the front of each log message, print the output from the ctime() + * function (not including the \n). The TIME_NUMBER_TYPE is the type + * that will store the output of time() and whose address we will pass + * into ctime. This requires that the ctime and time functions both + * be defined. + */ +#if LOG_TIME_NUMBER == 0 +#define LOG_CTIME_STRING 0 +#define TIME_TYPE unsigned int +#endif + +/* + * Store the time (in seconds) or timeval (in seconds and + * microseconds) when a pointer is allocated -- it shows up as a w# + * (for when) in the logfile. This is to give you some idea when a + * pointer was allocated but not freed. The library will log the + * starting and the ending time if either of these flags is set. + * TIMEVAL_INCLUDE is the include file to define struct timeval and + * GET_TIMEVAL does the actual reading of the current time of day. + * + * WARNING: only TIME _or_ TIMEVAL can be defined at one time. + * + * NOTE: This creates a certain amount of memory overhead. + */ +#define LOG_PNT_TIME 0 +#ifndef TIME_INCLUDE +#define TIME_INCLUDE +#endif +#ifndef TIME_TYPE +#define TIME_TYPE unsigned int +#endif +#define LOG_PNT_TIMEVAL 0 +#define TIMEVAL_INCLUDE +#define TIMEVAL_TYPE struct timeval +#define GET_TIMEVAL(timeval) (void)gettimeofday(&(timeval), NULL) + +/* + * In OSF (anyone else?) you can setup __fini_* functions in each + * module which will be called automagically at shutdown of the + * program. If you enable this variable, dmalloc will shut itself + * down and log statistics when the program closes on its own. Pretty + * cool OS feature. + */ +#define FINI_DMALLOC 0 + +/* If you enable this, you probably want the AUTO_SHUTDOWN flag turned off */ +#if FINI_DMALLOC +#undef AUTO_SHUTDOWN +#define AUTO_SHUTDOWN 0 +#endif + +/* + * Keep addresses that are freed from recycling back into the used + * queue for a certain number of memory transactions. For instance, + * if this is set to 10 then after you free a pointer, it cannot be + * reused until after 10 additional calls to malloc, free, realloc, + * etc.. Define to 0 to disable. NOTE: setting to 1 does nothing. + * + * For more drastic debugging, you can enable the never-reuse flag + * which will cause the library to never reuse previously allocated + * memory. This may significantly expand the memory requirements of + * your system however. + */ +#define FREED_POINTER_DELAY CONFIG_MM_DMALLOC_FREED_POINTER_DELAY + +/* + * Maximum number of splits. This should mean that these routines can + * handle at least 2^128 different values (that's _quite_ a few). And + * then you can always increase the value. + */ +#define MAX_QSORT_SPLITS 32 + +/* + * Maximum number of entries that must be in list for it to be + * partitioned. If there are fewer elements then just do our + * insertion sort. + */ +#define MAX_QSORT_PARTITION 8 + +/* + * Max values for backtrace depth in slot. + * Note: don't enable set other value if backtrace call malloc internally + */ +#ifdef CONFIG_MM_DMALLOC_MAX_BACKTRACE_DEPTH +#define MAX_BACKTRACE_DEPTH CONFIG_MM_DMALLOC_MAX_BACKTRACE_DEPTH +#else +#define MAX_BACKTRACE_DEPTH 0 +#endif + +/* + * Max values for backtrace depth in tab. + */ +#ifdef CONFIG_MM_DMALLOC_MAX_TAB_BACKTRACE_DEPTH +#define MAX_TAB_BACKTRACE_DEPTH CONFIG_MM_DMALLOC_MAX_TAB_BACKTRACE_DEPTH +#else +#define MAX_TAB_BACKTRACE_DEPTH MAX_BACKTRACE_DEPTH +#endif + +/* + * Size of the table of file and line number memory entries. This + * memory table records the top locations by file/line or + * return-address of all pointers allocated. It also tabulates the + * freed memory pointers so you can easily locate the large memory + * leaks. See the MEMORY_TABLE_TOP_LOG value below to 0 to disable + * the table. + * + * NOTE: The table will only hold the _first_ pointers into the table. + * If you configure a size of 10 then the 11th pointer allocated will + * not be accounted for. + * + * NOTE: the library will actually allocated 2 times this many entries + * for speed reasons. + */ +#define MEMORY_TABLE_SIZE CONFIG_MM_DMALLOC_MEMORY_TABLE_SIZE + +/* + * This indicates how many of the top entries from the memory table + * you want to log by default to the log file. + * + * NOTE: to display the top entries correctly, your OS must support + * the quicksort function. + */ +#define MEMORY_TABLE_TOP_LOG CONFIG_MM_DMALLOC_MEMORY_TABLE_TOP_LOG + +/* + * Define this to 1 to only display the memory table summary of the + * dumped table pointers. The default is to display the summary as + * well as the individual pointers so the individual leaks can be + * tracked down. + */ +#define DUMP_UNFREED_SUMMARY_ONLY 0 + +/* + * If (and _only_ if) your system does not have sbrk(), you can have + * dmalloc pre-allocate its only heap space. The default heap size is + * 1mb but you can set the space to be any size. For super-small + * memory applications please understand that dmalloc is in no way + * optimized for space and so you can easily run out of memory with + * it. + * + * WARNING: this probably should only be used if HAVE_SBRK and + * HAVE_MMAP are 0. Please send me email with any problems or + * comments on this feature. + */ +#if HAVE_SBRK == 0 && HAVE_MMAP == 0 +#define INTERNAL_MEMORY_SPACE (0) +#endif + +/* + * The default smallest allowable allocations in bytes. Any blocks + * asked for that are smaller will be rounded up to this size. + */ +#define DEFAULT_SMALLEST_ALLOCATION 8 + +/****************************** thread settings ******************************/ + +/* + * The following definition allows use of the library in threaded + * programs. The most common package is MIT's pthreads so this is the + * default. Please send me mail if these definitions are configurable + * enough to work with your thread package. + */ + +#ifndef LOCK_THREADS +#define LOCK_THREADS 1 +#endif + +#if LOCK_THREADS + +/* + * Which threads library header to include when needed. It is assumed + * that the types and functions in the THREAD_TYPE and THREAD_GET_ID + * macros below are defined in this include file. In addition, the + * thread mutex init, lock, and unlock functions in malloc.c should + * also be prototyped here. + */ +#define THREAD_INCLUDE + +#include + +void umm_initsemaphore(sem_t *sem, int val); +bool umm_takesemaphore(sem_t *sem); +void umm_givesemaphore(sem_t *sem); + +#define pthread_mutex_init umm_initsemaphore +#define pthread_mutex_lock !umm_takesemaphore +#define pthread_mutex_unlock umm_givesemaphore + +/* + * As we approach the time when we start mutex locking the library, we + * need to init the mutex variable. This sets how many times before + * we start locking should we init the variable taking in account that + * the init itself might generate a call into the library. Ugh. + */ +#define THREAD_INIT_LOCK 0 + +/* + * For those threaded programs, the following settings allow the + * library to log the identity of the thread that allocated a specific + * pointer. The thread-id will show up as a ``t'' followed by a + * string identifying the thread. The LOG_THREAD_ID macro says + * whether the thread-id is logged at the front of all log messages. + * The THREAD_TYPE macro defines the variable type of the id. The + * THREAD_GET_ID macro is what function to call to get the currently + * running thread. The THREAD_ID_TO_STRING defines how the thread-id + * value is translated to the string necessary to be included with the + * ``t'' in the logfile. + */ +#define LOG_PNT_THREAD_ID 1 +#define THREAD_TYPE pid_t +#define THREAD_GET_ID() getpid() +#if HAVE_SNPRINTF +#define THREAD_ID_TO_STRING(buf, buf_size, thread_id) \ + (void)snprintf((buf), (buf_size), "%#lx", \ + (long)(thread_id)) +#else +#define THREAD_ID_TO_STRING(buf, buf_size, thread_id) \ + (void)sprintf((buf), "%#lx", (long)(thread_id)) +#endif + +#endif /* LOCK_THREADS */ + +#include + +ssize_t umm_write(int fd, FAR const void *buf, size_t nbytes); +ssize_t umm_fwrite(FAR const void *ptr, size_t size, size_t items, FAR FILE *stream); + +#define write umm_write +#define fwrite umm_fwrite + +#endif /* ! __SETTINGS_H__ */ diff --git a/mm/dmalloc/version.h b/mm/dmalloc/version.h new file mode 100644 index 00000000000..f6406de53c4 --- /dev/null +++ b/mm/dmalloc/version.h @@ -0,0 +1,5 @@ +/* this is produced by configure */ +#ifndef __VERSION_H__ +#define __VERSION_H__ +static char *dmalloc_version = "5.6.5"; +#endif /* ! __VERSION_H__ */