diff options
author | Ratakor <ratakor@disroot.org> | 2023-06-25 23:41:12 +0200 |
---|---|---|
committer | Ratakor <ratakor@disroot.org> | 2023-06-25 23:41:12 +0200 |
commit | 2c9f28013122ee242a56a188dbf339fb6f8cf3ff (patch) | |
tree | a39e4e44d1c243d2d5a90bb1aa7c9c2c320cc2df | |
parent | 62ac361d518a8a07238d1531858693c9157006b9 (diff) |
Add ignore and comment, refactor realloc
Add dalloc_ignore() to ignore a pointer in memory leak check, it can be
useful for a daemon because the program never stop.
Add dalloc_comment() to add small comment to pointers so it's easier to
figure out what they are from dalloc error message.
Remove function attributes but keep destructor for dalloc_check_all().
Internal:
Add find_pointer_index() to search for a pointer in pointers, it search
from the last to the first pointer added.
Change __realloc() to use realloc() instead of __free() and __malloc(),
this is needed to keep comment and ignored field easily, also it should
be more efficient.
-rw-r--r-- | README.md | 15 | ||||
-rw-r--r-- | dalloc.c | 125 | ||||
-rw-r--r-- | dalloc.h | 23 |
3 files changed, 127 insertions, 36 deletions
@@ -30,3 +30,18 @@ Run both dalloc_check_free() and dalloc_check_overflow() on program exit. #### dalloc_sighandler(int sig) Output signal meaning and exit. To be used with signal() from signal.h. e.g.: `signal(SIG, dalloc_sighandler);` Require `_DEFAULT_SOURCE` or equivalent. + +#### dalloc_ignore(void *p) +Ignore the pointer in argument for memory leak check. This can be useful when +developing an application that never stop. +When `DALLOC` is not defined this function does nothing. + +#### dalloc_comment(void *p, char *comment) +Add a comment to a pointer so it is more easy to know what the pointer stands +for just by looking at the error message from dalloc. +When `DALLOC` is not defined this function does nothing. + +Notes +----- +An error with "Unknown pointer" is either caused by a double free or when +freeing a pointer allocated with a function not supported by dalloc. @@ -29,11 +29,11 @@ void dalloc_check_all(void) __attribute__((destructor)); #endif /* DALLOC */ #include "dalloc.h" -#define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) #define OVER_ALLOC 64 #define MAGIC_NUMBER 0x99 #define EXIT_STATUS 9 #define MAX_POINTERS 1024 +#define COMMENT_MAX 128 #ifndef PATH_MAX #define PATH_MAX 256 #endif /* PATH_MAX */ @@ -41,11 +41,14 @@ void dalloc_check_all(void) __attribute__((destructor)); struct Pointer { void *p; size_t siz; + char comment[COMMENT_MAX]; + int ignored; char file[PATH_MAX]; int line; }; static int overflow(unsigned char *p, size_t siz); +static size_t find_pointer_index(void *p, char *file, int line); static pthread_mutex_t dalloc_mutex = PTHREAD_MUTEX_INITIALIZER; static struct Pointer pointers[MAX_POINTERS]; @@ -61,6 +64,23 @@ overflow(unsigned char *p, size_t siz) return i < OVER_ALLOC; } +static size_t +find_pointer_index(void *p, char *file, int line) +{ + ssize_t i = npointers; + + while (i-- > 0 && p != pointers[i].p); + + if (i == -1) { + fprintf(stderr, "%s:%d: dalloc: Unknown pointer %p\n", + file, line, p); + pthread_mutex_unlock(&dalloc_mutex); + exit(EXIT_STATUS); + } + + return i; +} + size_t dalloc_check_overflow(void) { @@ -81,6 +101,8 @@ dalloc_check_overflow(void) fprintf(stderr, "\n%s:%d: %p, total: %zu bytes", pointers[i].file, pointers[i].line, pointers[i].p, pointers[i].siz); + if (pointers[i].comment[0] != 0) + fprintf(stderr, " /* %s */", pointers[i].comment); } pthread_mutex_unlock(&dalloc_mutex); @@ -105,10 +127,15 @@ dalloc_check_free(void) pthread_mutex_lock(&dalloc_mutex); fprintf(stderr, "Memory allocated and not freed:"); for (i = 0; i < npointers; i++) { + if (pointers[i].ignored) + continue; + sum += pointers[i].siz; fprintf(stderr, "\n%s:%d: %p, %zu bytes", pointers[i].file, pointers[i].line, pointers[i].p, pointers[i].siz); + if (pointers[i].comment[0] != 0) + fprintf(stderr, " /* %s */", pointers[i].comment); } pthread_mutex_unlock(&dalloc_mutex); @@ -138,27 +165,55 @@ dalloc_sighandler(int sig) } void +__dalloc_ignore(void *p, char *file, int line) +{ + size_t i; + +#ifdef NO_DALLOC + return; +#endif /* NO_DALLOC */ + + pthread_mutex_lock(&dalloc_mutex); + i = find_pointer_index(p, file, line); + pointers[i].ignored = 1; + pthread_mutex_unlock(&dalloc_mutex); +} + +void +__dalloc_comment(void *p, char *comment, char *file, int line) +{ + size_t i, j; + +#ifdef NO_DALLOC + return; +#endif /* NO_DALLOC */ + + pthread_mutex_lock(&dalloc_mutex); + i = find_pointer_index(p, file, line); + for (j = 0; j < COMMENT_MAX && comment[j] != '\0'; j++) + pointers[i].comment[j] = comment[j]; + pointers[i].comment[j] = '\0'; + pthread_mutex_unlock(&dalloc_mutex); +} + +void __free(void *p, char *file, int line) { - size_t i = 0; + size_t i; if (p == NULL) return; pthread_mutex_lock(&dalloc_mutex); - while (p != pointers[i].p && ++i < npointers); - if (i == npointers) { - fprintf(stderr, "%s:%d: dalloc: Double free\n", file, line); - pthread_mutex_unlock(&dalloc_mutex); - exit(EXIT_STATUS); - } - + i = find_pointer_index(p, file, line); if (overflow(pointers[i].p, pointers[i].siz)) { fprintf(stderr, "%s:%d: dalloc: " "Memory overflow on %p, total: %zu bytes\n", file, line, pointers[i].p, pointers[i].siz); - fprintf(stderr, - "The pointer was allocated in '%s' on line %d\n", + fprintf(stderr, "The pointer "); + if (pointers[i].comment[0] != 0) + fprintf(stderr, "'%s' ", pointers[i].comment); + fprintf(stderr, "was allocated in '%s' on line %d.\n", pointers[i].file, pointers[i].line); pthread_mutex_unlock(&dalloc_mutex); exit(EXIT_STATUS); @@ -236,9 +291,7 @@ __calloc(size_t nmemb, size_t siz, char *file, int line) void * __realloc(void *p, size_t siz, char *file, int line) { - - void *np; - size_t i = 0, osiz; + size_t i, j; if (p == NULL) return __malloc(siz, file, line); @@ -249,21 +302,45 @@ __realloc(void *p, size_t siz, char *file, int line) } pthread_mutex_lock(&dalloc_mutex); - while (p != pointers[i].p && ++i < npointers); - if (i == npointers) { - fprintf(stderr, "%s:%d: dalloc: realloc: Unknown pointer %p\n", - file, line, p); + i = find_pointer_index(p, file, line); + + if (overflow(pointers[i].p, pointers[i].siz)) { + fprintf(stderr, "%s:%d: dalloc: " + "Memory overflow on %p, total: %zu bytes\n", + file, line, pointers[i].p, pointers[i].siz); + fprintf(stderr, "The pointer "); + if (pointers[i].comment[0] != 0) + fprintf(stderr, "'%s' ", pointers[i].comment); + fprintf(stderr, "was allocated in '%s' on line %d.\n", + pointers[i].file, pointers[i].line); pthread_mutex_unlock(&dalloc_mutex); exit(EXIT_STATUS); } - osiz = pointers[i].siz; - pthread_mutex_unlock(&dalloc_mutex); - np = __malloc(siz, file, line); - memcpy(np, p, MIN(osiz, siz)); - __free(p, file, line); + if (siz + OVER_ALLOC < OVER_ALLOC) { + p = NULL; + errno = ENOMEM; + } else { + p = realloc(p, siz + OVER_ALLOC); + } - return np; + if (p == NULL) { + fprintf(stderr, "%s:%d: dalloc: %s\n", + file, line, strerror(errno)); + pthread_mutex_unlock(&dalloc_mutex); + exit(EXIT_STATUS); + } + + memset((unsigned char *)p + siz, MAGIC_NUMBER, OVER_ALLOC); + pointers[i].p = p; + pointers[i].siz = siz; + for (j = 0; j < PATH_MAX - 1 && file[j] != '\0'; j++) + pointers[i].file[j] = file[j]; + pointers[i].file[j] = '\0'; + pointers[i].line = line; + pthread_mutex_unlock(&dalloc_mutex); + + return p; } void * @@ -29,24 +29,23 @@ extern "C" { #define exit(dummy) (exitsegv(dummy)) #endif /* EXITSEGV */ +#define dalloc_ignore(p) (__dalloc_ignore(p, __FILE__, __LINE__)) +#define dalloc_comment(p, com) (__dalloc_comment(p, com, __FILE__, __LINE__)) + size_t dalloc_check_overflow(void); void dalloc_check_free(void); void dalloc_check_all(void); void dalloc_sighandler(int sig); +void __dalloc_ignore(void *p, char *file, int line); +void __dalloc_comment(void *p, char *comment, char *file, int line); void __free(void *p, char *file, int line); -void *__malloc(size_t siz, char *file, int line) - __attribute_warn_unused_result__; -void *__calloc(size_t nmemb, size_t siz, char *file, int line) - __attribute_warn_unused_result__; -void *__realloc(void *p, size_t siz, char *file, int line) - __attribute_warn_unused_result__; -void *__reallocarray(void *p, size_t nmemb, size_t siz, char *file, int line) - __attribute_warn_unused_result__; -char *__strdup(const char *s, char *file, int line) - __attribute_warn_unused_result__ __nonnull ((1)); -char *__strndup(const char *s, size_t n, char *file, int line) - __attribute_warn_unused_result__ __nonnull ((1)); +void *__malloc(size_t siz, char *file, int line); +void *__calloc(size_t nmemb, size_t siz, char *file, int line); +void *__realloc(void *p, size_t siz, char *file, int line); +void *__reallocarray(void *p, size_t nmemb, size_t siz, char *file, int line); +char *__strdup(const char *s, char *file, int line); +char *__strndup(const char *s, size_t n, char *file, int line); void exitsegv(int dummy); |