/* Memory Debugging */ #ifdef DWC_DEBUG_MEMORY #include "dwc_os.h" #include "dwc_list.h" struct allocation { void *addr; void *ctx; char *func; int line; uint32_t size; int dma; DWC_CIRCLEQ_ENTRY(allocation) entry; }; DWC_CIRCLEQ_HEAD(allocation_queue, allocation); struct allocation_manager { void *mem_ctx; struct allocation_queue allocations; /* statistics */ int num; int num_freed; int num_active; uint32_t total; uint32_t cur; uint32_t max; }; static struct allocation_manager *manager = NULL; static int add_allocation(void *ctx, uint32_t size, char const *func, int line, void *addr, int dma) { struct allocation *a; DWC_ASSERT(manager != NULL, "manager not allocated"); a = __DWC_ALLOC_ATOMIC(manager->mem_ctx, sizeof(*a)); if (!a) { return -DWC_E_NO_MEMORY; } a->func = __DWC_ALLOC_ATOMIC(manager->mem_ctx, DWC_STRLEN(func) + 1); if (!a->func) { __DWC_FREE(manager->mem_ctx, a); return -DWC_E_NO_MEMORY; } DWC_MEMCPY(a->func, func, DWC_STRLEN(func) + 1); a->addr = addr; a->ctx = ctx; a->line = line; a->size = size; a->dma = dma; DWC_CIRCLEQ_INSERT_TAIL(&manager->allocations, a, entry); /* Update stats */ manager->num++; manager->num_active++; manager->total += size; manager->cur += size; if (manager->max < manager->cur) { manager->max = manager->cur; } return 0; } static struct allocation *find_allocation(void *ctx, void *addr) { struct allocation *a; DWC_CIRCLEQ_FOREACH(a, &manager->allocations, entry) { if (a->ctx == ctx && a->addr == addr) { return a; } } return NULL; } static void free_allocation(void *ctx, void *addr, char const *func, int line) { struct allocation *a = find_allocation(ctx, addr); if (!a) { DWC_ASSERT(0, "Free of address %p that was never allocated or already freed %s:%d", addr, func, line); return; } DWC_CIRCLEQ_REMOVE(&manager->allocations, a, entry); manager->num_active--; manager->num_freed++; manager->cur -= a->size; __DWC_FREE(manager->mem_ctx, a->func); __DWC_FREE(manager->mem_ctx, a); } int dwc_memory_debug_start(void *mem_ctx) { DWC_ASSERT(manager == NULL, "Memory debugging has already started\n"); if (manager) { return -DWC_E_BUSY; } manager = __DWC_ALLOC(mem_ctx, sizeof(*manager)); if (!manager) { return -DWC_E_NO_MEMORY; } DWC_CIRCLEQ_INIT(&manager->allocations); manager->mem_ctx = mem_ctx; manager->num = 0; manager->num_freed = 0; manager->num_active = 0; manager->total = 0; manager->cur = 0; manager->max = 0; return 0; } void dwc_memory_debug_stop(void) { struct allocation *a; dwc_memory_debug_report(); DWC_CIRCLEQ_FOREACH(a, &manager->allocations, entry) { DWC_ERROR("Memory leaked from %s:%d\n", a->func, a->line); free_allocation(a->ctx, a->addr, NULL, -1); } __DWC_FREE(manager->mem_ctx, manager); } void dwc_memory_debug_report(void) { struct allocation *a; DWC_PRINTF("\n\n\n----------------- Memory Debugging Report -----------------\n\n"); DWC_PRINTF("Num Allocations = %d\n", manager->num); DWC_PRINTF("Freed = %d\n", manager->num_freed); DWC_PRINTF("Active = %d\n", manager->num_active); DWC_PRINTF("Current Memory Used = %d\n", manager->cur); DWC_PRINTF("Total Memory Used = %d\n", manager->total); DWC_PRINTF("Maximum Memory Used at Once = %d\n", manager->max); DWC_PRINTF("Unfreed allocations:\n"); DWC_CIRCLEQ_FOREACH(a, &manager->allocations, entry) { DWC_PRINTF(" addr=%p, size=%d from %s:%d, DMA=%d\n", a->addr, a->size, a->func, a->line, a->dma); } } /* The replacement functions */ void *dwc_alloc_debug(void *mem_ctx, uint32_t size, char const *func, int line) { void *addr = __DWC_ALLOC(mem_ctx, size); if (!addr) { return NULL; } if (add_allocation(mem_ctx, size, func, line, addr, 0)) { __DWC_FREE(mem_ctx, addr); return NULL; } return addr; } void *dwc_alloc_atomic_debug(void *mem_ctx, uint32_t size, char const *func, int line) { void *addr = __DWC_ALLOC_ATOMIC(mem_ctx, size); if (!addr) { return NULL; } if (add_allocation(mem_ctx, size, func, line, addr, 0)) { __DWC_FREE(mem_ctx, addr); return NULL; } return addr; } void dwc_free_debug(void *mem_ctx, void *addr, char const *func, int line) { free_allocation(mem_ctx, addr, func, line); __DWC_FREE(mem_ctx, addr); } void *dwc_dma_alloc_debug(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr, char const *func, int line) { void *addr = __DWC_DMA_ALLOC(dma_ctx, size, dma_addr); if (!addr) { return NULL; } if (add_allocation(dma_ctx, size, func, line, addr, 1)) { __DWC_DMA_FREE(dma_ctx, size, addr, *dma_addr); return NULL; } return addr; } void *dwc_dma_alloc_atomic_debug(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr, char const *func, int line) { void *addr = __DWC_DMA_ALLOC_ATOMIC(dma_ctx, size, dma_addr); if (!addr) { return NULL; } if (add_allocation(dma_ctx, size, func, line, addr, 1)) { __DWC_DMA_FREE(dma_ctx, size, addr, *dma_addr); return NULL; } return addr; } void dwc_dma_free_debug(void *dma_ctx, uint32_t size, void *virt_addr, dwc_dma_t dma_addr, char const *func, int line) { free_allocation(dma_ctx, virt_addr, func, line); __DWC_DMA_FREE(dma_ctx, size, virt_addr, dma_addr); } #endif /* DWC_DEBUG_MEMORY */