323 lines
5.7 KiB
C
323 lines
5.7 KiB
C
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif /* HAVE_CONFIG_H */
|
|
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include <tcc.h>
|
|
#include "cc/cc.h"
|
|
|
|
#ifdef TCC_MEMORY_DEBUG
|
|
# undef tcc_free
|
|
# undef tcc_malloc
|
|
# undef tcc_mallocz
|
|
# undef tcc_realloc
|
|
# undef tcc_strdup
|
|
#endif /* TCC_MEMORY_DEBUG */
|
|
|
|
static void *
|
|
default_reallocator(void *ptr, size_t size)
|
|
{
|
|
void *ptr1;
|
|
|
|
if (size == 0)
|
|
{
|
|
free(ptr);
|
|
return (NULL);
|
|
}
|
|
|
|
ptr1 = realloc(ptr, size);
|
|
if (ptr1 == NULL)
|
|
{
|
|
fprintf(stderr, "memory full\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
return (ptr1);
|
|
}
|
|
|
|
static TCCReallocFunc *reallocator = default_reallocator;
|
|
|
|
void
|
|
tcc_set_realloc(TCCReallocFunc *my_realloc)
|
|
{
|
|
if (my_realloc == NULL)
|
|
{
|
|
reallocator = default_reallocator;
|
|
return;
|
|
}
|
|
|
|
reallocator = my_realloc;
|
|
}
|
|
|
|
void
|
|
tcc_free(void *ptr)
|
|
{
|
|
reallocator(ptr, 0);
|
|
}
|
|
|
|
void *
|
|
tcc_malloc(size_t size)
|
|
{
|
|
return (reallocator(NULL, size));
|
|
}
|
|
|
|
void *
|
|
tcc_mallocz(size_t size)
|
|
{
|
|
void *ptr;
|
|
|
|
ptr = tcc_malloc(size);
|
|
if (ptr == NULL)
|
|
{
|
|
return (NULL);
|
|
}
|
|
memset(ptr, 0, size);
|
|
|
|
return (ptr);
|
|
}
|
|
|
|
void *
|
|
tcc_realloc(void *ptr, size_t size)
|
|
{
|
|
return (reallocator(ptr, size));
|
|
}
|
|
|
|
char *
|
|
tcc_strdup(const char *str)
|
|
{
|
|
char *ptr;
|
|
|
|
ptr = tcc_malloc(strlen(str) + 1);
|
|
strcpy(ptr, str);
|
|
|
|
return (ptr);
|
|
}
|
|
|
|
#ifdef TCC_MEMORY_DEBUG
|
|
|
|
# define MEMORY_DEBUG_MAGIC1 0xFEEDDEB1
|
|
# define MEMORY_DEBUG_MAGIC2 0xFEEDDEB2
|
|
# define MEMORY_DEBUG_MAGIC3 0xFEEDDEB3
|
|
|
|
# define MEMORY_DEBUG_FILE_LEN 40
|
|
|
|
# define MEMORY_DEBUG_CHECK3(hdr) \
|
|
((MemoryDebugHeader *)((uint8_t *)hdr + header->size))->magic3
|
|
|
|
# define MEMORY_USER_PTR(hdr) \
|
|
((uint8_t *)header + offsetof(MemoryDebugHeader, magic3))
|
|
|
|
# define MEMORY_HEADER_PTR(ptr) \
|
|
(MemoryDebugHeader *)((uint8_t *)ptr - offsetof(MemoryDebugHeader, magic3))
|
|
|
|
typedef struct MemoryDebugHeader {
|
|
uint32_t magic1;
|
|
size_t size;
|
|
struct MemoryDebugHeader *prev;
|
|
struct MemoryDebugHeader *next;
|
|
int linenum;
|
|
char filename[MEMORY_DEBUG_FILE_LEN + 1];
|
|
uint32_t magic2;
|
|
__attribute__((aligned(16))) uint8_t magic3[4];
|
|
} MemoryDebugHeader;
|
|
|
|
static MemoryDebugHeader *mem_debug_chain;
|
|
static size_t mem_cur_size;
|
|
size_t mem_max_size;
|
|
static int nb_states;
|
|
|
|
static MemoryDebugHeader *
|
|
malloc_check(void *ptr, const char *msg)
|
|
{
|
|
MemoryDebugHeader *header;
|
|
|
|
header = MEMORY_HEADER_PTR(ptr);
|
|
if (header->magic1 != MEMORY_DEBUG_MAGIC1
|
|
|| header->magic2 != MEMORY_DEBUG_MAGIC2
|
|
|| read32le(MEMORY_DEBUG_CHECK3(header)) != MEMORY_DEBUG_MAGIC3
|
|
|| header->size == (size_t)-1)
|
|
{
|
|
fprintf(stderr, "%s check failed\n", msg);
|
|
if (header->magic1 == MEMORY_DEBUG_MAGIC1)
|
|
{
|
|
fprintf(stderr, "%s:%u: block allocated here.\n",
|
|
header->filename, header->linenum);
|
|
}
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
return (header);
|
|
}
|
|
|
|
void
|
|
tcc_free_debug(void *ptr)
|
|
{
|
|
MemoryDebugHeader *header;
|
|
|
|
if (ptr == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
header = malloc_check(ptr, "tcc_free");
|
|
|
|
mem_cur_size -= header->size;
|
|
header->size = (size_t)-1;
|
|
if (header->next)
|
|
{
|
|
header->next->prev = header->prev;
|
|
}
|
|
if (header->prev)
|
|
{
|
|
header->prev->next = header->next;
|
|
}
|
|
if (header == mem_debug_chain)
|
|
{
|
|
mem_debug_chain = header->next;
|
|
}
|
|
|
|
tcc_free(header);
|
|
}
|
|
|
|
void *
|
|
tcc_malloc_debug(size_t size, const char *file, int line)
|
|
{
|
|
int ofs;
|
|
MemoryDebugHeader *header;
|
|
|
|
if (size == 0)
|
|
{
|
|
return (NULL);
|
|
}
|
|
header = tcc_malloc(sizeof(MemoryDebugHeader) + size);
|
|
header->magic1 = MEMORY_DEBUG_MAGIC1;
|
|
header->magic2 = MEMORY_DEBUG_MAGIC2;
|
|
header->size = size;
|
|
write32le(MEMORY_DEBUG_CHECK3(header), MEMORY_DEBUG_MAGIC3);
|
|
header->linenum = line;
|
|
ofs = strlen(file) - MEMORY_DEBUG_FILE_LEN;
|
|
strncpy(header->filename, file + (ofs > 0 ? ofs : 0), MEMORY_DEBUG_FILE_LEN);
|
|
header->filename[MEMORY_DEBUG_FILE_LEN] = 0;
|
|
|
|
header->next = mem_debug_chain;
|
|
header->prev = NULL;
|
|
if (header->next)
|
|
{
|
|
header->next->prev = header;
|
|
}
|
|
mem_debug_chain = header;
|
|
mem_cur_size += size;
|
|
if (mem_cur_size > mem_max_size)
|
|
{
|
|
mem_max_size = mem_cur_size;
|
|
}
|
|
|
|
return (MEMORY_USER_PTR(header));
|
|
}
|
|
|
|
void *
|
|
tcc_mallocz_debug(size_t size, const char *file, int line)
|
|
{
|
|
void *ptr;
|
|
|
|
ptr = tcc_malloc_debug(size, file, line);
|
|
if (ptr == NULL)
|
|
{
|
|
return (NULL);
|
|
}
|
|
|
|
memset(ptr, 0, size);
|
|
|
|
return (ptr);
|
|
}
|
|
|
|
void *
|
|
tcc_realloc_debug(void *ptr, size_t size, const char *file, int line)
|
|
{
|
|
MemoryDebugHeader *header;
|
|
int mem_debug_chain_update;
|
|
|
|
mem_debug_chain_update = 0;
|
|
|
|
if (ptr == NULL)
|
|
{
|
|
return (tcc_malloc_debug(size, file, line));
|
|
}
|
|
if (size == 0)
|
|
{
|
|
tcc_free_debug(ptr);
|
|
return (NULL);
|
|
}
|
|
|
|
header = malloc_check(ptr, "tcc_realloc");
|
|
|
|
mem_cur_size -= header->size;
|
|
mem_debug_chain_update = (header == mem_debug_chain);
|
|
header = tcc_realloc(header, sizeof(MemoryDebugHeader) + size);
|
|
header->size = size;
|
|
write32le(MEMORY_DEBUG_CHECK3(header), MEMORY_DEBUG_MAGIC3);
|
|
if (header->next)
|
|
{
|
|
header->next->prev = header;
|
|
}
|
|
if (header->prev)
|
|
{
|
|
header->prev->next = header;
|
|
}
|
|
if (mem_debug_chain_update)
|
|
{
|
|
mem_debug_chain = header;
|
|
}
|
|
mem_cur_size += size;
|
|
if (mem_cur_size > mem_max_size)
|
|
{
|
|
mem_max_size = mem_cur_size;
|
|
}
|
|
|
|
return (MEMORY_USER_PTR(header));
|
|
}
|
|
|
|
char *
|
|
tcc_strdup_debug(const char *str, const char *file, int line)
|
|
{
|
|
char *ptr;
|
|
|
|
ptr = tcc_malloc_debug(strlen(str) + 1, file, line);
|
|
strcpy(ptr, str);
|
|
|
|
return (ptr);
|
|
}
|
|
|
|
void
|
|
tcc_memcheck(int d)
|
|
{
|
|
MemoryDebugHeader *header;
|
|
|
|
nb_states += d;
|
|
if (0 == nb_states && mem_cur_size)
|
|
{
|
|
header = mem_debug_chain;
|
|
fflush(stdout);
|
|
fprintf(stderr, "MEM_DEBUG: mem_leak= %d bytes, mem_max_size= %d bytes\n",
|
|
mem_cur_size, mem_max_size);
|
|
while (header)
|
|
{
|
|
fprintf(stderr, "%s:%u: error: %u bytes leaked\n",
|
|
header->filename, header->linenum, header->size);
|
|
header = header->next;
|
|
}
|
|
fflush(stderr);
|
|
mem_cur_size = 0;
|
|
mem_max_size = 0;
|
|
mem_debug_chain = NULL;
|
|
#if TCC_MEMORY_DEBUG-0 == 2
|
|
exit(2);
|
|
#endif /* TCC_MEMORY_DEBUG == 2 */
|
|
}
|
|
}
|
|
|
|
#endif /* TCC_MEMORY_DEBUG */
|