Add guf_alloc_tracker
This commit is contained in:
parent
6ffb79f7a0
commit
37f9011166
@ -27,10 +27,10 @@ else ()
|
|||||||
set(DBG_FLAGS /fsanitize=address)
|
set(DBG_FLAGS /fsanitize=address)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
add_executable(libguf_example src/test/example.c src/test/impls/str_impl.c src/test/impls/dict_impl.c src/test/impls/linalg_impl.c)
|
add_executable(libguf_example src/test/example.c src/test/impls/str_impl.c src/test/impls/dict_impl.c src/test/impls/linalg_impl.c src/test/impls/alloc_tracker_impl.c)
|
||||||
target_include_directories(libguf_example PRIVATE src src/test)
|
target_include_directories(libguf_example PRIVATE src src/test)
|
||||||
|
|
||||||
add_executable(libguf_test src/test/test.cpp src/test/test_dbuf.cpp src/test/test_dict.cpp src/test/test_str.cpp src/test/test_ckdint.cpp src/test/test_utf8.cpp src/test/impls/init_impl.c src/test/impls/dbuf_impl.c src/test/impls/str_impl.c src/test/impls/dict_impl.c src/test/impls/rand_impl.c src/test/impls/sort_impl.c src/test/impls/linalg_impl.c src/test/impls/ckdint_impl.c)
|
add_executable(libguf_test src/test/test.cpp src/test/test_dbuf.cpp src/test/test_dict.cpp src/test/test_str.cpp src/test/test_ckdint.cpp src/test/test_utf8.cpp src/test/impls/init_impl.c src/test/impls/dbuf_impl.c src/test/impls/str_impl.c src/test/impls/dict_impl.c src/test/impls/rand_impl.c src/test/impls/sort_impl.c src/test/impls/linalg_impl.c src/test/impls/ckdint_impl.c src/test/impls/alloc_tracker_impl.c)
|
||||||
target_include_directories(libguf_test PRIVATE src src/test)
|
target_include_directories(libguf_test PRIVATE src src/test)
|
||||||
|
|
||||||
set_target_properties(libguf_example libguf_test PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX})
|
set_target_properties(libguf_example libguf_test PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX})
|
||||||
|
|||||||
@ -15,17 +15,11 @@ typedef struct guf_allocator {
|
|||||||
void *ctx;
|
void *ctx;
|
||||||
} guf_allocator;
|
} guf_allocator;
|
||||||
|
|
||||||
typedef struct guf_alloc_meta {
|
// typedef enum guf_alloc_fn_type {
|
||||||
size_t alloc_count, realloc_count, free_count;
|
// GUF_ALLOC_FN_TYPE_ALLOC,
|
||||||
ptrdiff_t allocated_bytes;
|
// GUF_ALLOC_FN_TYPE_REALLOC,
|
||||||
uint32_t alloc_id;
|
// GUF_ALLOC_FN_TYPE_FREE,
|
||||||
} guf_alloc_meta;
|
// } guf_alloc_fn_type;
|
||||||
|
|
||||||
typedef enum guf_alloc_fn_type {
|
|
||||||
GUF_ALLOC_FN_TYPE_ALLOC,
|
|
||||||
GUF_ALLOC_FN_TYPE_REALLOC,
|
|
||||||
GUF_ALLOC_FN_TYPE_FREE,
|
|
||||||
} guf_alloc_fn_type;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
GUF_ALLOC_MAX_BYTES: Largest number of bytes an allocated buffer of elements of TYPE can have.
|
GUF_ALLOC_MAX_BYTES: Largest number of bytes an allocated buffer of elements of TYPE can have.
|
||||||
|
|||||||
@ -6,10 +6,11 @@
|
|||||||
#define GUF_ALLOC_LIBC_H
|
#define GUF_ALLOC_LIBC_H
|
||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
#include "guf_alloc.h"
|
#include "guf_alloc.h"
|
||||||
|
#define GUF_ALLOC_TRACKER_IMPL_STATIC
|
||||||
|
#include "guf_alloc_tracker.h"
|
||||||
|
|
||||||
typedef struct guf_libc_alloc_ctx {
|
typedef struct guf_libc_alloc_ctx {
|
||||||
guf_alloc_meta meta;
|
guf_alloc_tracker tracker;
|
||||||
bool track_allocs;
|
|
||||||
bool zero_init;
|
bool zero_init;
|
||||||
} guf_libc_alloc_ctx;
|
} guf_libc_alloc_ctx;
|
||||||
|
|
||||||
@ -17,13 +18,22 @@ static inline void *guf_libc_alloc(ptrdiff_t size, void *ctx)
|
|||||||
{
|
{
|
||||||
GUF_ASSERT_RELEASE(size >= 0);
|
GUF_ASSERT_RELEASE(size >= 0);
|
||||||
guf_libc_alloc_ctx *alloc_ctx = (guf_libc_alloc_ctx*)ctx;
|
guf_libc_alloc_ctx *alloc_ctx = (guf_libc_alloc_ctx*)ctx;
|
||||||
|
|
||||||
|
void *res = NULL;
|
||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
return NULL;
|
res = NULL;
|
||||||
} else if (alloc_ctx && alloc_ctx->zero_init) {
|
} else if (alloc_ctx && alloc_ctx->zero_init) {
|
||||||
return calloc(size, 1);
|
res = calloc(size, 1);
|
||||||
} else {
|
} else {
|
||||||
return malloc(size);
|
res = malloc(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (alloc_ctx && alloc_ctx->tracker.enabled) {
|
||||||
|
const bool succ = guf_track_alloc(&alloc_ctx->tracker, size);
|
||||||
|
GUF_ASSERT(succ);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void *guf_libc_realloc(void *ptr, ptrdiff_t old_size, ptrdiff_t new_size, void *ctx)
|
static inline void *guf_libc_realloc(void *ptr, ptrdiff_t old_size, ptrdiff_t new_size, void *ctx)
|
||||||
@ -37,21 +47,41 @@ static inline void *guf_libc_realloc(void *ptr, ptrdiff_t old_size, ptrdiff_t ne
|
|||||||
|
|
||||||
void *new_ptr = realloc(ptr, new_size);
|
void *new_ptr = realloc(ptr, new_size);
|
||||||
if (!new_ptr || new_size == 0) {
|
if (!new_ptr || new_size == 0) {
|
||||||
return NULL;
|
new_ptr = NULL;
|
||||||
} else if (alloc_ctx && alloc_ctx->zero_init && new_size > old_size) {
|
} else if (alloc_ctx && alloc_ctx->zero_init && new_size > old_size) {
|
||||||
ptrdiff_t len = new_size - old_size;
|
const ptrdiff_t len = new_size - old_size;
|
||||||
GUF_ASSERT(len > 0);
|
GUF_ASSERT(len > 0);
|
||||||
GUF_ASSERT(old_size + len == new_size);
|
GUF_ASSERT(old_size + len == new_size);
|
||||||
memset((char*)ptr + old_size, 0, len); // TODO: sketchy
|
memset((char*)ptr + old_size, 0, len); // TODO: sketchy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (alloc_ctx && alloc_ctx->tracker.enabled) {
|
||||||
|
const bool succ = guf_track_realloc(&alloc_ctx->tracker, old_size, new_size);
|
||||||
|
GUF_ASSERT(succ);
|
||||||
|
}
|
||||||
|
|
||||||
return new_ptr;
|
return new_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void guf_libc_free(void *ptr, ptrdiff_t size, void *ctx)
|
static inline void guf_libc_free(void *ptr, ptrdiff_t size, void *ctx)
|
||||||
{
|
{
|
||||||
(void)ctx;
|
|
||||||
(void)size;
|
|
||||||
free(ptr);
|
free(ptr);
|
||||||
|
|
||||||
|
guf_libc_alloc_ctx *alloc_ctx = (guf_libc_alloc_ctx*)ctx;
|
||||||
|
if (alloc_ctx && alloc_ctx->tracker.enabled) {
|
||||||
|
const bool succ = guf_track_free(&alloc_ctx->tracker, size);
|
||||||
|
GUF_ASSERT(succ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline guf_allocator *guf_libc_allocator_init(guf_allocator *a, guf_libc_alloc_ctx *ctx)
|
||||||
|
{
|
||||||
|
GUF_ASSERT_RELEASE(a);
|
||||||
|
a->alloc = guf_libc_alloc;
|
||||||
|
a->realloc = guf_libc_realloc;
|
||||||
|
a->free = guf_libc_free;
|
||||||
|
a->ctx = ctx;
|
||||||
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
static guf_allocator guf_allocator_libc = {
|
static guf_allocator guf_allocator_libc = {
|
||||||
|
|||||||
@ -1,117 +0,0 @@
|
|||||||
|
|
||||||
#include "guf_common.h"
|
|
||||||
#include "guf_alloc.h"
|
|
||||||
#include "math.h"
|
|
||||||
|
|
||||||
typedef struct guf_alloc_tracker {
|
|
||||||
FILE *log, *err_log;
|
|
||||||
size_t alloc_count, realloc_count, free_count;
|
|
||||||
ptrdiff_t allocated_bytes;
|
|
||||||
uint32_t id;
|
|
||||||
} guf_alloc_tracker;
|
|
||||||
|
|
||||||
static bool guf_track_alloc(guf_alloc_tracker *t, ptrdiff_t size)
|
|
||||||
{
|
|
||||||
GUF_ASSERT(t);
|
|
||||||
GUF_ASSERT(size >= 0);
|
|
||||||
bool success = true;
|
|
||||||
if (t->err_log && t->alloc_count == SIZE_MAX) {
|
|
||||||
fprintf(t->err_log, "guf_alloc_track (id %" PRIu32 ") WARNING: alloc_count overflow\n", t->id);
|
|
||||||
//success = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
t->alloc_count = guf_add_saturated_size_t(t->alloc_count, 1);
|
|
||||||
|
|
||||||
if (guf_add_is_overflow_ptrdiff(t->allocated_bytes, size)) {
|
|
||||||
if (t->err_log) {
|
|
||||||
fprintf(t->err_log, "guf_alloc_track (id %" PRIu32 ") ERROR: allocated_byte overflow\n", t->id);
|
|
||||||
}
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
t->allocated_bytes = guf_add_saturated_ptrdiff(t->allocated_bytes, size);
|
|
||||||
if (t->allocated_bytes < 0) {
|
|
||||||
if (t->err_log) {
|
|
||||||
fprintf(t->err_log, "guf_alloc_track (id %" PRIu32 ") ERROR: allocated_bytes < 0\n", t->id);
|
|
||||||
}
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t->log) {
|
|
||||||
fprintf(t->log, "guf_alloc_track (id %" PRIu32 "): alloc (%td bytes) %s\n", t->id, size, success ? "SUCCESS" : "FAILURE");
|
|
||||||
}
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static bool guf_track_realloc(guf_alloc_tracker *t, ptrdiff_t old_size, ptrdiff_t new_size)
|
|
||||||
{
|
|
||||||
GUF_ASSERT(t);
|
|
||||||
GUF_ASSERT(old_size >= 0 && new_size >= 0);
|
|
||||||
bool success = true;
|
|
||||||
if (t->err_log && t->realloc_count == SIZE_MAX) {
|
|
||||||
fprintf(t->err_log, "guf_realloc_track (id %" PRIu32 ") WARNING: realloc_count overflow\n");
|
|
||||||
//success = false;
|
|
||||||
}
|
|
||||||
t->realloc_count = guf_add_saturated_size_t(t->realloc_count, 1);
|
|
||||||
|
|
||||||
if (old_size < 0 || new_size < 0) {
|
|
||||||
if (t->err_log) {
|
|
||||||
fprintf(t->err_log, "guf_realloc_track (id %" PRIu32 ") ERROR: old_size < 0 or new_size < 0\n");
|
|
||||||
}
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
t->allocated_bytes = guf_sub_saturated_ptrdiff(t->allocated_bytes, old_size);
|
|
||||||
if (t->allocated_bytes < 0) {
|
|
||||||
success = false;
|
|
||||||
fprintf(t->err_log, "guf_realloc_track (id %" PRIu32 ") ERROR: allocated_bytes < 0 after subtracting old_size\n", t->id);
|
|
||||||
}
|
|
||||||
if (guf_add_is_overflow_ptrdiff(t->allocated_bytes, new_size)) {
|
|
||||||
success = false;
|
|
||||||
if (t->err_log) {
|
|
||||||
fprintf(t->err_log, "guf_realloc_track (id %" PRIu32 ") ERROR: allocated_bytes overflow \n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t->allocated_bytes = guf_add_saturated_ptrdiff(t->allocated_bytes, new_size);
|
|
||||||
|
|
||||||
if (t->allocated_bytes < 0) {
|
|
||||||
success = false;
|
|
||||||
fprintf(t->err_log, "guf_realloc_track (id %" PRIu32 ") ERROR: allocated_bytes < 0 after adding new_size\n", t->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t->log) {
|
|
||||||
fprintf(t->log, "guf_realloc_track (id %" PRIu32 "): realloc (from %td to %td bytes) %s", t->id, old_size, new_size, (success ? "SUCCESS" : "FAILURE"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool guf_track_free(guf_alloc_tracker *t, ptrdiff_t size)
|
|
||||||
{
|
|
||||||
GUF_ASSERT(t);
|
|
||||||
GUF_ASSERT(size >= 0);
|
|
||||||
|
|
||||||
if (t->err_log && t->free_count == SIZE_MAX) {
|
|
||||||
fprintf(t->err_log, "guf_track_free (id %" PRIu32 ") WARNING: free_count overflow\n");
|
|
||||||
}
|
|
||||||
bool success = true;
|
|
||||||
|
|
||||||
if (size < 0) {
|
|
||||||
success = false;
|
|
||||||
if (t->err_log) {
|
|
||||||
fprintf(t->err_log, "guf_track_free (id %" PRIu32 ") ERROR: size < 0\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t->allocated_bytes < size) {
|
|
||||||
success = false;
|
|
||||||
if (t->err_log) {
|
|
||||||
fprintf(t->err_log, "guf_track_free (id %" PRIu32 ") ERROR: freed more bytes than allocated\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t->allocated_bytes = guf_sub_saturated_ptrdiff(t->allocated_bytes, size);
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
200
src/guf_alloc_tracker.h
Normal file
200
src/guf_alloc_tracker.h
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
#if defined(GUF_ALLOC_TRACKER_IMPL_STATIC)
|
||||||
|
#define GUF_ALLOC_TRACKER_KWRDS static inline
|
||||||
|
#else
|
||||||
|
#define GUF_ALLOC_TRACKER_KWRDS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef GUF_ALLOC_TRACKER_H
|
||||||
|
#define GUF_ALLOC_TRACKER_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "guf_common.h"
|
||||||
|
|
||||||
|
typedef struct guf_alloc_tracker {
|
||||||
|
FILE *log, *err_log;
|
||||||
|
size_t alloc_count, realloc_count, free_count;
|
||||||
|
ptrdiff_t allocated_bytes;
|
||||||
|
uint32_t id;
|
||||||
|
bool enabled;
|
||||||
|
} guf_alloc_tracker;
|
||||||
|
|
||||||
|
#if !defined(GUF_ALLOC_TRACKER_IMPL_STATIC) && !defined(GUF_ALLOC_TRACKER_IMPL)
|
||||||
|
GUF_ALLOC_TRACKER_KWRDS guf_alloc_tracker *guf_alloc_tracker_init(guf_alloc_tracker *t, uint32_t id, FILE *log, FILE *err_log);
|
||||||
|
GUF_ALLOC_TRACKER_KWRDS void guf_alloc_tracker_print(const guf_alloc_tracker *t, FILE *f);
|
||||||
|
GUF_ALLOC_TRACKER_KWRDS bool guf_alloc_tracker_found_leak(guf_alloc_tracker *t);
|
||||||
|
|
||||||
|
GUF_ALLOC_TRACKER_KWRDS bool guf_track_alloc(guf_alloc_tracker *t, ptrdiff_t size);
|
||||||
|
GUF_ALLOC_TRACKER_KWRDS bool guf_track_realloc(guf_alloc_tracker *t, ptrdiff_t old_size, ptrdiff_t new_size);
|
||||||
|
GUF_ALLOC_TRACKER_KWRDS bool guf_track_free(guf_alloc_tracker *t, ptrdiff_t size);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(GUF_ALLOC_TRACKER_IMPL_STATIC) || defined(GUF_ALLOC_TRACKER_IMPL)
|
||||||
|
|
||||||
|
#include "guf_alloc.h"
|
||||||
|
#define GUF_MATH_CKDINT_IMPL_STATIC
|
||||||
|
#include "guf_math_ckdint.h"
|
||||||
|
|
||||||
|
GUF_ALLOC_TRACKER_KWRDS guf_alloc_tracker *guf_alloc_tracker_init(guf_alloc_tracker *t, uint32_t id, FILE *log, FILE *err_log)
|
||||||
|
{
|
||||||
|
GUF_ASSERT_RELEASE(t);
|
||||||
|
t->log = log;
|
||||||
|
t->err_log = err_log;
|
||||||
|
t->alloc_count = t->realloc_count = t->free_count = 0;
|
||||||
|
t->allocated_bytes = 0;
|
||||||
|
t->id = id;
|
||||||
|
t->enabled = true;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
GUF_ALLOC_TRACKER_KWRDS bool guf_alloc_tracker_found_leak(guf_alloc_tracker *t)
|
||||||
|
{
|
||||||
|
GUF_ASSERT_RELEASE(t);
|
||||||
|
return (t->allocated_bytes != 0) || (t->alloc_count != t->free_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GUF_ALLOC_TRACKER_KWRDS void guf_alloc_tracker_print(const guf_alloc_tracker *t, FILE *f)
|
||||||
|
{
|
||||||
|
GUF_ASSERT(t);
|
||||||
|
if (!f) {
|
||||||
|
f = stdout;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!t) {
|
||||||
|
fprintf(f, "guf_alloc_tracker_fprint: guf_alloc_tracker is NULL");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(f,
|
||||||
|
"guf_alloc_tracker (id = %" PRIu32 "):\n"
|
||||||
|
"allocated_bytes: %td\n"
|
||||||
|
"alloc_count: %zu\n"
|
||||||
|
"realloc_count: %zu\n"
|
||||||
|
"free_count: %zu\n",
|
||||||
|
t->id, t->allocated_bytes, t->alloc_count, t->realloc_count, t->free_count
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
GUF_ALLOC_TRACKER_KWRDS bool guf_track_alloc(guf_alloc_tracker *t, ptrdiff_t size)
|
||||||
|
{
|
||||||
|
GUF_ASSERT(t);
|
||||||
|
GUF_ASSERT(size >= 0);
|
||||||
|
|
||||||
|
bool success = true;
|
||||||
|
|
||||||
|
if (guf_saturating_add_size_t(t->alloc_count, 1, &t->alloc_count) != GUF_MATH_CKD_SUCCESS && t->err_log) {
|
||||||
|
fprintf(t->err_log, "WARNING in guf_track_alloc (id %" PRIu32 "): alloc_count reached SIZE_MAX\n", t->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (guf_saturating_add_ptrdiff_t(t->allocated_bytes, size, &t->allocated_bytes) != GUF_MATH_CKD_SUCCESS) {
|
||||||
|
if (t->err_log) {
|
||||||
|
fprintf(t->err_log, "ERROR in guf_track_alloc (id %" PRIu32 "): allocated_byte overflow\n", t->id);
|
||||||
|
}
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t->allocated_bytes < 0) {
|
||||||
|
if (t->err_log) {
|
||||||
|
fprintf(t->err_log, "ERROR in guf_track_alloc (id %" PRIu32 "): allocated_bytes < 0\n", t->id);
|
||||||
|
}
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t->log) {
|
||||||
|
fprintf(t->log, "guf_alloc_tracker (id %" PRIu32 "): alloc (%td bytes) %s\n", t->id, size, success ? "SUCCESS" : "FAILURE");
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GUF_ALLOC_TRACKER_KWRDS bool guf_track_realloc(guf_alloc_tracker *t, ptrdiff_t old_size, ptrdiff_t new_size)
|
||||||
|
{
|
||||||
|
GUF_ASSERT(t);
|
||||||
|
GUF_ASSERT(old_size >= 0 && new_size >= 0);
|
||||||
|
bool success = true;
|
||||||
|
|
||||||
|
if (guf_saturating_add_size_t(t->realloc_count, 1, &t->realloc_count) && t->err_log) {
|
||||||
|
fprintf(t->err_log, "WARNING in guf_track_realloc (id %" PRIu32 "): realloc_count reached SIZE_MAX\n", t->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (old_size < 0 || new_size < 0) {
|
||||||
|
success = false;
|
||||||
|
if (t->err_log) {
|
||||||
|
fprintf(t->err_log, "ERROR in guf_track_realloc (id %" PRIu32 "): old_size < 0 or new_size < 0\n", t->id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (guf_saturating_sub_ptrdiff_t(t->allocated_bytes, old_size, &t->allocated_bytes)) {
|
||||||
|
success = false;
|
||||||
|
if (t->err_log) {
|
||||||
|
fprintf(t->err_log, "ERROR in guf_track_realloc (id %" PRIu32 "): allocated_bytes - old_size under/overflows\n", t->id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (t->allocated_bytes < 0) {
|
||||||
|
success = false;
|
||||||
|
fprintf(t->err_log, "ERROR in guf_track_realloc (id %" PRIu32 "): allocated_bytes < 0 after subtracting old_size\n", t->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (guf_saturating_add_ptrdiff_t(t->allocated_bytes, new_size, &t->allocated_bytes)) {
|
||||||
|
success = false;
|
||||||
|
if (t->err_log) {
|
||||||
|
fprintf(t->err_log, "ERROR in guf_track_realloc (id %" PRIu32 "): allocated_bytes overflow \n", t->id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (t->allocated_bytes < 0) {
|
||||||
|
success = false;
|
||||||
|
fprintf(t->err_log, "ERROR in guf_track_realloc (id %" PRIu32 "): allocated_bytes < 0 after adding new_size\n", t->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t->log) {
|
||||||
|
fprintf(t->log, "guf_alloc_tracker (id %" PRIu32 "): realloc (from %td to %td bytes) %s\n", t->id, old_size, new_size, (success ? "SUCCESS" : "FAILURE"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
GUF_ALLOC_TRACKER_KWRDS bool guf_track_free(guf_alloc_tracker *t, ptrdiff_t size)
|
||||||
|
{
|
||||||
|
GUF_ASSERT(t);
|
||||||
|
GUF_ASSERT(size >= 0);
|
||||||
|
bool success = true;
|
||||||
|
|
||||||
|
if (guf_saturating_add_size_t(t->free_count, 1, &t->free_count) && t->err_log) {
|
||||||
|
fprintf(t->err_log, "WARNING in guf_track_free (id %" PRIu32 "): free_count reached SIZE_MAX\n", t->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size < 0) {
|
||||||
|
success = false;
|
||||||
|
if (t->err_log) {
|
||||||
|
fprintf(t->err_log, "ERROR in guf_track_free (id %" PRIu32 "): size < 0\n", t->id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t->allocated_bytes < size) {
|
||||||
|
success = false;
|
||||||
|
if (t->err_log) {
|
||||||
|
fprintf(t->err_log, "ERROR in guf_track_free (id %" PRIu32 "): freed more bytes than allocated\n", t->id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (guf_saturating_sub_ptrdiff_t(t->allocated_bytes, size, &t->allocated_bytes)) {
|
||||||
|
success = false;
|
||||||
|
if (t->err_log) {
|
||||||
|
fprintf(t->err_log, "ERROR in guf_track_free (id %" PRIu32 "): allocated_bytes - size under/overflows\n", t->id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t->log) {
|
||||||
|
fprintf(t->log, "guf_alloc_tracker (id %" PRIu32 "): free (%td bytes) %s\n", t->id, size, (success ? "SUCCESS" : "FAILURE"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* End impl */
|
||||||
|
|
||||||
|
#endif /* End header-guard */
|
||||||
|
|
||||||
|
#undef GUF_ALLOC_TRACKER_KWRDS
|
||||||
|
#undef GUF_ALLOC_TRACKER_IMPL
|
||||||
|
#undef GUF_ALLOC_TRACKER_IMPL_STATIC
|
||||||
@ -1,11 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
is parametrized: no
|
is parametrized: no
|
||||||
*/
|
*/
|
||||||
#if defined(GUF_MATH_CKDINT_IMPL_STATIC)
|
|
||||||
#define GUF_MATH_CKDINT_KWRDS static inline
|
|
||||||
#else
|
|
||||||
#define GUF_MATH_CKDINT_KWRDS
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// Functions for safely checking for over- and underflow of arithmetic operations
|
// Functions for safely checking for over- and underflow of arithmetic operations
|
||||||
@ -85,6 +80,8 @@
|
|||||||
|
|
||||||
typedef enum guf_math_ckd_result {GUF_MATH_CKD_SUCCESS = 0, GUF_MATH_CKD_OVERFLOW, GUF_MATH_CKD_UNDERFLOW} guf_math_ckd_result;
|
typedef enum guf_math_ckd_result {GUF_MATH_CKD_SUCCESS = 0, GUF_MATH_CKD_OVERFLOW, GUF_MATH_CKD_UNDERFLOW} guf_math_ckd_result;
|
||||||
|
|
||||||
|
#if !defined(GUF_MATH_CKDINT_IMPL_STATIC) && !defined(GUF_MATH_CKDINT_IMPL)
|
||||||
|
|
||||||
// Signed integer arithmetic checks (generated with libguf/tools/ckdint-gen.py)
|
// Signed integer arithmetic checks (generated with libguf/tools/ckdint-gen.py)
|
||||||
|
|
||||||
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_add_int(int a, int b);
|
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_add_int(int a, int b);
|
||||||
@ -262,6 +259,7 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_add_size_t(size_t a, size
|
|||||||
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_sub_size_t(size_t a, size_t b, size_t *result);
|
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_sub_size_t(size_t a, size_t b, size_t *result);
|
||||||
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_size_t(size_t a, size_t b, size_t *result);
|
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_size_t(size_t a, size_t b, size_t *result);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(GUF_MATH_CKDINT_IMPL) || defined(GUF_MATH_CKDINT_IMPL_STATIC)
|
#if defined(GUF_MATH_CKDINT_IMPL) || defined(GUF_MATH_CKDINT_IMPL_STATIC)
|
||||||
#include "guf_assert.h"
|
#include "guf_assert.h"
|
||||||
@ -2063,3 +2061,4 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_size_t(size_t a, size
|
|||||||
|
|
||||||
#undef GUF_MATH_CKDINT_IMPL
|
#undef GUF_MATH_CKDINT_IMPL
|
||||||
#undef GUF_MATH_CKDINT_IMPL_STATIC
|
#undef GUF_MATH_CKDINT_IMPL_STATIC
|
||||||
|
#undef GUF_MATH_CKDINT_KWRDS
|
||||||
|
|||||||
@ -59,7 +59,7 @@ int main(void)
|
|||||||
guf_platform_assert_native_word_bits();
|
guf_platform_assert_native_word_bits();
|
||||||
|
|
||||||
guf_allocator test_allocator = guf_allocator_libc;
|
guf_allocator test_allocator = guf_allocator_libc;
|
||||||
guf_libc_alloc_ctx test_allocator_ctx = {.zero_init = true, .track_allocs = false};
|
guf_libc_alloc_ctx test_allocator_ctx = {.zero_init = true, .tracker = (guf_alloc_tracker){.enabled = false}};
|
||||||
test_allocator.ctx = &test_allocator_ctx;
|
test_allocator.ctx = &test_allocator_ctx;
|
||||||
|
|
||||||
dict_cstr_int ht;
|
dict_cstr_int ht;
|
||||||
|
|||||||
2
src/test/impls/alloc_tracker_impl.c
Normal file
2
src/test/impls/alloc_tracker_impl.c
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
#define GUF_ALLOC_TRACKER_IMPL
|
||||||
|
#include "guf_alloc_tracker.h"
|
||||||
@ -16,8 +16,11 @@ void DbufIntTest::run()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// allocator_ctx.tracker.log = fopen("alloc_log.txt", "w");
|
||||||
|
// allocator_ctx.tracker.err_log = fopen("alloc_err_log.txt", "w");
|
||||||
|
|
||||||
dbuf_int dbuf {};
|
dbuf_int dbuf {};
|
||||||
dbuf_int_init(&dbuf, 0, &guf_allocator_libc);
|
dbuf_int_init(&dbuf, 0, &allocator);
|
||||||
|
|
||||||
push_check_name("test_push");
|
push_check_name("test_push");
|
||||||
|
|
||||||
@ -29,7 +32,7 @@ void DbufIntTest::run()
|
|||||||
dbuf_int_free(&dbuf, NULL);
|
dbuf_int_free(&dbuf, NULL);
|
||||||
TEST_CHECK(dbuf.size == 0 && dbuf.capacity == 0 && dbuf.data == NULL);
|
TEST_CHECK(dbuf.size == 0 && dbuf.capacity == 0 && dbuf.data == NULL);
|
||||||
|
|
||||||
dbuf_int_init(&dbuf, 24, &guf_allocator_libc);
|
dbuf_int_init(&dbuf, 24, &allocator);
|
||||||
TEST_CHECK(dbuf.size == 0 && dbuf.capacity == 24 && dbuf.data);
|
TEST_CHECK(dbuf.size == 0 && dbuf.capacity == 24 && dbuf.data);
|
||||||
|
|
||||||
test_push(&dbuf, 365);
|
test_push(&dbuf, 365);
|
||||||
@ -57,6 +60,12 @@ void DbufIntTest::run()
|
|||||||
test_insert_remove(2049);
|
test_insert_remove(2049);
|
||||||
|
|
||||||
pop_check_name();
|
pop_check_name();
|
||||||
|
|
||||||
|
TEST_CHECK(!guf_alloc_tracker_found_leak(&allocator_ctx.tracker));
|
||||||
|
// guf_alloc_tracker_print(&allocator_ctx.tracker, stdout);
|
||||||
|
// puts("");
|
||||||
|
// fclose(allocator_ctx.tracker.log);
|
||||||
|
// fclose(allocator_ctx.tracker.err_log);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<int> DbufIntTest::dbuf_to_vec(dbuf_int *dbuf)
|
std::vector<int> DbufIntTest::dbuf_to_vec(dbuf_int *dbuf)
|
||||||
@ -97,7 +106,7 @@ void DbufIntTest::test_push(dbuf_int *dbuf, int n)
|
|||||||
void DbufIntTest::test_insert_remove(int n)
|
void DbufIntTest::test_insert_remove(int n)
|
||||||
{
|
{
|
||||||
dbuf_int dbuf = {};
|
dbuf_int dbuf = {};
|
||||||
dbuf_int_init(&dbuf, 0, &guf_allocator_libc);
|
dbuf_int_init(&dbuf, 0, &allocator);
|
||||||
std::vector<int> vec = dbuf_to_vec(&dbuf);
|
std::vector<int> vec = dbuf_to_vec(&dbuf);
|
||||||
|
|
||||||
guf_err err = GUF_ERR_NONE;
|
guf_err err = GUF_ERR_NONE;
|
||||||
@ -222,6 +231,9 @@ void DbufCstringTest::run()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// allocator_ctx.tracker.log = fopen("alloc_log.txt", "w");
|
||||||
|
// allocator_ctx.tracker.err_log = fopen("alloc_err_log.txt", "w");
|
||||||
|
|
||||||
push_check_name("push_insert_erase");
|
push_check_name("push_insert_erase");
|
||||||
|
|
||||||
for (int i = 1; i <= 32; ++i) {
|
for (int i = 1; i <= 32; ++i) {
|
||||||
@ -235,7 +247,7 @@ void DbufCstringTest::run()
|
|||||||
test_push_insert_erase(2048, 11);
|
test_push_insert_erase(2048, 11);
|
||||||
|
|
||||||
dbuf_heap_cstr str_dbuf = {};
|
dbuf_heap_cstr str_dbuf = {};
|
||||||
dbuf_heap_cstr_init(&str_dbuf, 0, &guf_allocator_libc);
|
dbuf_heap_cstr_init(&str_dbuf, 0, &allocator);
|
||||||
std::vector<std::string> str_vec {};
|
std::vector<std::string> str_vec {};
|
||||||
|
|
||||||
for (int i = 0; i < 512; ++i) {
|
for (int i = 0; i < 512; ++i) {
|
||||||
@ -261,6 +273,12 @@ void DbufCstringTest::run()
|
|||||||
test_find(42);
|
test_find(42);
|
||||||
test_find(129);
|
test_find(129);
|
||||||
pop_check_name();
|
pop_check_name();
|
||||||
|
|
||||||
|
TEST_CHECK(!guf_alloc_tracker_found_leak(&allocator_ctx.tracker));
|
||||||
|
// guf_alloc_tracker_print(&allocator_ctx.tracker, stdout);
|
||||||
|
// puts("");
|
||||||
|
// fclose(allocator_ctx.tracker.log);
|
||||||
|
// fclose(allocator_ctx.tracker.err_log);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DbufCstringTest::test_iter(std::vector<std::string>& str_vec, dbuf_heap_cstr *str_dbuf, int step)
|
void DbufCstringTest::test_iter(std::vector<std::string>& str_vec, dbuf_heap_cstr *str_dbuf, int step)
|
||||||
@ -324,7 +342,7 @@ void DbufCstringTest::test_push_insert_erase(int n, ptrdiff_t start_cap)
|
|||||||
{
|
{
|
||||||
std::vector<std::string> str_vec;
|
std::vector<std::string> str_vec;
|
||||||
dbuf_heap_cstr str_dbuf {};
|
dbuf_heap_cstr str_dbuf {};
|
||||||
dbuf_heap_cstr_init(&str_dbuf, start_cap, &guf_allocator_libc);
|
dbuf_heap_cstr_init(&str_dbuf, start_cap, &allocator);
|
||||||
|
|
||||||
for (int i = 0; i < n; ++i) {
|
for (int i = 0; i < n; ++i) {
|
||||||
constexpr int BUF_SZ = 128;
|
constexpr int BUF_SZ = 128;
|
||||||
@ -449,7 +467,7 @@ void DbufCstringTest::test_find(int n)
|
|||||||
std::vector<std::string> str_vec {};
|
std::vector<std::string> str_vec {};
|
||||||
|
|
||||||
dbuf_heap_cstr str_dbuf = {};
|
dbuf_heap_cstr str_dbuf = {};
|
||||||
dbuf_heap_cstr_init(&str_dbuf, 0, &guf_allocator_libc);
|
dbuf_heap_cstr_init(&str_dbuf, 0, &allocator);
|
||||||
|
|
||||||
for (int i = 0; i < n; ++i) {
|
for (int i = 0; i < n; ++i) {
|
||||||
constexpr int BUF_SZ = 128;
|
constexpr int BUF_SZ = 128;
|
||||||
|
|||||||
@ -6,14 +6,24 @@ extern "C"
|
|||||||
{
|
{
|
||||||
#include "guf_alloc_libc.h"
|
#include "guf_alloc_libc.h"
|
||||||
#include "impls/dbuf_impl.h"
|
#include "impls/dbuf_impl.h"
|
||||||
|
#include "guf_alloc_libc.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DbufIntTest : public Test
|
struct DbufIntTest : public Test
|
||||||
{
|
{
|
||||||
DbufIntTest(const std::string& name) : Test(name) {};
|
DbufIntTest(const std::string& name) : Test(name)
|
||||||
|
{
|
||||||
|
allocator_ctx.zero_init = false;
|
||||||
|
guf_alloc_tracker_init(&allocator_ctx.tracker, 1, NULL, NULL);
|
||||||
|
guf_libc_allocator_init(&allocator, &allocator_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
void run() override;
|
void run() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
guf_allocator allocator;
|
||||||
|
guf_libc_alloc_ctx allocator_ctx;
|
||||||
|
|
||||||
std::vector<int> dbuf_to_vec(dbuf_int *dbuf);
|
std::vector<int> dbuf_to_vec(dbuf_int *dbuf);
|
||||||
void test_push(dbuf_int *dbuf, int n);
|
void test_push(dbuf_int *dbuf, int n);
|
||||||
void test_insert_remove(int n);
|
void test_insert_remove(int n);
|
||||||
@ -22,10 +32,19 @@ private:
|
|||||||
|
|
||||||
struct DbufCstringTest : public Test
|
struct DbufCstringTest : public Test
|
||||||
{
|
{
|
||||||
DbufCstringTest(std::string name) : Test(name) {};
|
DbufCstringTest(std::string name) : Test(name)
|
||||||
|
{
|
||||||
|
allocator_ctx.zero_init = false;
|
||||||
|
guf_alloc_tracker_init(&allocator_ctx.tracker, 2, NULL, NULL);
|
||||||
|
guf_libc_allocator_init(&allocator, &allocator_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
void run() override;
|
void run() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
guf_allocator allocator;
|
||||||
|
guf_libc_alloc_ctx allocator_ctx;
|
||||||
|
|
||||||
void test_iter(std::vector<std::string>& str_vec, dbuf_heap_cstr *str_dbuf, int step = 1);
|
void test_iter(std::vector<std::string>& str_vec, dbuf_heap_cstr *str_dbuf, int step = 1);
|
||||||
void test_push_insert_erase(int n, ptrdiff_t start_cap = 0);
|
void test_push_insert_erase(int n, ptrdiff_t start_cap = 0);
|
||||||
void test_find(int n = 32);
|
void test_find(int n = 32);
|
||||||
|
|||||||
11
todo.txt
11
todo.txt
@ -1,11 +1,7 @@
|
|||||||
- guf_wrapping_mul_TYPE: Don't rely on implementation defined behaviour
|
- guf_wrapping_mul_TYPE: Not 100 % sure if it does not depend on implementation defined behaviour, but it shouldn't
|
||||||
|
|
||||||
- header guards for optional int64_t types maybe...
|
|
||||||
|
|
||||||
- sort: add cpp #ifdef to remove restrict from declaration
|
- sort: add cpp #ifdef to remove restrict from declaration
|
||||||
|
|
||||||
- separate impl and headers from tests (for compile perf)
|
|
||||||
|
|
||||||
- tests for guf_dict with GUF_DICT_64_BIT_IDX (and also hash32/hash64); maybe pass kv_type to insert to avoid copy
|
- tests for guf_dict with GUF_DICT_64_BIT_IDX (and also hash32/hash64); maybe pass kv_type to insert to avoid copy
|
||||||
- dict elems shrink to fit; allow to pass GUF_DBUF_USE_GROWTH_FAC_ONE_POINT_FIVE; start capacity (for elems and kv_indices?)
|
- dict elems shrink to fit; allow to pass GUF_DBUF_USE_GROWTH_FAC_ONE_POINT_FIVE; start capacity (for elems and kv_indices?)
|
||||||
- dict: if load factor is high due to mostly tombstones, just try rehashing without resizing first?
|
- dict: if load factor is high due to mostly tombstones, just try rehashing without resizing first?
|
||||||
@ -15,11 +11,6 @@
|
|||||||
|
|
||||||
- guf_stack, guf_queue, guf_dqueue, guf_prio_queue (using a heap), guf_ringbuf
|
- guf_stack, guf_queue, guf_dqueue, guf_prio_queue (using a heap), guf_ringbuf
|
||||||
|
|
||||||
- track allocs for test (implement alloc tracker):
|
|
||||||
- each thread needs its own alloc and alloc_ctx; don't track granular, give each allocator it's unique id maybe?
|
|
||||||
|
|
||||||
- potential idea for alloc: instead of using a pointer (8 or 4 bytes), use a 2 byte id to save space (and avoid dangling pointers):
|
|
||||||
|
|
||||||
- no guf_init.h
|
- no guf_init.h
|
||||||
|
|
||||||
- unicode normalisation
|
- unicode normalisation
|
||||||
|
|||||||
@ -408,7 +408,10 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
"""))
|
"""))
|
||||||
|
|
||||||
|
|
||||||
|
print("#if !defined(GUF_MATH_CKDINT_IMPL_STATIC) && !defined(GUF_MATH_CKDINT_IMPL)")
|
||||||
print(code_header)
|
print(code_header)
|
||||||
|
print("#endif")
|
||||||
|
|
||||||
print("#if defined(GUF_MATH_CKDINT_IMPL) || defined(GUF_MATH_CKDINT_IMPL_STATIC)")
|
print("#if defined(GUF_MATH_CKDINT_IMPL) || defined(GUF_MATH_CKDINT_IMPL_STATIC)")
|
||||||
print('#include "guf_assert.h"')
|
print('#include "guf_assert.h"')
|
||||||
@ -417,5 +420,6 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
print("#endif")
|
print("#endif")
|
||||||
|
|
||||||
|
print("#undef GUF_MATH_CKDINT_KWRDS")
|
||||||
print("#undef GUF_MATH_CKDINT_IMPL")
|
print("#undef GUF_MATH_CKDINT_IMPL")
|
||||||
print("#undef GUF_MATH_CKDINT_IMPL_STATIC\n")
|
print("#undef GUF_MATH_CKDINT_IMPL_STATIC\n")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user