Skip to content

Commit

Permalink
Add CONTEXT_realloc. Add heap tracking!
Browse files Browse the repository at this point in the history
Context_destroy now frees all memory allocated via Context_*
And can produce peak or current usage reports.
test_graph now leaks no memory.
  • Loading branch information
lilith committed Feb 24, 2016
1 parent 037c296 commit c524dcb
Show file tree
Hide file tree
Showing 8 changed files with 253 additions and 38 deletions.
2 changes: 2 additions & 0 deletions fastscaling.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ ProfilingLog * Context_get_profiler_log(Context * context);

Context * Context_create(void);
void Context_destroy(Context * context);
void Context_free_allocated_memory(Context * context);
void Context_print_memory_info(Context * context);

const char * Context_error_message(Context * context, char * buffer, size_t buffer_size);

Expand Down
1 change: 1 addition & 0 deletions imageflow.h
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,7 @@ struct flow_job_resource_buffer{
};

struct flow_job_png_encoder_state {
Context * context;
char *buffer;
size_t size;
struct flow_job_resource_buffer * output_resource;
Expand Down
206 changes: 204 additions & 2 deletions lib/context.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "fastscaling_private.h"
#include <stdio.h>
#include <string.h>
#include <nathanaeljones/imageflow/fastscaling.h>

#ifdef _MSC_VER
#pragma unmanaged
Expand Down Expand Up @@ -36,6 +37,10 @@ void Context_add_to_callstack(Context * context, const char * file, int line)
}
}

static size_t Context_size_of_context(Context * context){
return context->heap_tracking.total_slots * sizeof(struct HeapAllocation) + context->log.capacity * sizeof(ProfilingEntry) + sizeof(struct ContextStruct);
}

