aoc-2025/libguf/guf_alloc_tracker.h
2025-12-23 21:13:41 +01:00

213 lines
7.5 KiB
C
Executable File

#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"
#include "guf_str_view_type.h"
typedef struct guf_alloc_tracker {
FILE *log, *err_log;
const char *name;
size_t alloc_count, realloc_count, free_count;
ptrdiff_t allocated_bytes;
unsigned 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, unsigned id, const char* name, FILE *log, FILE *err_log);
GUF_ALLOC_TRACKER_KWRDS guf_alloc_tracker guf_alloc_tracker_new(unsigned id, const char* name, 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(const 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, unsigned id, const char* name, 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->name = name;
t->id = id;
t->enabled = true;
return t;
}
GUF_ALLOC_TRACKER_KWRDS guf_alloc_tracker guf_alloc_tracker_new(unsigned id, const char* name, FILE *log, FILE *err_log)
{
guf_alloc_tracker t;
guf_alloc_tracker_init(&t, id, name, log, err_log);
return t;
}
GUF_ALLOC_TRACKER_KWRDS bool guf_alloc_tracker_found_leak(const 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 (name '%s' id = %u):\n"
"allocated_bytes: %td\n"
"alloc_count: %zu\n"
"realloc_count: %zu\n"
"free_count: %zu\n",
t->name ? t->name : "unnamed", 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 (name '%s' id %u): alloc_count reached SIZE_MAX\n", t->name ? t->name : "unnamed", 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 (name '%s' id %u): allocated_byte overflow\n", t->name ? t->name : "unnamed", t->id);
}
success = false;
}
if (t->allocated_bytes < 0) {
if (t->err_log) {
fprintf(t->err_log, "ERROR in guf_track_alloc (name '%s' id %u): allocated_bytes < 0\n", t->name ? t->name : "unnamed", t->id);
}
success = false;
}
if (t->log) {
fprintf(t->log, "guf_alloc_tracker (name '%s' id %u): alloc (%td bytes) %s\n", t->name ? t->name : "unnamed", 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 (name '%s' id %u): realloc_count reached SIZE_MAX\n", t->name ? t->name : "unnamed", t->id);
}
if (old_size < 0 || new_size < 0) {
success = false;
if (t->err_log) {
fprintf(t->err_log, "ERROR in guf_track_realloc (name '%s' id %u): old_size < 0 or new_size < 0\n", t->name ? t->name : "unnamed", 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 (name '%s' id %u): allocated_bytes - old_size under/overflows\n", t->name ? t->name : "unnamed", t->id);
}
}
if (t->allocated_bytes < 0) {
success = false;
fprintf(t->err_log, "ERROR in guf_track_realloc (name '%s' id %u): allocated_bytes < 0 after subtracting old_size\n", t->name ? t->name : "unnamed", 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 (name '%s' id %u): allocated_bytes overflow \n", t->name ? t->name : "unnamed", t->id);
}
}
if (t->allocated_bytes < 0) {
success = false;
fprintf(t->err_log, "ERROR in guf_track_realloc (name '%s' id %u): allocated_bytes < 0 after adding new_size\n", t->name ? t->name : "unnamed", t->id);
}
if (t->log) {
fprintf(t->log, "guf_alloc_tracker (name '%s' id %u): realloc (from %td to %td bytes) %s\n", t->name ? t->name : "unnamed", 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 (name '%s' id %u): free_count reached SIZE_MAX\n", t->name ? t->name : "unnamed", t->id);
}
if (size < 0) {
success = false;
if (t->err_log) {
fprintf(t->err_log, "ERROR in guf_track_free (name '%s' id %u): size < 0\n", t->name ? t->name : "unnamed", t->id);
}
}
if (t->allocated_bytes < size) {
success = false;
if (t->err_log) {
fprintf(t->err_log, "ERROR in guf_track_free (name '%s' id %u): freed more bytes than allocated\n", t->name ? t->name : "unnamed", 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 (name '%s' id %u): allocated_bytes - size under/overflows\n", t->name ? t->name : "unnamed", t->id);
}
}
if (t->log) {
fprintf(t->log, "guf_alloc_tracker (name '%s' id %u): free (%td bytes) %s\n", t->name ? t->name : "unnamed", 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