Trying to use heaptrack with uClibc stdlib crashes immediately on start due to infinite recursion. When a heaptrack's allocation function is called for the first time it calls ::hooks::init(), which then calls atexit(). If the atexit() implementation allocates then the allocation function in heaptrack is called again, which then calls ::hooks::init() again etc. etc. leading to an infinite recursion. Calling heaptrack_init() before atexit() in ::hooks::init() solves the problem, but I'm not sure if it may have any side effects. Note that even glibc calls memory allocation functions in its atexit() implementation. However, it has a preallocated static array for 32 exit handlers, so heaptrack forcing itself at the beginning of LD_PRELOAD is unlikely to hit the limit (which would force glibc to allocate another array). In case of uClibc it can be compiled with or without dynamic allocation in atexit(), ours is compiled with dynamic allocation enabled and as such it has no preallocated array. Bit of a backtrace: #1 0xb6cac238 in __new_exitfn () at libc/stdlib/_atexit.c:241 #2 0xb6cac098 in __GI___cxa_atexit (func=0xb6edf640 <(anonymous namespace)::hooks::<lambda()>::_FUN(void)>, arg=0x0, dso_handle=0xb6ef6338 <_dl_getenv+88>) at libc/stdlib/_atexit.c:164 #3 0xb6edf694 in (anonymous namespace)::hooks::init () at /sources/heaptrack-1.1.0/src/track/heaptrack_preload.cpp:126 #4 0xb6edfaf8 in realloc (ptr=0x0, size=320) at /sources/heaptrack-1.1.0/src/track/heaptrack_preload.cpp:198 #5 0xb6cac298 in __new_exitfn () at libc/stdlib/_atexit.c:246 #6 0xb6cac098 in __GI___cxa_atexit (func=0xb6edf640 <(anonymous namespace)::hooks::<lambda()>::_FUN(void)>, arg=0x0, dso_handle=0xb6ef6338 <_dl_getenv+88>) at libc/stdlib/_atexit.c:164 #7 0xb6edf694 in (anonymous namespace)::hooks::init () at /sources/heaptrack-1.1.0/src/track/heaptrack_preload.cpp:126 #8 0xb6edfaf8 in realloc (ptr=0x0, size=320) at /sources/heaptrack-1.1.0/src/track/heaptrack_preload.cpp:198 #9 0xb6cac298 in __new_exitfn () at libc/stdlib/_atexit.c:246 #10 0xb6cac098 in __GI___cxa_atexit (func=0xb6edf640 <(anonymous namespace)::hooks::<lambda()>::_FUN(void)>, arg=0x0, dso_handle=0xb6ef6338 <_dl_getenv+88>) at libc/stdlib/_atexit.c:164 #11 0xb6edf694 in (anonymous namespace)::hooks::init () at /sources/heaptrack-1.1.0/src/track/heaptrack_preload.cpp:126 #12 0xb6edfaf8 in realloc (ptr=0x0, size=320) at /sources/heaptrack-1.1.0/src/track/heaptrack_preload.cpp:198 #13 0xb6cac298 in __new_exitfn () at libc/stdlib/_atexit.c:246 #14 0xb6cac098 in __GI___cxa_atexit (func=0xb6edf640 <(anonymous namespace)::hooks::<lambda()>::_FUN(void)>, arg=0x0, dso_handle=0xb6ef6338 <_dl_getenv+88>) at libc/stdlib/_atexit.c:164 #15 0xb6edf694 in (anonymous namespace)::hooks::init () at /sources/heaptrack-1.1.0/src/track/heaptrack_preload.cpp:126 #16 0xb6edfaf8 in realloc (ptr=0x0, size=320) at /sources/heaptrack-1.1.0/src/track/heaptrack_preload.cpp:198
Hey Dan, thanks for the report. Can you tell me how to reproduce this locally on Arch? Without that, it's going to be hard for me to fix this properly. Have you tried moving the atexit call into the lambda of the heaptrack_init call? I.e. after the comment // cleanup environment to prevent tracing of child apps If that works for you too, then I think it would be preferrable.
Hi Milian, sorry for the slow response. You probably have to compile uClibc (I'm running against an ancient one here, 0.9.33.2) and then LD_PRELOAD it or link an app directly against in. When you run the app under heaptrack, you should get a crash immediately at the start. I don't know if there's an easy way to reproduce this against glibc. Moving the atexit() at the end of the lambda works.
Git commit 5ff967ab12fda20834100ea3861d65d74221db89 by Milian Wolff. Committed on 02/07/2018 at 20:58. Pushed by mwolff into branch '1.1'. Call libc / libstdc++ freeres function in libheaptrack's atexit On one hand, this prevents an infinite recursion when the call to atexit allocates, which happens e.g. with uClibc, since the call from heaptrack_preload did not install the recursion guard yet. On the other hand, this code move ensures that the freeres functions get called properly before heaptrack_stop is called. M +0 -19 src/track/heaptrack_preload.cpp M +19 -0 src/track/libheaptrack.cpp https://commits.kde.org/heaptrack/5ff967ab12fda20834100ea3861d65d74221db89