When running dart under heaptrack, or even just a dart compiled executable, heaptrack bails out with: > starting application, this might take some time... > terminate called after throwing an instance of '(anonymous namespace)::HeapTrack::LockCheckFailed' > terminate called recursively To reproduce: 1. Have a main.dart with minimal contents such as: void main(List<String> args) { print("Hello"); } 2. Download flutter from: https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.10.3-stable.tar.xz 3. Unpack somewhere 4. Just to make sure dart works, let's run: ~/flutter-3.13/bin/cache/dart-sdk/bin/dart main.dart 5. Run under heaptrack: heaptrack ~/flutter-3.13/bin/cache/dart-sdk/bin/dart main.dart optionally, you can get a main.exe executable with: $ flutter-3.13/bin/cache/dart-sdk/bin/dart compile exe main.dart $ heaptrack main.exe Which results in the same crash.
with dart installed from the arch package I get this: ``` $ heaptrack dart main.dart heaptrack output will be written to "/home/milian/heaptrack.dart.8966.zst" starting application, this might take some time... Hello # it hangs here ``` it hangs because it waits for $something to write to the pipe and then close it, but that never happens - our injection works but nothing ever calls any of the functions we intercept. it seems that's b/c dart uses `tc_malloc` instead of malloc, which somehow messes with our interception logic. I.e. with `gdb` I get: ``` $ gdb main.exe (gdb) break malloc (gdb) r Breakpoint 1.1, 0x0000555555a28944 in tc_malloc () (gdb) bt #0 0x0000555555a28944 in tc_malloc () #1 0x00007ffff7c43bc0 in ?? () from /usr/lib/libc.so.6 #2 0x0000555555783604 in MallocExtension::Initialize() () #3 0x0000555555778e13 in ?? () #4 0x00007ffff7c27dfe in __libc_start_main () from /usr/lib/libc.so.6 #5 0x0000555555751be5 in _start () ``` and indeed, this is pretty strange: ``` $ nm -aD main.exe | grep malloc 00000000004d4940 T __libc_malloc 00000000004d4940 T malloc 00000000004d4940 T tc_malloc ``` note how these _all_ have the same address. I tried to intercept `tc_malloc` like we do for `mi_malloc` but that doesn't work apparently...
ah, I found something that works: ``` $ heaptrack --use-inject ./main.exe heaptrack output will be written to "/home/milian/heaptrack.main.exe.19150.zst" starting application, this might take some time... NOTE: heaptrack detected DEBUGINFOD_URLS but will disable it to prevent unintended network delays during recording If you really want to use DEBUGINFOD, export HEAPTRACK_ENABLE_DEBUGINFOD=1 Hello heaptrack stats: allocations: 46 leaked allocations: 24 temporary allocations: 4 ``` I don't understand why the LD_PRELOAD mechanism doesn't work here...
The std::terminate call got fixed with this: commit 74fa950160892b06e26d3358b502a6331c2041bd (HEAD -> master, origin/master, origin/HEAD) Author: Milian Wolff <mail@milianw.de> Date: Thu Aug 24 09:29:09 2023 +0200 Don't use exceptions when timer thread fails to lock This is a bit unweildy without C++17 and std::optional, but it gets the job done. Apparently throwing exceptions doesn't work from within heaptrack when it's injected into a flutter binary, for unclear reasons. Maybe due to [1] but that's supposedly fixed in newer flutter versions already. But as soon as we throw, we would terminate in the context of a terminate. [1]: https://github.com/flutter/flutter/issues/66575