in src/alloc_pool.c [526:669]
void qd_alloc_finalize(void)
{
//
// Note that the logging facility is already finalized by the time this is called.
// We will dump debugging information into debug_dump if specified.
//
// The assumption coming into this finalizer is that all allocations have been
// released. Any non-released objects shall be flagged.
//
//
// Note: By the time we get here, the server threads have been joined and there is
// only the main thread remaining. There is therefore no reason to be
// concerned about locking.
//
qd_alloc_item_t *item;
qd_alloc_type_t *type_item = DEQ_HEAD(type_list);
#ifdef QD_MEMORY_DEBUG
const char *last_leak = 0;
#endif
FILE *dump_file = 0;
if (debug_dump) {
dump_file = fopen(debug_dump, "w");
}
#if QD_MEMORY_DEBUG
// For memory debug builds make it very hard to ignore leaks
else {
dump_file = stderr;
}
#endif
while (type_item) {
qd_entity_cache_remove(QD_ALLOCATOR_TYPE, type_item);
qd_alloc_type_desc_t *desc = type_item->desc;
//
// Reclaim the items on the global free pool
//
item = pop_stack(&desc->global_pool->free_list);
while (item) {
FREE_CACHE_ALIGNED(item);
desc->stats->total_free_to_heap++;
item = pop_stack(&desc->global_pool->free_list);
}
free_stack_chunks(&desc->global_pool->free_list);
free(desc->global_pool);
desc->global_pool = 0;
desc->header = 0; // reset header, so we can initialize again later in qd_alloc (in subsequent test)
//
// Reclaim the items on thread pools
//
qd_alloc_pool_t *tpool = DEQ_HEAD(desc->tpool_list);
while (tpool) {
item = pop_stack(&tpool->free_list);
while (item) {
FREE_CACHE_ALIGNED(item);
desc->stats->total_free_to_heap++;
item = pop_stack(&tpool->free_list);
}
DEQ_REMOVE_HEAD(desc->tpool_list);
free_stack_chunks(&tpool->free_list);
FREE_CACHE_ALIGNED(tpool);
tpool = DEQ_HEAD(desc->tpool_list);
}
//
// Check the stats for lost items
//
if (dump_file && desc->stats->total_free_to_heap < desc->stats->total_alloc_from_heap) {
bool suppressed = false;
for (int i = 0; leaking_types[i]; ++i) {
if (strcmp(desc->type_name, leaking_types[i]) == 0) {
suppressed = true;
break;
}
}
fprintf(dump_file,
"alloc.c: Items of type '%s' remain allocated at shutdown: %"PRId64"%s\n",
desc->type_name,
desc->stats->total_alloc_from_heap - desc->stats->total_free_to_heap,
suppressed ? " (SUPPRESSED)" : "");
#ifdef QD_MEMORY_DEBUG
qd_alloc_type_t *qtype = (qd_alloc_type_t*) desc->debug;
qd_alloc_item_t *item = DEQ_HEAD(qtype->allocated);
char buf[100];
while (item) {
DEQ_REMOVE_HEAD(qtype->allocated);
char **strings = backtrace_symbols(item->backtrace, item->backtrace_size);
if (!suppressed) {
// DISPATCH-1795: avoid output noise by only printing
// backtraces for leaks that are not suppressed
qd_log_formatted_time(&item->timestamp, buf, 100);
fprintf(dump_file, "Leak: %s type: %s address: %p\n",
buf, desc->type_name, (void *)(&item[1]));
for (size_t i = 0; i < item->backtrace_size; i++)
fprintf(dump_file, "%s\n", strings[i]);
fprintf(dump_file, "\n");
last_leak = desc->type_name;
}
free(strings);
// free the item to prevent ASAN from also reporting this leak.
// Since this is a custom heap ASAN will dump the first
// malloc() of the object - not the last time it was allocated
// from the pool.
FREE_CACHE_ALIGNED(item);
item = DEQ_HEAD(qtype->allocated);
}
#endif
}
//
// Reclaim the descriptor components
//
free(desc->stats);
sys_mutex_free(desc->lock);
desc->lock = 0;
desc->trailer = 0;
DEQ_REMOVE_HEAD(type_list);
free(type_item);
type_item = DEQ_HEAD(type_list);
}
sys_mutex_free(init_lock);
if (debug_dump) {
fclose(dump_file);
free(debug_dump);
debug_dump = 0;
}
#ifdef QD_MEMORY_DEBUG
if (last_leak) {
fprintf(stderr, "ERROR: Aborted due to unexpected alloc pool leak of type '%s'\n", last_leak);
fflush(0);
abort();
}
#endif
}