void Context_clear_error(Context * context){
context->error.callstack_count = 0;
context->error.callstack[0].file = NULL;
Expand Down Expand Up @@ -97,26 +102,190 @@ const char * Context_stacktrace (Context * context, char * buffer, size_t buffer
return buffer;
}

static bool expand_heap_tracking(Context * context){
size_t growth_factor = 2;
size_t growth_divisor = 1;

size_t new_size = (context->heap_tracking.total_slots * growth_factor) / growth_divisor + 1;
if (new_size < context->heap_tracking.total_slots) new_size = context->heap_tracking.total_slots;
if (new_size < 64) new_size = 64;

struct HeapAllocation * allocs = (struct HeapAllocation *)context->heap._calloc(context, new_size, sizeof(struct HeapAllocation),__FILE__, __LINE__);
if (allocs == NULL){
CONTEXT_error(context, Out_of_memory);
return false;
}


struct HeapAllocation * old = context->heap_tracking.allocs;
if (old != NULL) {
memcpy(allocs, old,
context->heap_tracking.total_slots * sizeof(struct HeapAllocation));
}

context->heap_tracking.allocs = allocs;
context->heap_tracking.total_slots = new_size;
if (old != NULL) {
context->heap._free(context, old, __FILE__, __LINE__);
}
return true;
}

static bool Context_memory_track(Context * context, void * ptr, size_t byte_count, const char * file, int line){
if (context->heap_tracking.next_free_slot == context->heap_tracking.total_slots){
if (!expand_heap_tracking(context)){
CONTEXT_error_return(context);
}
}
struct HeapAllocation * next = &context->heap_tracking.allocs[context->heap_tracking.next_free_slot];
if (next->ptr != NULL){
CONTEXT_error(context, Invalid_internal_state);
return false;
}
next->allocated_by = file;
next->allocated_by_line = line;
next->bytes = byte_count;
next->ptr = ptr;
context->heap_tracking.allocations_gross++;
context->heap_tracking.allocations_net++;
if (context->heap_tracking.allocations_net_peak < context->heap_tracking.allocations_net){
context->heap_tracking.allocations_net_peak = context->heap_tracking.allocations_net;
}
context->heap_tracking.bytes_allocated_gross += byte_count;
context->heap_tracking.bytes_allocated_net += byte_count;

if (context->heap_tracking.bytes_allocated_net_peak < context->heap_tracking.bytes_allocated_net){
context->heap_tracking.bytes_allocated_net_peak = context->heap_tracking.bytes_allocated_net;
}

for (size_t i = context->heap_tracking.next_free_slot + 1; i < context->heap_tracking.total_slots; i++){
if (context->heap_tracking.allocs[i].ptr == NULL){
context->heap_tracking.next_free_slot = i;
return true;
}
}
context->heap_tracking.next_free_slot = context->heap_tracking.total_slots;
return true;
}
static void Context_memory_untrack(Context * context, void * ptr, const char * file, int line){
for (int64_t i = context->heap_tracking.total_slots - 1; i >= 0; i--){
if (context->heap_tracking.allocs[i].ptr == ptr){
struct HeapAllocation * alloc = &context->heap_tracking.allocs[i];

context->heap_tracking.allocations_net--;
context->heap_tracking.bytes_allocated_net -= alloc->bytes;
context->heap_tracking.bytes_freed += alloc->bytes;
alloc->ptr = NULL;
alloc->bytes = 0;
alloc->allocated_by = NULL;
alloc->allocated_by_line = 0;

//Only seek backwards, so we always point to the first.
if ((int64_t)context->heap_tracking.next_free_slot > i){
context->heap_tracking.next_free_slot = (int64_t)i;
}

return;
}
}
//TODO: failed to untrack?? warning??
#ifdef DEBUG
fprintf(stderr, "%s:%d Failed to untrack memory allocated at %zu bytes\n", file, line, ptr);
#endif


}

void Context_print_memory_info(Context * context){
size_t meta_bytes = Context_size_of_context(context);
fprintf(stderr, "Context %p is using %zu bytes for metadata, %zu bytes for %zu allocations (total bytes %zu)\n", (void *)context, meta_bytes, context->heap_tracking.bytes_allocated_net, context->heap_tracking.allocations_net, context->heap_tracking.bytes_allocated_net + meta_bytes);
fprintf(stderr, "Context %p peak usage %zu bytes total, %zu allocations. %zu bytes from %zu allocations freed explicitly\n", (void *)context, meta_bytes + context->heap_tracking.bytes_allocated_net_peak, context->heap_tracking.allocations_net_peak, context->heap_tracking.bytes_freed, context->heap_tracking.allocations_gross - context->heap_tracking.allocations_net);

}
void Context_free_allocated_memory(Context * context){

// fprintf(stderr, "Context_free_allocated_memory:\n");
// Context_print_memory_info(context);
for (size_t i = 0; i < context->heap_tracking.total_slots; i++){
if (context->heap_tracking.allocs[i].ptr != NULL){


context->heap._free(context, context->heap_tracking.allocs[i].ptr, __FILE__, __LINE__);


struct HeapAllocation * alloc = &context->heap_tracking.allocs[i];
//fprintf(stderr, "Freed %zu bytes at %p, allocated at %s:%u\n",alloc->bytes, alloc->ptr, alloc->allocated_by, alloc->allocated_by_line);

context->heap_tracking.allocations_net--;
context->heap_tracking.bytes_allocated_net -= alloc->bytes;
alloc->ptr = NULL;
alloc->bytes = 0;
alloc->allocated_by = NULL;
alloc->allocated_by_line = 0;

//Only seek backwards, so we always point to the first.
if (context->heap_tracking.next_free_slot > i){
context->heap_tracking.next_free_slot = i;
}
}
}
if (context->heap_tracking.allocations_net != 0 ||
context->heap_tracking.bytes_allocated_net != 0){
fprintf(stderr, "Failed to deallocated %zu bytes", context->heap_tracking.bytes_allocated_net);

}

}

void * Context_calloc(Context * context, size_t instance_count, size_t instance_size, const char * file, int line)
{
#ifdef DEBUG
fprintf(stderr, "%s:%d calloc of %zu * %zu bytes\n", file, line, instance_count, instance_size);
#endif
return context->heap._calloc(context, instance_count, instance_size, file, line);

void * ptr = context->heap._calloc(context, instance_count, instance_size, file, line);
if (ptr == NULL) return NULL;
if (!Context_memory_track(context, ptr, instance_count * instance_size, file, line)){
context->heap._free(context, ptr, file, line);
return NULL;
}
return ptr;
}

void * Context_malloc(Context * context, size_t byte_count, const char * file, int line)
{
#ifdef DEBUG
fprintf(stderr, "%s:%d malloc of %zu bytes\n", file, line, byte_count);
#endif
return context->heap._malloc(context, byte_count, file, line);
void * ptr = context->heap._malloc(context, byte_count, file, line);
if (ptr == NULL) return NULL;
if (!Context_memory_track(context, ptr, byte_count, file, line)){
context->heap._free(context, ptr, file, line);
return NULL;
}
return ptr;
}


void * Context_realloc(Context * context, void * old_pointer, size_t new_byte_count, const char * file, int line)
{
#ifdef DEBUG
fprintf(stderr, "%s:%d realloc of %zu bytes\n", file, line, byte_count);
#endif
void * ptr = context->heap._realloc(context, old_pointer, new_byte_count, file, line);
if (ptr == NULL) return NULL;
Context_memory_untrack(context, old_pointer, __FILE__, __LINE__);
if (!Context_memory_track(context, ptr, new_byte_count, file, line)){
context->heap._free(context, ptr, file, line);
return NULL;
}
return ptr;
}

void Context_free(Context * context, void * pointer, const char * file, int line)
{
if (pointer == NULL) return;
Context_memory_untrack(context, pointer, file, line);
context->heap._free(context, pointer, file, line);
}

Expand All @@ -133,6 +302,10 @@ static void * DefaultHeapManager_malloc(struct ContextStruct * context, size_t b
{
return malloc(byte_count);
}
static void * DefaultHeapManager_realloc(struct ContextStruct * context, void * old_pointer, size_t new_byte_count, const char * file, int line)
{
return realloc(old_pointer, new_byte_count);
}
static void DefaultHeapManager_free(struct ContextStruct * context, void * pointer, const char * file, int line)
{
free(pointer);
Expand All @@ -143,9 +316,25 @@ void DefaultHeapManager_initialize(HeapManager * manager)
manager->_calloc = DefaultHeapManager_calloc;
manager->_malloc = DefaultHeapManager_malloc;
manager->_free = DefaultHeapManager_free;
manager->_realloc = DefaultHeapManager_realloc;
manager->_context_terminate = NULL;
}


static void Context_heap_tracking_initialize(Context * context){

context->heap_tracking.total_slots = 0;
context->heap_tracking.next_free_slot = 0;
context->heap_tracking.allocations_gross = 0;
context->heap_tracking.allocations_net =0;
context->heap_tracking.allocs = NULL;
context->heap_tracking.bytes_allocated_gross = 0;
context->heap_tracking.bytes_allocated_net = 0;
context->heap_tracking.allocations_net_peak = 0;
context->heap_tracking.bytes_allocated_net_peak = 0;
context->heap_tracking.bytes_freed = 0;
}

void Context_initialize(Context * context)
{
context->log.log = NULL;
Expand All @@ -159,9 +348,18 @@ void Context_initialize(Context * context)
context->error.reason = No_Error;
context->error.locked = false;
DefaultHeapManager_initialize(&context->heap);
Context_heap_tracking_initialize(context);
Context_set_floatspace (context, Floatspace_as_is, 0.0f, 0.0f, 0.0f);
}

static void Context_heap_tracking_terminate(Context *context){

if (context->heap_tracking.allocs != NULL){
context->heap._free(context, context->heap_tracking.allocs, __FILE__, __LINE__);
}
Context_heap_tracking_initialize(context);
}

Context * Context_create(void)
{
Context * c = (Context *)malloc(sizeof(Context));
Expand All @@ -176,7 +374,11 @@ void Context_terminate(Context * context)
if (context != NULL) {
if (context->heap._context_terminate != NULL) {
context->heap._context_terminate(context);
}else{
Context_free_allocated_memory(context);
}
Context_heap_tracking_terminate(context);

CONTEXT_free(context, context->log.log);
}
}
Expand Down
26 changes: 25 additions & 1 deletion lib/fastscaling_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,15 @@ typedef struct BitmapFloatStruct {

typedef void * (*context_calloc_function)(struct ContextStruct * context, size_t count, size_t element_size, const char * file, int line);
typedef void * (*context_malloc_function)(struct ContextStruct * context, size_t byte_count, const char * file, int line);

typedef void * (*context_realloc_function)(struct ContextStruct * context, void * old_pointer, size_t new_byte_count, const char * file, int line);
typedef void (*context_free_function) (struct ContextStruct * context, void * pointer, const char * file, int line);
typedef void (*context_terminate_function) (struct ContextStruct * context);

typedef struct _HeapManager {
context_calloc_function _calloc;
context_malloc_function _malloc;
context_realloc_function _realloc;
context_free_function _free;
context_terminate_function _context_terminate;
void * _private_state;
Expand All @@ -73,7 +76,7 @@ typedef struct _ErrorCallstackLine {

typedef struct _ErrorInfo {
StatusCode reason;
ErrorCallstackLine callstack[8];
ErrorCallstackLine callstack[14];
int callstack_count;
int callstack_capacity;
bool locked;
Expand Down Expand Up @@ -105,6 +108,24 @@ typedef struct _ColorspaceInfo {

} ColorspaceInfo;

struct HeapAllocation{
void * ptr;
size_t bytes;
const char * allocated_by;
int allocated_by_line;
};
struct HeapTrackingInfo{
struct HeapAllocation * allocs;
size_t next_free_slot;
size_t total_slots;
size_t bytes_allocated_net;
size_t bytes_allocated_gross;
size_t allocations_net;
size_t allocations_gross;
size_t bytes_freed;
size_t allocations_net_peak;
size_t bytes_allocated_net_peak;
};


/** Context: main structure **/
Expand All @@ -114,6 +135,7 @@ typedef struct ContextStruct {
HeapManager heap;
ProfilingLog log;
ColorspaceInfo colorspace;
struct HeapTrackingInfo heap_tracking;
} Context;


Expand All @@ -126,6 +148,7 @@ void Context_terminate(Context * context);

void * Context_calloc(Context * context, size_t, size_t, const char * file, int line);
void * Context_malloc(Context * context, size_t, const char * file, int line);
void * Context_realloc(Context * context, void * old_pointer, size_t new_byte_count, const char * file, int line);
void Context_free(Context * context, void * pointer, const char * file, int line);
bool Context_enable_profiling(Context * context,uint32_t default_capacity);
void Context_set_last_error(Context * context, StatusCode code, const char * file, int line);
Expand All @@ -137,6 +160,7 @@ void Context_add_to_callstack(Context * context, const char * file, int line);
#define CONTEXT_calloc(context, instance_count, element_size) Context_calloc(context, instance_count, element_size, __FILE__, __LINE__)
#define CONTEXT_calloc_array(context, instance_count, type_name) (type_name *) Context_calloc(context, instance_count, sizeof(type_name), __FILE__, __LINE__)
#define CONTEXT_malloc(context, byte_count) Context_malloc(context, byte_count, __FILE__, __LINE__)
#define CONTEXT_realloc(context,old_pointer, new_byte_count) Context_realloc(context, old_pointer, new_byte_count, __FILE__, __LINE__)
#define CONTEXT_free(context, pointer) Context_free(context, pointer, __FILE__, __LINE__)
#define CONTEXT_error(context, status_code) CONTEXT_SET_LAST_ERROR(context,status_code)

Expand Down

0 comments on commit c524dcb

Please sign in to comment.