From 155b06a4f5fa6656aef164678a101c5dbe0a9732 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Thu, 6 Nov 2025 17:33:52 +0100 Subject: [PATCH] Add `heaptrack` support (#25257) This PR, courtesy of @NagyZoltanPeter (https://github.com/waku-org/nwaku/pull/3522) adds the ability to track memory allocations in a program suitable for use with [heaptrack](https://github.com/KDE/heaptrack). By passing `-d:heaptrack --debugger:native` to compilation, calls to heaptrack will be injected when memory is being allocated and released - unlike `-d:useMalloc` this strategy also works with `refc` and the default memory pool. See https://github.com/KDE/heaptrack for usage examples. The resulting binary needs to be run with `heaptrack` and with the shared `libheaptrack_preload.so` in the `LD_LIBRARY_PATH`. --- doc/nimc.md | 2 ++ lib/system/alloc.nim | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/doc/nimc.md b/doc/nimc.md index 24a2b3afe1..bb48af1370 100644 --- a/doc/nimc.md +++ b/doc/nimc.md @@ -580,6 +580,8 @@ Define Effect Currently only clang and vcc. `strip` Strip debug symbols added by the backend compiler from the executable. +`heaptrack` Track memory allocations using + [heaptrack](https://github.com/KDE/heaptrack) ====================== ========================================================= diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index fcb7ccb0c8..4109348fc2 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -837,6 +837,15 @@ when defined(gcDestructors): dec maxIters if it == nil: break +when defined(heaptrack): + const heaptrackLib = + when defined(heaptrack_inject): + "libheaptrack_inject.so" + else: + "libheaptrack_preload.so" + proc heaptrack_malloc(a: pointer, size: int) {.cdecl, importc, dynlib: heaptrackLib.} + proc heaptrack_free(a: pointer) {.cdecl, importc, dynlib: heaptrackLib.} + proc rawAlloc(a: var MemRegion, requestedSize: int): pointer = when defined(nimTypeNames): inc(a.allocCounter) @@ -959,6 +968,8 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer = sysAssert(isAccessible(a, result), "rawAlloc 14") sysAssert(allocInv(a), "rawAlloc: end") when logAlloc: cprintf("var pointer_%p = alloc(%ld) # %p\n", result, requestedSize, addr a) + when defined(heaptrack): + heaptrack_malloc(result, requestedSize) proc rawAlloc0(a: var MemRegion, requestedSize: int): pointer = result = rawAlloc(a, requestedSize) @@ -967,6 +978,8 @@ proc rawAlloc0(a: var MemRegion, requestedSize: int): pointer = proc rawDealloc(a: var MemRegion, p: pointer) = when defined(nimTypeNames): inc(a.deallocCounter) + when defined(heaptrack): + heaptrack_free(p) #sysAssert(isAllocatedPtr(a, p), "rawDealloc: no allocated pointer") sysAssert(allocInv(a), "rawDealloc: begin") var c = pageAddr(p)