#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 #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