diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c index 6a9ea367107..ed41f30e291 100644 --- a/src/backend/utils/mmgr/aset.c +++ b/src/backend/utils/mmgr/aset.c @@ -43,6 +43,7 @@ * *------------------------------------------------------------------------- */ +#include #include "postgres.h" @@ -264,6 +265,114 @@ static AllocSetFreeList context_freelists[2] = } }; +typedef struct backtrace_cache_entry +{ + void *ptr; + char *symbol; +} backtrace_cache_entry; + + +#define BACKTRACE_CACHE_MAX_SIZE 8192 +static int backtrace_cache_size = -1; +static backtrace_cache_entry backtrace_cache[BACKTRACE_CACHE_MAX_SIZE]; + +static int +backtrace_cache_cmp(const void *a, const void *b) +{ + backtrace_cache_entry *ca = (backtrace_cache_entry *) a; + backtrace_cache_entry *cb = (backtrace_cache_entry *) b; + + if (ca->ptr < cb->ptr) + return -1; + else if (ca->ptr > cb->ptr) + return 1; + else + return 0; +} + +static char * +backtrace_cache_lookup(void *ptr) +{ + char **strings = NULL; + char *str = NULL; + + if (backtrace_cache_size < 0) + { + backtrace_cache_size = 0; + memset(backtrace_cache, 0, sizeof(backtrace_cache)); + } + + if (backtrace_cache_size > 0) + { + backtrace_cache_entry key; + backtrace_cache_entry *res; + + key.ptr = ptr; + key.symbol = NULL; + + res = bsearch(&key, + backtrace_cache, backtrace_cache_size, sizeof(backtrace_cache_entry), + backtrace_cache_cmp); + + if (res != NULL) + return strdup(res->symbol); + } + + strings = backtrace_symbols(&ptr, 1); + + Assert(strings != NULL); + + str = strdup(strings[0]); + free(strings); + + if (backtrace_cache_size < BACKTRACE_CACHE_MAX_SIZE) + { + backtrace_cache[backtrace_cache_size].ptr = ptr; + backtrace_cache[backtrace_cache_size].symbol = strdup(str); + backtrace_cache_size++; + + qsort(backtrace_cache, backtrace_cache_size, sizeof(backtrace_cache_entry), + backtrace_cache_cmp); + } + + return str; +} + +static inline void +memtrace_log(char *action, MemoryContext context, void *chunk, Size size) +{ + char bt[8192]; + int nptrs; + void *buffer[16]; + char *ptr = bt; + + /* interested only in Portal Context */ + if (context != PortalContext) + return; + + nptrs = backtrace(buffer, 16); + + memset(bt, 0, sizeof(bt)); + + for (int i = 0; i < nptrs; i++) + { + char *symbol = backtrace_cache_lookup(buffer[i]); + + if (i > 0) + { + *ptr = '/'; + ptr++; + } + + ptr = stpcpy(ptr, symbol); + free(symbol); + } + + elog(LOG, "MEMTRACE %s context %p chunk %p size %ld location %s", action, context, chunk, size, bt); +} + +#define MEMTRACE(action, context, chunk, size) memtrace_log((action), (context), (chunk), (size)) + /* ---------- * AllocSetFreeIndex - @@ -1028,7 +1137,11 @@ AllocSetAlloc(MemoryContext context, Size size, int flags) * AllocSetAllocLarge(). */ if (size > set->allocChunkLimit) - return AllocSetAllocLarge(context, size, flags); + { + void *p = AllocSetAllocLarge(context, size, flags); + MEMTRACE("allocate", context, p, size); + return p; + } /* * Request is small enough to be treated as a chunk. Look in the @@ -1076,6 +1189,8 @@ AllocSetAlloc(MemoryContext context, Size size, int flags) /* Disallow access to the chunk header. */ VALGRIND_MAKE_MEM_NOACCESS(chunk, ALLOC_CHUNKHDRSZ); + MEMTRACE("allocate", context, MemoryChunkGetPointer(chunk), size); + return MemoryChunkGetPointer(chunk); } @@ -1093,10 +1208,18 @@ AllocSetAlloc(MemoryContext context, Size size, int flags) * chunk into that block. Else must start a new one. */ if (unlikely(availspace < (chunk_size + ALLOC_CHUNKHDRSZ))) - return AllocSetAllocFromNewBlock(context, size, flags, fidx); + { + void *p = AllocSetAllocFromNewBlock(context, size, flags, fidx); + MEMTRACE("allocate", context, p, size); + return p; + } /* There's enough space on the current block, so allocate from that */ - return AllocSetAllocChunkFromBlock(context, block, size, chunk_size, fidx); + { + void *p = AllocSetAllocChunkFromBlock(context, block, size, chunk_size, fidx); + MEMTRACE("allocate", context, p, size); + return p; + } } /* @@ -1126,6 +1249,8 @@ AllocSetFree(void *pointer) set = block->aset; + MEMTRACE("free", (MemoryContext) set, pointer, 0); + #ifdef MEMORY_CONTEXT_CHECKING { /* Test for someone scribbling on unused space in chunk */ @@ -1170,6 +1295,8 @@ AllocSetFree(void *pointer) Assert(AllocBlockIsValid(block)); set = block->aset; + MEMTRACE("free", (MemoryContext) set, pointer, 0); + fidx = MemoryChunkGetValue(chunk); Assert(FreeListIdxIsValid(fidx)); link = GetFreeListLink(chunk); @@ -1268,6 +1395,8 @@ AllocSetRealloc(void *pointer, Size size, int flags) set = block->aset; + MEMTRACE("free", (MemoryContext) set, pointer, 0); + /* only check size in paths where the limits could be hit */ MemoryContextCheckSize((MemoryContext) set, size, flags); @@ -1373,6 +1502,8 @@ AllocSetRealloc(void *pointer, Size size, int flags) /* Disallow access to the chunk header. */ VALGRIND_MAKE_MEM_NOACCESS(chunk, ALLOC_CHUNKHDRSZ); + MEMTRACE("allocate", (MemoryContext) set, pointer, size); + return pointer; }