Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GC Memory leak #4617

Open
al1-ce opened this issue Apr 10, 2024 · 10 comments
Open

GC Memory leak #4617

al1-ce opened this issue Apr 10, 2024 · 10 comments

Comments

@al1-ce
Copy link

al1-ce commented Apr 10, 2024

OS info

OS: Arch Linux
LDC: v1.37.0

Steps to reproduce

touch test.d
echo "void main() {}" >> test.d
ldc test.d
valgrind --leak-check=full --show-leak-kinds=all ./test

Valgrind trace

Short (no --leak-check=full)

==40149== Memcheck, a memory error detector
==40149== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==40149== Using Valgrind-3.22.0 and LibVEX; rerun with -h for copyright info
==40149== Command: ./test
==40149== 
==40149== 
==40149== HEAP SUMMARY:
==40149==     in use at exit: 124 bytes in 4 blocks
==40149==   total heap usage: 1,085 allocs, 1,081 frees, 524,365 bytes allocated
==40149== 
==40149== LEAK SUMMARY:
==40149==    definitely lost: 0 bytes in 0 blocks
==40149==    indirectly lost: 0 bytes in 0 blocks
==40149==      possibly lost: 32 bytes in 1 blocks
==40149==    still reachable: 92 bytes in 3 blocks
==40149==         suppressed: 0 bytes in 0 blocks
==40149== Rerun with --leak-check=full to see details of leaked memory
==40149== 
==40149== For lists of detected and suppressed errors, rerun with: -s
==40149== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Full (click me)
==39912== Memcheck, a memory error detector
==39912== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==39912== Using Valgrind-3.22.0 and LibVEX; rerun with -h for copyright info
==39912== Command: ./test
==39912== 
==39912== 
==39912== HEAP SUMMARY:
==39912==     in use at exit: 124 bytes in 4 blocks
==39912==   total heap usage: 1,085 allocs, 1,081 frees, 524,365 bytes allocated
==39912== 
==39912== 10 bytes in 1 blocks are still reachable in loss record 1 of 4
==39912==    at 0x4843788: malloc (vg_replace_malloc.c:442)
==39912==    by 0x4F4CF0E: strdup (strdup.c:42)
==39912==    by 0x4D6D73C: rt.trace._sharedStaticCtor_L23_C1() (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D6AA37: rt.minfo.rt_moduleCtor().__foreachbody1(ref rt.sections_elf_shared.DSO) (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D6BA28: rt.sections_elf_shared.DSO.opApply(scope int(ref rt.sections_elf_shared.DSO) delegate) (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D62EC8: rt_init (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D6346C: rt.dmain2._d_run_main2(char[][], ulong, extern(C) int(char[][]) function).runAll() (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D632C6: _d_run_main2 (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D6311C: _d_run_main (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x109181: main (in /g/test)
==39912== 
==39912== 10 bytes in 1 blocks are still reachable in loss record 2 of 4
==39912==    at 0x4843788: malloc (vg_replace_malloc.c:442)
==39912==    by 0x4F4CF0E: strdup (strdup.c:42)
==39912==    by 0x4D6D75A: rt.trace._sharedStaticCtor_L23_C1() (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D6AA37: rt.minfo.rt_moduleCtor().__foreachbody1(ref rt.sections_elf_shared.DSO) (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D6BA28: rt.sections_elf_shared.DSO.opApply(scope int(ref rt.sections_elf_shared.DSO) delegate) (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D62EC8: rt_init (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D6346C: rt.dmain2._d_run_main2(char[][], ulong, extern(C) int(char[][]) function).runAll() (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D632C6: _d_run_main2 (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D6311C: _d_run_main (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x109181: main (in /g/test)
==39912== 
==39912== 32 bytes in 1 blocks are possibly lost in loss record 3 of 4
==39912==    at 0x4843788: malloc (vg_replace_malloc.c:442)
==39912==    by 0x4D3B7CA: core.internal.gc.impl.conservative.gc.initialize() (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D2CAB9: core.gc.registry.createGCInstance(immutable(char)[]) (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D48B9C: gc_init (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D48C15: gc_init_nothrow (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D483E6: _DThn16_4core8internal2gc4impl5protoQo7ProtoGC6qallocMFNbmkMxC8TypeInfoZSQCn6memory8BlkInfo_ (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D48DDB: gc_qalloc (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D4F917: core.lifetime._d_newitemT!(rt.critical_.D_CRITICAL_SECTION)._d_newitemT() (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D62D1D: _d_criticalenter2 (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D6E41F: rt.trace._staticDtor_L408_C1() (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D6AAF5: rt.minfo.rt_moduleTlsDtor().__foreachbody1(ref rt.sections_elf_shared.DSO) (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D6BAC1: rt.sections_elf_shared.DSO.opApplyReverse(scope int(ref rt.sections_elf_shared.DSO) delegate) (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912== 
==39912== 72 bytes in 1 blocks are still reachable in loss record 4 of 4
==39912==    at 0x484ABC0: realloc (vg_replace_malloc.c:1690)
==39912==    by 0x4D35B50: core.internal.container.common.xrealloc(void*, ulong) (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D35511: core.internal.container.array.Array!(core.gc.gcinterface.Range).Array.insertBack!().insertBack(core.gc.gcinterface.Range) (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D485FC: _DThn16_4core8internal2gc4impl5protoQo7ProtoGC8addRangeMFNbNiPvmxC8TypeInfoZv (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D695CD: rt.memory.initStaticDataGC().__foreachbody1(ref rt.sections_elf_shared.DSO) (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D6BA28: rt.sections_elf_shared.DSO.opApply(scope int(ref rt.sections_elf_shared.DSO) delegate) (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D62EC3: rt_init (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D6346C: rt.dmain2._d_run_main2(char[][], ulong, extern(C) int(char[][]) function).runAll() (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D632C6: _d_run_main2 (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x4D6311C: _d_run_main (in /usr/lib/libdruntime-ldc-shared.so.107.1)
==39912==    by 0x109181: main (in /g/test)
==39912== 
==39912== LEAK SUMMARY:
==39912==    definitely lost: 0 bytes in 0 blocks
==39912==    indirectly lost: 0 bytes in 0 blocks
==39912==      possibly lost: 32 bytes in 1 blocks
==39912==    still reachable: 92 bytes in 3 blocks
==39912==         suppressed: 0 bytes in 0 blocks
==39912== 
==39912== For lists of detected and suppressed errors, rerun with: -s
==39912== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
@al1-ce al1-ce changed the title Empty main produces memory leak GC Memory leak Apr 10, 2024
@al1-ce
Copy link
Author

al1-ce commented Apr 10, 2024

If to use -betterC switch then leak disappears

echo "extern(C) int main() { return 0; }" > test.d
ldc test.d -betterC
valgrind ./test

Outputs:

==41353== Memcheck, a memory error detector
==41353== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==41353== Using Valgrind-3.22.0 and LibVEX; rerun with -h for copyright info
==41353== Command: ./test
==41353== 
==41353== 
==41353== HEAP SUMMARY:
==41353==     in use at exit: 0 bytes in 0 blocks
==41353==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==41353== 
==41353== All heap blocks were freed -- no leaks are possible
==41353== 
==41353== For lists of detected and suppressed errors, rerun with: -s
==41353== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

@kassane
Copy link
Contributor

kassane commented Apr 11, 2024

and sanitizers? (address, memory, thread)

@al1-ce
Copy link
Author

al1-ce commented Apr 11, 2024

Havent tested, not sure how to though

@kassane
Copy link
Contributor

kassane commented Apr 11, 2024

Havent tested, not sure how to though

Try:

$> export ASAN_OPTIONS=abort_on_error=0:fast_unwind_on_malloc=0:detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1
$> ldc2 hello.d -fsanitize=memory
$> ./hello
## Output empty/none or stacktrce?

Reference

@al1-ce
Copy link
Author

al1-ce commented Apr 11, 2024

Nothing
But running resulting binary with valgrind kills my pc

@yellowsink
Copy link

yellowsink commented Apr 15, 2024

isn't leaking intentional in DMD and LDC without -lowmem?
i misunderstood, never mind.

@kinke
Copy link
Member

kinke commented Apr 15, 2024

These are probably just harmless little leaks in upstream druntime, where nobody bothered cleaning up a few bytes. E.g., for

==39912== 32 bytes in 1 blocks are possibly lost in loss record 3 of 4
==39912== at 0x4843788: malloc (vg_replace_malloc.c:442)
==39912== by 0x4D3B7CA: core.internal.gc.impl.conservative.gc.initialize() (in /usr/lib/libdruntime-ldc-shared.so.107.1

See https://github.com/dlang/dmd/blob/274eec89f4ce7f34a1776e3ac1b1e9c82de90811/druntime/src/core/internal/gc/impl/conservative/gc.d#L142 - that ConservativeGC object itself is malloc'd during runtime/GC initialization and most likely never freed.

@kassane
Copy link
Contributor

kassane commented Apr 21, 2024

Does this leak occur on another system and/or libc?
Because on the Alpine I didn't have the same problem.

@al1-ce
Copy link
Author

al1-ce commented Apr 22, 2024

I have no way of checking other platforms

Also not sure about libc, like, what I do with it?

@kassane
Copy link
Contributor

kassane commented Apr 22, 2024

Also not sure about libc, like, what I do with it?

I cited libc because Alpine uses musl and not glibc.
It would be interesting to know if this applies only to glibc, or even only to archlinux.

If you have docker installed, you can test it:

docker-ldc2
# ldc2 master - upstream (musl fixed)
$ docker pull kassany/alpine-ldc2
$ docker run --rm -it -v $(pwd):/app -w /app kassany/alpine-ldc2:latest ash

# for musl can build `ldc2/ldmd2 -static foo.d` (libunwind-static already installed)
# run valgrind (your host) in static-foo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants