1
0
mirror of https://gitlab.xiph.org/xiph/ezstream.git synced 2024-12-04 14:46:31 -05:00
ezstream/src/xalloc.c

718 lines
18 KiB
C
Raw Normal View History

/*
* Copyright (C) 2007 Moritz Grimm <mgrimm@gmx.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "xalloc.h"
#ifndef SIZE_T_MAX
# define SIZE_T_MAX ((size_t)-1)
#endif
#ifndef va_copy
# define va_copy(d, s) (d) = (s)
#endif
#define XALLOC_DBGLVL_MAX 2
#if defined(XALLOC_DEBUG) && defined(XALLOC_SILENT)
# undef XALLOC_SILENT
#endif /* XALLOC_DEBUG && XALLOC_SILENT */
#ifdef THREAD_SAFE
# include <pthread.h>
static pthread_mutex_t xalloc_mutex;
static pthread_mutex_t strerror_mutex;
# define XALLOC_LOCK(mtx) do { \
int error; \
if ((error = pthread_mutex_lock(&mtx)) != 0) \
_xalloc_error(error, "XALLOC: Internal error in %s:%u: pthread_mutex_lock()", \
__FILE__, __LINE__); \
} while (0)
# define XALLOC_UNLOCK(mtx) do { \
int error; \
if ((error = pthread_mutex_unlock(&mtx)) != 0) \
_xalloc_error(error, "XALLOC: Internal error in %s:%u: pthread_mutex_unlock()", \
__FILE__, __LINE__); \
} while (0)
#else
# define XALLOC_LOCK(mtx) ((void)0)
# define XALLOC_UNLOCK(mtx) ((void)0)
#endif /* THREAD_SAFE */
#ifdef XALLOC_DEBUG
# include <sys/tree.h>
int _memory_cmp(void *, void *);
struct memory {
RB_ENTRY(memory) entry;
void *ptr;
size_t size;
const char *allocated_by;
unsigned int allocated_in_line;
const char *reallocated_by;
unsigned int reallocated_in_line;
const char *freed_by;
unsigned int freed_in_line;
};
RB_HEAD(memory_tree, memory) memory_tree_head = RB_INITIALIZER(&memory_tree_head);
RB_PROTOTYPE(memory_tree, memory, entry, _memory_cmp)
void _memory_free(struct memory **);
#endif /* XALLOC_DEBUG */
void _xalloc_warn(const char *, ...);
void _xalloc_error(int, const char *, ...);
void _xalloc_fatal(const char *, ...);
void _xalloc_debug_printf(unsigned int, const char *, ...);
#ifdef XALLOC_WITH_XASPRINTF
int _xalloc_vasprintf(char **, const char *, va_list, size_t *);
#endif /* XALLOC_WITH_XASPRINTF */
static unsigned int debug_level = 0;
static FILE *debug_output = NULL;
static int xalloc_initialized = 0;
static size_t xalloc_allocated;
static size_t xalloc_total;
static size_t xalloc_peak;
static size_t xalloc_freed;
static void * (*real_malloc)(size_t) = NULL;
static void * (*real_calloc)(size_t, size_t) = NULL;
static void * (*real_realloc)(void *, size_t) = NULL;
static const char *unknown_file = "<unknown>";
#ifdef XALLOC_DEBUG
RB_GENERATE(memory_tree, memory, entry, _memory_cmp)
int
_memory_cmp(void *arg_a, void *arg_b)
{
struct memory *a = (struct memory *)arg_a;
struct memory *b = (struct memory *)arg_b;
if (a->ptr < b->ptr)
return (-1);
else if (a->ptr > b->ptr)
return (1);
return (0);
}
void
_memory_free(struct memory **mem_p)
{
struct memory *mem = *mem_p;
if (mem->allocated_by != NULL)
mem->allocated_by = NULL;
if (mem->reallocated_by != NULL)
mem->reallocated_by = NULL;
if (mem->freed_by != NULL)
mem->freed_by = NULL;
free(mem);
*mem_p = NULL;
}
#endif /* XALLOC_DEBUG */
void
_xalloc_warn(const char *fmt, ...)
{
va_list ap;
if (debug_output == NULL)
debug_output = XALLOC_DEFAULT_OUTPUT;
va_start(ap, fmt);
#ifndef XALLOC_SILENT
vfprintf(debug_output, fmt, ap);
fflush(debug_output);
#endif /* !XALLOC_SILENT */
va_end(ap);
}
void
_xalloc_error(int errnum, const char *fmt, ...)
{
va_list ap;
if (debug_output == NULL)
debug_output = XALLOC_DEFAULT_OUTPUT;
va_start(ap, fmt);
#ifndef XALLOC_SILENT
vfprintf(debug_output, fmt, ap);
if (errnum > 0) {
if (xalloc_initialized)
XALLOC_LOCK(strerror_mutex);
vfprintf(debug_output, ": %s\n", strerror(errnum));
if (xalloc_initialized)
XALLOC_UNLOCK(strerror_mutex);
}
fflush(debug_output);
#endif /* !XALLOC_SILENT */
va_end(ap);
exit(1);
}
void
_xalloc_fatal(const char *fmt, ...)
{
va_list ap;
if (debug_output == NULL)
debug_output = XALLOC_DEFAULT_OUTPUT;
va_start(ap, fmt);
#ifndef XALLOC_SILENT
vfprintf(debug_output, fmt, ap);
fflush(debug_output);
#endif /* !XALLOC_SILENT */
va_end(ap);
abort();
}
void
_xalloc_debug_printf(unsigned int level, const char *fmt, ...)
{
va_list ap;
if (level > debug_level)
return;
va_start(ap, fmt);
#ifdef XALLOC_DEBUG
vfprintf(debug_output, fmt, ap);
fflush(debug_output);
#endif /* XALLOC_DEBUG */
va_end(ap);
}
#ifdef XALLOC_WITH_XASPRINTF
int
_xalloc_vasprintf(char **str_p, const char *fmt, va_list ap, size_t *strsiz)
{
int ret = -1;
va_list ap_local;
*str_p = NULL;
#ifndef WIN32
# ifndef HAVE_BROKEN_VSNPRINTF
/* MODERN UNIX */
va_copy(ap_local, ap);
*strsiz = vsnprintf(NULL, (size_t)0, fmt, ap_local) + 1;
va_end(ap_local);
# ifdef HAVE_ASPRINTF
if ((ret = vasprintf(str_p, fmt, ap)) == -1)
*str_p = NULL;
# else
if ((*str_p = real_calloc(*strsiz, sizeof(char))) == NULL)
return (-1);
ret = vsnprintf(*str_p, *strsiz, fmt, ap);
# endif /* HAVE_ASPRINTF */
# else
/* ANCIENT UNIX */
{
char *buf = NULL;
*strsiz = 4;
for (;;) {
char *tbuf;
int pret;
if ((tbuf = real_realloc(buf, *strsiz)) == NULL) {
free(buf);
return (-1);
}
buf = tbuf;
va_copy(ap_local, ap);
pret = vsnprintf(buf, *strsiz, fmt, ap_local);
va_end(ap_local);
if (pret > 0 && pret < (int)*strsiz)
break;
if ((int)(*strsiz *= 2) < 0) {
free(buf);
return (-1);
}
}
ret = vsnprintf(buf, *strsiz, fmt, ap);
*str_p = buf;
}
# endif /* !HAVE_BROKEN_VSNPRINTF */
#else
/* WINDOWS */
va_copy(ap_local, ap);
*strsiz = _vscprintf(fmt, ap_local) + 1;
va_end(ap_local);
if ((*str_p = real_calloc(*strsiz, sizeof(char))) == NULL)
return (-1);
ret = _vsnprintf(*str_p, *strsiz, fmt, ap);
#endif /* !WIN32 */
return (ret);
}
#endif /* XALLOC_WITH_XASPRINTF */
void
xalloc_initialize_debug(unsigned int level, FILE *output)
{
#ifdef THREAD_SAFE
int err;
#endif /* THREAD_SAFE */
if (xalloc_initialized)
_xalloc_fatal("XALLOC: xalloc_initialize(): Xalloc library already initialized\n");
if ((debug_level = level) > XALLOC_DBGLVL_MAX)
debug_level = XALLOC_DBGLVL_MAX;
if (output == NULL)
debug_output = XALLOC_DEFAULT_OUTPUT;
else
debug_output = output;
real_malloc = malloc;
real_calloc = calloc;
real_realloc = realloc;
xalloc_allocated = 0;
xalloc_total = 0;
xalloc_peak = 0;
xalloc_freed = 0;
#ifdef THREAD_SAFE
if ((err = pthread_mutex_init(&strerror_mutex, NULL)) != 0)
_xalloc_error(err, "XALLOC: xalloc_initialize(): Initializing xalloc_mutex");
if ((err = pthread_mutex_init(&xalloc_mutex, NULL)) != 0)
_xalloc_error(err, "XALLOC: xalloc_initialize(): Initializing strerror_mutex");
#endif /* THREAD_SAFE */
xalloc_initialized = 1;
}
void
xalloc_set_functions(void *(*malloc_func)(size_t),
void *(*calloc_func)(size_t, size_t),
void *(*realloc_func)(void *, size_t))
{
if (!xalloc_initialized)
_xalloc_fatal("XALLOC: xalloc_set_functions(): Xalloc library not initialized\n");
if (malloc_func == NULL ||
calloc_func == NULL ||
realloc_func == NULL)
_xalloc_fatal("XALLOC: xalloc_set_functions(): Bad argument(s)\n");
XALLOC_LOCK(xalloc_mutex);
real_malloc = malloc_func;
real_calloc = calloc_func;
real_realloc = realloc_func;
XALLOC_UNLOCK(xalloc_mutex);
}
void
xalloc_shutdown(void)
{
if (!xalloc_initialized)
_xalloc_fatal("XALLOC: xalloc_shutdown(): Xalloc library not initialized\n");
#ifdef XALLOC_DEBUG
if (debug_level > 0) {
struct memory *mem, *mem_next;
size_t leaked_bytes = 0;
XALLOC_LOCK(xalloc_mutex);
for (mem = RB_MIN(memory_tree, &memory_tree_head);
mem != NULL;
mem = mem_next) {
mem_next = RB_NEXT(memory_tree, &memory_tree_head, mem);
RB_REMOVE(memory_tree, &memory_tree_head, mem);
if (mem->freed_by == NULL) {
_xalloc_debug_printf(1, "XALLOC: MEMORY LEAK (%p): allocated in %s:%u, ",
mem->ptr,
mem->allocated_by,
mem->allocated_in_line);
if (mem->reallocated_by != NULL)
_xalloc_debug_printf(1, "last reallocated in %s:%u, ",
mem->reallocated_by,
mem->reallocated_in_line);
_xalloc_debug_printf(1, "leaks %lu bytes\n",
(unsigned long)mem->size);
leaked_bytes += mem->size;
free(mem->ptr);
}
_memory_free(&mem);
}
if (leaked_bytes != xalloc_allocated)
_xalloc_fatal("XALLOC: Internal error: xalloc_shutdown(): leaked_bytes(%lu) != xalloc_allocated(%lu)\n",
(unsigned long)leaked_bytes,
(unsigned long)xalloc_allocated);
_xalloc_debug_printf(1, "XALLOC: STATS: leaked: %lu bytes, peak allocation: %lu bytes (freed/total: %lu/%lu bytes)\n",
(unsigned long)xalloc_allocated,
(unsigned long)xalloc_peak,
(unsigned long)xalloc_freed,
(unsigned long)xalloc_total);
XALLOC_UNLOCK(xalloc_mutex);
}
#endif /* XALLOC_DEBUG */
#ifdef THREAD_SAFE
if (pthread_mutex_destroy(&xalloc_mutex) != 0)
_xalloc_fatal("XALLOC: Internal error: xalloc_shutdown(): xalloc_mutex %p cannot be destroyed\n",
xalloc_mutex);
if (pthread_mutex_destroy(&strerror_mutex) != 0)
_xalloc_fatal("XALLOC: Internal error: xalloc_shutdown(): strerror_mutex %p cannot be destroyed\n",
strerror_mutex);
#endif /* THREAD_SAFE */
xalloc_initialized = 0;
}
void *
xmalloc_c(size_t size, const char *file, unsigned int line)
{
void *ret;
if (!xalloc_initialized)
_xalloc_fatal("XALLOC: xmalloc(): Xalloc library not initialized\n");
if (size == 0)
_xalloc_fatal("XALLOC: xmalloc(): %s:%u: Zero size\n",
file ? file : unknown_file, line);
if ((ret = real_malloc(size)) == NULL)
_xalloc_error(errno, "XALLOC: xmalloc(): %s:%u: Allocating %lu bytes",
file ? file : unknown_file, line,
(unsigned long)(size));
#ifdef XALLOC_DEBUG
if (debug_level > 0) {
struct memory *mem;
if ((mem = real_calloc(1, sizeof(struct memory))) == NULL)
_xalloc_error(errno, "XALLOC: Internal error");
mem->ptr = ret;
mem->size = size;
if (file)
mem->allocated_by = file;
else
mem->allocated_by = unknown_file;
mem->allocated_in_line = line;
XALLOC_LOCK(xalloc_mutex);
RB_INSERT(memory_tree, &memory_tree_head, mem);
xalloc_allocated += size;
xalloc_total += size;
if (xalloc_allocated > xalloc_peak)
xalloc_peak = xalloc_allocated;
XALLOC_UNLOCK(xalloc_mutex);
}
#endif /* XALLOC_DEBUG */
return (ret);
}
void *
xcalloc_c(size_t nmemb, size_t size, int may_fail,
const char *file, unsigned int line)
{
void *ret;
if (!xalloc_initialized)
_xalloc_fatal("XALLOC: xcalloc(): Xalloc library not initialized\n");
if (nmemb == 0 || size == 0)
_xalloc_fatal("XALLOC: xcalloc(): %s:%u: Zero size\n",
file ? file : unknown_file, line);
if (SIZE_T_MAX / nmemb < size)
_xalloc_fatal("XALLOC: xcalloc(): %s:%u: Integer overflow (nmemb * size > SIZE_T_MAX)\n",
file ? file : unknown_file, line);
if ((ret = real_calloc(nmemb, size)) == NULL && may_fail == 0)
_xalloc_error(errno, "XALLOC: xcalloc(): %s:%u: Allocating %lu bytes",
file ? file : unknown_file, line,
(unsigned long)(nmemb * size));
#ifdef XALLOC_DEBUG
if (ret != NULL && debug_level > 0) {
struct memory *mem;
if ((mem = real_calloc(1, sizeof(struct memory))) == NULL)
_xalloc_error(errno, "XALLOC: Internal error");
mem->ptr = ret;
mem->size = nmemb * size;
if (file)
mem->allocated_by = file;
else
mem->allocated_by = unknown_file;
mem->allocated_in_line = line;
XALLOC_LOCK(xalloc_mutex);
RB_INSERT(memory_tree, &memory_tree_head, mem);
xalloc_allocated += nmemb * size;
xalloc_total += nmemb * size;
if (xalloc_allocated > xalloc_peak)
xalloc_peak = xalloc_allocated;
XALLOC_UNLOCK(xalloc_mutex);
}
#endif /* XALLOC_DEBUG */
return (ret);
}
void *
xrealloc_c(void *ptr, size_t nmemb, size_t size,
const char *file, unsigned int line)
{
void *ret;
size_t nsiz = nmemb * size;
#ifdef XALLOC_DEBUG
struct memory *mem = NULL;
#endif /* XALLOC_DEBUG */
if (!xalloc_initialized)
_xalloc_fatal("XALLOC: xrealloc(): Xalloc library not initialized\n");
if (nmemb == 0 || size == 0)
_xalloc_fatal("XALLOC: xrealloc(): %s:%u: Zero size\n",
file ? file : unknown_file, line);
if (SIZE_T_MAX / nmemb < size)
_xalloc_fatal("XALLOC: xrealloc(): %s:%u: Integer overflow (nmemb * size > SIZE_T_MAX)\n",
file ? file : unknown_file, line);
if (ptr == NULL) {
ret = real_malloc(nsiz);
#ifdef XALLOC_DEBUG
if (debug_level > 0) {
if ((mem = real_calloc(1, sizeof(struct memory))) == NULL)
_xalloc_error(errno, "XALLOC: Internal error");
mem->ptr = ret;
if (file)
mem->allocated_by = file;
else
mem->allocated_by = unknown_file;
mem->allocated_in_line = line;
}
#endif /* XALLOC_DEBUG */
} else {
#ifdef XALLOC_DEBUG
struct memory find_mem;
XALLOC_LOCK(xalloc_mutex);
if (debug_level > 0) {
find_mem.ptr = ptr;
if ((mem = RB_FIND(memory_tree, &memory_tree_head, &find_mem)) == NULL)
_xalloc_fatal("XALLOC: xrealloc(): %s:%u: Junk pointer %p not accounted for\n",
file ? file : unknown_file,
line, ptr);
RB_REMOVE(memory_tree, &memory_tree_head, mem);
}
XALLOC_UNLOCK(xalloc_mutex);
#endif /* XALLOC_DEBUG */
ret = real_realloc(ptr, nsiz);
#ifdef XALLOC_DEBUG
if (debug_level > 0) {
mem->ptr = ret;
if (file)
mem->reallocated_by = file;
else
mem->reallocated_by = unknown_file;
mem->reallocated_in_line = line;
}
#endif /* XALLOC_DEBUG */
}
if (ret == NULL)
_xalloc_error(errno, "XALLOC: xrealloc(): %s:%u: (Re)allocating %lu bytes",
file ? file : unknown_file, line,
(unsigned long)(nmemb * size));
#ifdef XALLOC_DEBUG
if (debug_level > 0) {
ssize_t diff = nsiz - mem->size;
XALLOC_LOCK(xalloc_mutex);
xalloc_allocated += diff;
if (diff < 0)
xalloc_freed += -diff;
else
xalloc_total += diff;
if (xalloc_allocated > xalloc_peak)
xalloc_peak = xalloc_allocated;
mem->size = nsiz;
RB_INSERT(memory_tree, &memory_tree_head, mem);
XALLOC_UNLOCK(xalloc_mutex);
}
#endif /* XALLOC_DEBUG */
return (ret);
}
char *
xstrdup_c(const char *str, const char *file, unsigned int line)
{
size_t len;
char *nstr;
if (!xalloc_initialized)
_xalloc_fatal("XALLOC: xstrdup(): Xalloc library not initialized\n");
len = strlen(str) + 1;
if ((nstr = xcalloc_c(len, sizeof(char), 0, file, line)) == NULL)
_xalloc_error(errno, "XALLOC: xstrdup(): %s:%u: Allocating %lu bytes: %s\n",
file ? file : unknown_file, line,
(unsigned long)(len));
memcpy(nstr, str, len);
return (nstr);
}
void
xfree_c(void **ptr_p, const char *file, unsigned int line)
{
if (!xalloc_initialized)
_xalloc_fatal("XALLOC: xfree(): Xalloc library not initialized\n");
if (ptr_p == NULL)
_xalloc_fatal("XALLOC: xfree(): Bad argument(s)\n");
if (*ptr_p == NULL) {
_xalloc_warn("XALLOC: xfree(): Warning: %s:%u: Freeing NULL pointer\n",
file ? file : unknown_file, line);
return;
}
#ifdef XALLOC_DEBUG
if (debug_level > 0) {
struct memory *mem = NULL, find_mem;
XALLOC_LOCK(xalloc_mutex);
find_mem.ptr = *ptr_p;
if ((mem = RB_FIND(memory_tree, &memory_tree_head, &find_mem)) == NULL)
_xalloc_fatal("XALLOC: xfree(): %s:%u: Junk pointer %p not accounted for\n",
file ? file : unknown_file, line,
*ptr_p);
if (mem->freed_by != NULL) {
_xalloc_debug_printf(2, "XALLOC: DOUBLE FREE in %s:%u: allocated in %s:%u, ",
file ? file : unknown_file, line,
mem->allocated_by,
mem->allocated_in_line);
if (mem->reallocated_by != NULL)
_xalloc_debug_printf(2, "last reallocated in %s:%u, ",
mem->reallocated_by,
mem->reallocated_in_line);
_xalloc_debug_printf(2, "already freed in %s:%u\n",
mem->freed_by,
mem->freed_in_line);
abort();
}
xalloc_freed += mem->size;
xalloc_allocated -= mem->size;
mem->size = 0;
if (debug_level > 1) {
if (file)
mem->freed_by = file;
else
mem->freed_by = unknown_file;
mem->freed_in_line = line;
} else {
RB_REMOVE(memory_tree, &memory_tree_head, mem);
_memory_free(&mem);
}
XALLOC_UNLOCK(xalloc_mutex);
}
#endif /* XALLOC_DEBUG */
free(*ptr_p);
#ifdef XALLOC_DEBUG
if (debug_level <= 1)
#endif /* XALLOC_DEBUG */
{
*ptr_p = NULL;
}
}
#ifdef XALLOC_WITH_XASPRINTF
int
xasprintf_c(const char *file, unsigned int line,
char **str_p, const char *fmt, ...)
{
int ret;
va_list ap;
size_t strsiz = 0;
if (!xalloc_initialized)
_xalloc_fatal("XALLOC: xasprintf(): Xalloc library not initialized\n");
if (str_p == NULL || fmt == NULL)
_xalloc_fatal("XALLOC: xasprintf(): Bad argument(s)\n");
va_start(ap, fmt);
ret = _xalloc_vasprintf(str_p, fmt, ap, &strsiz);
va_end(ap);
if (ret == -1)
_xalloc_error(errno, "XALLOC: xasprintf(): %s:%u: Allocating %lu bytes",
file ? file : unknown_file, line, strsiz);
# ifdef XALLOC_DEBUG
if (debug_level > 0) {
struct memory *mem;
if ((mem = real_calloc(1, sizeof(struct memory))) == NULL)
_xalloc_error(errno, "XALLOC: Internal error");
mem->ptr = *str_p;
mem->size = strsiz;
if (file)
mem->allocated_by = file;
else
mem->allocated_by = unknown_file;
mem->allocated_in_line = line;
XALLOC_LOCK(xalloc_mutex);
RB_INSERT(memory_tree, &memory_tree_head, mem);
xalloc_allocated += strsiz;
xalloc_total += strsiz;
if (xalloc_allocated > xalloc_peak)
xalloc_peak = xalloc_allocated;
XALLOC_UNLOCK(xalloc_mutex);
}
# endif /* XALLOC_DEBUG */
return (ret);
}
#endif /* XALLOC_WITH_XASPRINTF */