Open HyperLightResearch opened 2 years ago
Are you able to tell what is leaking? What objects or where they're allocated? IIRC, the CRT leak checker runs before global cleanup routines are run, so certain global objects that are destructed/freed during shutdown are detected as leaking, when the code to clean them up simply hasn't had a chance to run yet. If there are leaks, I'll need more information about what it is. Running valgrind with openal-info says 0 bytes are definitely or potentially lost/leaked.
Are you able to tell what is leaking?
Unfortunately no, not at the moment. It's not a vtable, so at least its not a polymorphic object, that's about all we know. We can't get file/line on the leak reports even though we're trying to set things up the way MS describes here: https://learn.microsoft.com/en-us/visualstudio/debugger/finding-memory-leaks-using-the-crt-library?view=vs-2022
IIRC, the CRT leak checker runs before global cleanup routines are run, so certain global objects that are destructed/freed during shutdown are detected as leaking, when the code to clean them up simply hasn't had a chance to run yet.
There's a way to set up leak reporting such that it reports leaks after static destruction is done in the app (see link above).
In this specific case, OpenAL Soft is loaded automatically by another DLL that is itself manually loaded and unloaded. We see OpenAL32.dll get unloaded when our DLL is unloaded, so all static allocations would have been cleaned up by the time leaks are reported. When OpenAL startup code is commented out, we get no leaks. With it in, we get leaks even if no sounds are even created.
We're having a similarly cryptic issue with another library, so it's possible we're doing something goofy, but we run with leak reports always dumped by default during development and maintain a zero-leak policy. Still, we may be doing something wrong, so we will continue to look on our end.
Unfortunately no, not at the moment. It's not a vtable, so at least its not a polymorphic object, that's about all we know. We can't get file/line on the leak reports even though we're trying to set things up the way MS describes here: https://learn.microsoft.com/en-us/visualstudio/debugger/finding-memory-leaks-using-the-crt-library?view=vs-2022
Hmm, by the sounds of it, it'll need changes to the library so it can change new
to use the debug overload. I'd also need to properly forward line/file info through the wrappers.
Unfortunately MinGW doesn't seem to support the debug functionality (the _dbg
functions just forward to the normal functions), so I'm not able to test it here.
In this specific case, OpenAL Soft is loaded automatically by another DLL that is itself manually loaded and unloaded. We see OpenAL32.dll get unloaded when our DLL is unloaded, so all static allocations would have been cleaned up by the time leaks are reported.
That's interesting since OpenAL Soft pins the DLL on load:
BOOL APIENTRY DllMain(HINSTANCE module, DWORD reason, LPVOID /*reserved*/)
{
switch(reason)
{
case DLL_PROCESS_ATTACH:
/* Pin the DLL so we won't get unloaded until the process terminates */
GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
reinterpret_cast<WCHAR*>(module), &module);
break;
}
return TRUE;
}
That's supposed to prevent the DLL from being unloaded automatically, until process termination (because there's some background threads that don't like being deallocated at run-time, so it keeps the library loaded until the thread is killed at process end). This could be something to revisit and maybe make work, but unloading the DLL at run-time can still be problematic in some circumstances (since there's some system locks in place during DLL unload, access to certain resources is restricted and certain actions can't occur until after the DLL is unloaded and the locks released, when it's too late to actually do it).
We see OpenAL32.dll get unloaded when our DLL is unloaded
Ah, I was mistaken. OpenAL32.dll is not getting unloaded when we unload our DLL. When the process terminates, I never see output that says OpenAL32.dll gets unloaded. Obviously it's getting unloaded at some point, but it may not happen until after memory leak reporting is done. When I force-unload it right before returning from WinMain, we still see the leak, so it's likely not static data hanging around until after leak reporting.
Is there any way to fully clean up the DLL on process detach so that reference pinning isn't necessary and the DLL can be loaded and unloaded as expected? I'm curious about what threads and resources can't be freed until the OS tosses the entire process.
Is there any way to fully clean up the DLL on process detach so that reference pinning isn't necessary and the DLL can be loaded and unloaded as expected?
The primary issue is to get the WASAPI background thread to quit: https://github.com/kcat/openal-soft/blob/c835f07f/alc/backends/wasapi.cpp#L513 is the background thread function, and https://github.com/kcat/openal-soft/blob/c835f07f/alc/backends/wasapi.cpp#L1827 is where it's launched (and immediately detached, leaving it as a free-running background function). The background thread is needed since WASAPI requires COM to be initialized on the calling thread, while OpenAL doesn't (nor should OpenAL affect the status of COM initialization). So OpenAL Soft needs to run its own thread where it can initialize COM and make COM calls separately from caller threads.
What would need to happen is to not detach the thread, and keep a handle to the thread. Then during DLL unload (global destruction), it would need to send a MsgType::QuitThread
message to the background thread and join it, waiting for it to quit. The problem is, you can't join/wait for a thread to exit during global destruction (it deadlocks), but you can't leave the thread running either since the function code is going to be unloaded at any time afterward, and it'll start executing junk memory.
So, yes, it is the WASAPI backend that's leaking. When I switched to DSound we don't get any leaks. I suspect that the WASAPI leak might have to do with threading.
I took a stab at adding some cleanup code, but there are preconditions for some of these things that I am not aware of. I wasn't sure if cleanup could happen when the device is closed, or when the context is destroyed. Other than that, it seems somewhat straightofrward to send a QuitThread message to the backend and wait for it to join.
Separate from this but noticed while working on this, there's something interesting when it comes to reading INIs. The library doesn't search the entire path on Windows when looking for alsoft.ini. We don't copy the executable to the working directory when debugging, we just leave it where it is and specify our data directory as the current working directory on launch. alsoft.ini is found when it resides in the same directory as the executable, but not when it is in the current working directory, which is unexpected. I can file a separate bug for that.
(btw, the DSound backend calls CoInitialize() on the calling thread)
I took a stab at adding some cleanup code, but there are preconditions for some of these things that I am not aware of. I wasn't sure if cleanup could happen when the device is closed, or when the context is destroyed.
I'm not sure what you mean. Once global cleanup starts, devices and contexts aren't closed/destroyed, since it's unsafe at that time, if that's what you mean.
Other than that, it seems somewhat straightofrward to send a QuitThread message to the backend and wait for it to join.
Unfortunately you can't wait for it to join by the time it needs to quit. It's easy to send a QuitThread message, but thread::join()
deadlocks during global destruction, so there's no way to ensure the thread is completely stopped before letting the DLL finish unloading.
What may need to be done is to make a separate background COM thread for each device. It's not ideal, since the amount of work the COM thread needs to do is relatively low (it's very easy for one thread to handle all uses), but it would allow the background thread to quit properly when devices are closed. Alternatively, start and stop a single thread as needed (start it when a device opens and it's not yet running, stop it when there's no more devices using it). A touch more complicated, but probably doable.
alsoft.ini is found when it resides in the same directory as the executable, but not when it is in the current working directory, which is unexpected. I can file a separate bug for that.
Currently OpenAL Soft doesn't search the current working directory for an INI file, since the directory can be different depending on how it's run, and apps can change it at any time (before or after OpenAL loads/initializes), which would lead to different/inconsistent behavior.
Commit 68e04624b1f94d9bf8f9ebce5d90e64ab143efea should now ensure the COM thread is stopped when there's no open devices. The DLL still pins itself, but it might be safe to remove that, at least for properly behaving apps (ones that ensure contexts are properly destroyed, and devices are all closed, when the DLL is unloaded; ones that don't could see crashes).
Holy cow, that's awesome! I will give it a try and let you know how it goes! Thank you so much!!
Does the bug fix only work for 64 bit build? I was using the x86 build for a visual studio 2022 project and still receiving the same memory leak report. For the 64 bit build, after closing the OpenAL device, I see "Message loop finished" in debug output and no memory leak is reported.
Do you not see "Message loop finished" in the 32-bit build? The fix should work the same for 32-bit and 64-bit.
No I don't see the message in the 32-bit build. Though I installed the build from vcpkg and maybe they have not included the fix yet. I am using the 64-bit for now.
I'm using the latest openAL commit and I had the same issue (memory leak) on Windows 11 x64 / Windows SDK: 10.0.22621.0 / Visual Studio 2022 (v143) Here is the output log:
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\ntdll.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\kernel32.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\KernelBase.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\user32.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\win32u.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\gdi32.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\gdi32full.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\msvcp_win.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\ucrtbase.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\shell32.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\ole32.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\combase.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\rpcrt4.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\winmm.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\msvcp140d.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\vcruntime140d.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\vcruntime140_1d.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\ucrtbased.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\imm32.dll'.
The thread 0x60e4 has exited with code 0 (0x0).
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\advapi32.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\msvcrt.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\sechost.dll'.
Initializing Supported bac'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\windows.storage.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\WinTypes.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\SHCore.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\shlwapi.dll'.
Loading confiGot binary: GLoading confiKey disable-cVendor ID: ""Name: "12th GExtensions: +Key rt-prio nKey rt-time-lKey game_compKey game_compKey game_compKey game_compKey resamplerKey uhj/decodKey uhj/filteKey uhj/encodKey trap-al-eKey trap-alc-Key reverb/boKey drivers n'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\kernel.appcore.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\bcryptprimitives.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\clbcatq.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\MMDevAPI.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\devobj.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\cfgmgr32.dll'.
'wolf_tests.exe' (Win32): Unloaded 'C:\Windows\System32\cfgmgr32.dll'
'wolf_tests.exe' (Win32): Unloaded 'C:\Windows\System32\devobj.dll'
'wolf_tests.exe' (Win32): Unloaded 'C:\Windows\System32\MMDevAPI.dll'
Initialized bAdded "wasapiAdded "wasapiKey excludefxKey default-rKey eax/enablOpening defauStarting messStarting messGot message "'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\MMDevAPI.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\devobj.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\cfgmgr32.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\cfgmgr32.dll'.
'wolf_tests.exe' (Win32): Loaded 'C:\Windows\System32\cfgmgr32.dll'.
'wolf_tests.exe' (Win32): Unloaded 'C:\Windows\System32\cfgmgr32.dll'
'wolf_tests.exe' (Win32): Unloaded 'C:\Windows\System32\cfgmgr32.dll'
Created devicFreeing devicGot message "Message loop The thread 0x2e18 has exited with code 0 (0x0).
Debug Error!
Program: ...ceCodes\WolfEngine\WolfEngine\build\wolf\Debug\wolf_tests.exe
abort() has been called
That log is quite mangled and mostly overwritten. You can use the ALSOFT_LOGFILE
environment variable to have it written to the named file instead of getting mixed up with other output.
I am also having the same issue. If you check the dump you can clearly see that it is related to the audio device in the system. There is a leak for each device.
#include <iostream>
#include <crtdbg.h>
#include "alc.h"
int main()
{
_CrtMemState memState1, memState2, memState3;
_CrtMemCheckpoint(&memState1);
auto device = alcOpenDevice(nullptr);
if (!device)
{
std::cerr << "Failed to open device\n";
return 1;
}
alcCloseDevice(device);
_CrtMemCheckpoint(&memState2);
if (_CrtMemDifference(&memState3, &memState1, &memState2))
{
_CrtMemDumpAllObjectsSince(&memState1);
std::cerr << "Memory leak detected!\n";
}
return 0;
}
Here it is the dump created by running the code above:
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\ntdll.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\kernel32.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\KernelBase.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Users\Aybars\Desktop\Audio\build\Debug\OpenAL32.dll'. Symbols loaded.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\user32.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\win32u.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\gdi32.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\gdi32full.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\msvcp_win.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\ucrtbase.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\shell32.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\ole32.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\combase.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\rpcrt4.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\msvcp140d.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\msvcp140d_atomic_wait.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\advapi32.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\msvcrt.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\sechost.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\vcruntime140_1d.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\vcruntime140d.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\ucrtbased.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\avrt.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\winmm.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\imm32.dll'.
The thread 35392 has exited with code 0 (0x0).
[ALSOFT] (II) Initializing library v1.23.1-eee7eb7d master
[ALSOFT] (II) Supported backends: wasapi, dsound, winmm, null, wave
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\windows.storage.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\WinTypes.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\SHCore.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\shlwapi.dll'.
[ALSOFT] (II) Loading config C:\Users\Aybars\AppData\Roaming\alsoft.ini...
[ALSOFT] (II) Got binary: C:\Users\Aybars\Desktop\Audio\build\Debug, OpenAL_Audio_Manager.exe
[ALSOFT] (II) Loading config C:\Users\Aybars\Desktop\Audio\build\Debug\alsoft.ini...
[ALSOFT] (II) Vendor ID: ""
[ALSOFT] (II) Name: "12th Gen Intel(R) Core(TM) i5-12600KF"
[ALSOFT] (II) Extensions: +SSE +SSE2 +SSE3 +SSE4.1
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\kernel.appcore.dll'.
[ALSOFT] (II) Starting message thread
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\bcryptprimitives.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\clbcatq.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\MMDevAPI.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\devobj.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\cfgmgr32.dll'.
[ALSOFT] (II) Initialized backend "wasapi"
[ALSOFT] (II) Added "wasapi" for playback
[ALSOFT] (II) Added "wasapi" for capture
[ALSOFT] (II) Opening default playback device
[ALSOFT] (II) Got device "3 - LS49AG95 (3- AMD High Definition Audio Device)", "{1A78194C-30F5-4CA9-99F9-DD5CDC524DCF}", "{0.0.0.00000000}.{1a78194c-30f5-4ca9-99f9-dd5cdc524dcf}"
[ALSOFT] (II) Got device "Soundbar (Grundig Soundbar 2.1)", "{4313EA74-DE29-4F02-9E3E-BCB3B152D62C}", "{0.0.0.00000000}.{4313ea74-de29-4f02-9e3e-bcb3b152d62c}"
[ALSOFT] (II) Got device "Headset (Sound Blaster Tactic(3D) Omega)", "{A0AD32EB-0BA4-4B5A-BA5C-90D190E8E2B5}", "{0.0.0.00000000}.{a0ad32eb-0ba4-4b5a-ba5c-90d190e8e2b5}"
[ALSOFT] (II) Got device "Realtek Digital Output (Realtek(R) Audio)", "{D414344D-3CDB-418B-A403-1041C1BD5C54}", "{0.0.0.00000000}.{d414344d-3cdb-418b-a403-1041c1bd5c54}"
[ALSOFT] (II) Got device "Dijital Ses Arabirimi (2- MiraBox Capture)", "{08411398-CEDD-47F3-9541-6D59254A26B5}", "{0.0.1.00000000}.{08411398-cedd-47f3-9541-6d59254a26b5}"
[ALSOFT] (II) Got device "Headset Microphone (Sound Blaster Tactic(3D) Omega)", "{2838AB11-B7A5-410B-9F94-8BFE49AB8392}", "{0.0.1.00000000}.{2838ab11-b7a5-410b-9f94-8bfe49ab8392}"
[ALSOFT] (II) Starting message loop
[ALSOFT] (II) Got message "Open Device" (0x0000, this=000002893F6A57D8, param="")
[ALSOFT] (II) Created device 000002893F6ABA70, "OpenAL Soft on Soundbar (Grundig Soundbar 2.1)"
[ALSOFT] (II) Freeing device 000002893F6ABA70
[ALSOFT] (II) Got message "Close Device" (0x0004, this=000002893F6A57D8, param="")
Dumping objects ->
{638} normal block at 0x000002893F6C6B40, 8 bytes long.
Data: <p j? > 70 BA 6A 3F 89 02 00 00
{603} normal block at 0x000002893F68D640, 112 bytes long.
Data: <{ 0 . 0 . 1 . 0 > 7B 00 30 00 2E 00 30 00 2E 00 31 00 2E 00 30 00
{599} normal block at 0x000002893F68CE00, 112 bytes long.
Data: <{ 0 . 0 . 1 . 0 > 7B 00 30 00 2E 00 30 00 2E 00 31 00 2E 00 30 00
{598} normal block at 0x000002893F6C6730, 16 bytes long.
Data: <H l? > 48 D9 6C 3F 89 02 00 00 00 00 00 00 00 00 00 00
{595} normal block at 0x000002893F6C8CE0, 64 bytes long.
Data: <Headset Micropho> 48 65 61 64 73 65 74 20 4D 69 63 72 6F 70 68 6F
{594} normal block at 0x000002893F6C7130, 16 bytes long.
Data: < l? > F8 D8 6C 3F 89 02 00 00 00 00 00 00 00 00 00 00
{591} normal block at 0x000002893F6A4690, 48 bytes long.
Data: <{2838AB11-B7A5-4> 7B 32 38 33 38 41 42 31 31 2D 42 37 41 35 2D 34
{590} normal block at 0x000002893F6C6370, 16 bytes long.
Data: < l? > 20 D9 6C 3F 89 02 00 00 00 00 00 00 00 00 00 00
{582} normal block at 0x000002893F68D6F0, 112 bytes long.
Data: <{ 0 . 0 . 1 . 0 > 7B 00 30 00 2E 00 30 00 2E 00 31 00 2E 00 30 00
{581} normal block at 0x000002893F6C7220, 16 bytes long.
Data: < l? > D0 D8 6C 3F 89 02 00 00 00 00 00 00 00 00 00 00
{578} normal block at 0x000002893F6A3F90, 48 bytes long.
Data: <Dijital Ses Arab> 44 69 6A 69 74 61 6C 20 53 65 73 20 41 72 61 62
{577} normal block at 0x000002893F6C6C30, 16 bytes long.
Data: < l? > 80 D8 6C 3F 89 02 00 00 00 00 00 00 00 00 00 00
{574} normal block at 0x000002893F6A3F20, 48 bytes long.
Data: <{08411398-CEDD-4> 7B 30 38 34 31 31 33 39 38 2D 43 45 44 44 2D 34
{573} normal block at 0x000002893F6C6AA0, 16 bytes long.
Data: < l? > A8 D8 6C 3F 89 02 00 00 00 00 00 00 00 00 00 00
{567} normal block at 0x000002893F6CD880, 240 bytes long.
Data: <0ll? ?j? > 30 6C 6C 3F 89 02 00 00 90 3F 6A 3F 89 02 00 00
{566} normal block at 0x000002893F6C6E10, 16 bytes long.
Data: < > F0 9D 9B 8B FA 7F 00 00 00 00 00 00 00 00 00 00
{564} normal block at 0x000002893F68D2D0, 112 bytes long.
Data: <{ 0 . 0 . 0 . 0 > 7B 00 30 00 2E 00 30 00 2E 00 30 00 2E 00 30 00
{560} normal block at 0x000002893F68D9B0, 112 bytes long.
Data: <{ 0 . 0 . 0 . 0 > 7B 00 30 00 2E 00 30 00 2E 00 30 00 2E 00 30 00
{559} normal block at 0x000002893F6C6A00, 16 bytes long.
Data: < l? > D8 C6 6C 3F 89 02 00 00 00 00 00 00 00 00 00 00
{556} normal block at 0x000002893F6A4460, 48 bytes long.
Data: <Realtek Digital > 52 65 61 6C 74 65 6B 20 44 69 67 69 74 61 6C 20
{555} normal block at 0x000002893F6C6D20, 16 bytes long.
Data: < l? > 88 C6 6C 3F 89 02 00 00 00 00 00 00 00 00 00 00
{552} normal block at 0x000002893F6A4CB0, 48 bytes long.
Data: <{D414344D-3CDB-4> 7B 44 34 31 34 33 34 34 44 2D 33 43 44 42 2D 34
{551} normal block at 0x000002893F6C6780, 16 bytes long.
Data: < l? > B0 C6 6C 3F 89 02 00 00 00 00 00 00 00 00 00 00
{543} normal block at 0x000002893F68D220, 112 bytes long.
Data: <{ 0 . 0 . 0 . 0 > 7B 00 30 00 2E 00 30 00 2E 00 30 00 2E 00 30 00
{542} normal block at 0x000002893F6C63C0, 16 bytes long.
Data: <` l? > 60 C6 6C 3F 89 02 00 00 00 00 00 00 00 00 00 00
{539} normal block at 0x000002893F6A4230, 48 bytes long.
Data: <Headset (Sound B> 48 65 61 64 73 65 74 20 28 53 6F 75 6E 64 20 42
{538} normal block at 0x000002893F6C65F0, 16 bytes long.
Data: < l? > 10 C6 6C 3F 89 02 00 00 00 00 00 00 00 00 00 00
{535} normal block at 0x000002893F6A45B0, 48 bytes long.
Data: <{A0AD32EB-0BA4-4> 7B 41 30 41 44 33 32 45 42 2D 30 42 41 34 2D 34
{534} normal block at 0x000002893F6C7090, 16 bytes long.
Data: <8 l? > 38 C6 6C 3F 89 02 00 00 00 00 00 00 00 00 00 00
{526} normal block at 0x000002893F68D380, 112 bytes long.
Data: <{ 0 . 0 . 0 . 0 > 7B 00 30 00 2E 00 30 00 2E 00 30 00 2E 00 30 00
{525} normal block at 0x000002893F6C6A50, 16 bytes long.
Data: < l? > E8 C5 6C 3F 89 02 00 00 00 00 00 00 00 00 00 00
{522} normal block at 0x000002893F689A40, 32 bytes long.
Data: <Soundbar (Grundi> 53 6F 75 6E 64 62 61 72 20 28 47 72 75 6E 64 69
{521} normal block at 0x000002893F6C6960, 16 bytes long.
Data: < l? > 98 C5 6C 3F 89 02 00 00 00 00 00 00 00 00 00 00
{518} normal block at 0x000002893F6A4B60, 48 bytes long.
Data: <{4313EA74-DE29-4> 7B 34 33 31 33 45 41 37 34 2D 44 45 32 39 2D 34
{517} normal block at 0x000002893F6C64B0, 16 bytes long.
Data: < l? > C0 C5 6C 3F 89 02 00 00 00 00 00 00 00 00 00 00
{509} normal block at 0x000002893F68D170, 112 bytes long.
Data: <{ 0 . 0 . 0 . 0 > 7B 00 30 00 2E 00 30 00 2E 00 30 00 2E 00 30 00
{508} normal block at 0x000002893F6C67D0, 16 bytes long.
Data: <p l? > 70 C5 6C 3F 89 02 00 00 00 00 00 00 00 00 00 00
{505} normal block at 0x000002893F6C7360, 64 bytes long.
Data: <3 - LS49AG95 (3-> 33 20 2D 20 4C 53 34 39 41 47 39 35 20 28 33 2D
{504} normal block at 0x000002893F6C66E0, 16 bytes long.
Data: < l? > 20 C5 6C 3F 89 02 00 00 00 00 00 00 00 00 00 00
{501} normal block at 0x000002893F6A4070, 48 bytes long.
Data: <{1A78194C-30F5-4> 7B 31 41 37 38 31 39 34 43 2D 33 30 46 35 2D 34
{500} normal block at 0x000002893F6C6AF0, 16 bytes long.
Data: <H l? > 48 C5 6C 3F 89 02 00 00 00 00 00 00 00 00 00 00
{494} normal block at 0x000002893F6CC520, 480 bytes long.
Data: < fl? `sl? > E0 66 6C 3F 89 02 00 00 60 73 6C 3F 89 02 00 00
{490} normal block at 0x000002893F6C7760, 56 bytes long.
Data: < Wj? > 04 00 00 00 CD CD CD CD D8 57 6A 3F 89 02 00 00
{489} normal block at 0x000002893F67A210, 64 bytes long.
Data: <`wl? > 60 77 6C 3F 89 02 00 00 00 00 00 00 00 00 00 00
{445} normal block at 0x000002893F68B230, 16 bytes long.
Data: < > D0 9D 9B 8B FA 7F 00 00 00 00 00 00 00 00 00 00
{436} normal block at 0x000002893F68B4B0, 16 bytes long.
Data: <8 #f > 38 C9 D1 01 A4 00 00 00 A0 23 66 8B FA 7F 00 00
{377} normal block at 0x000002893F68A640, 32 bytes long.
Data: <OpenAL_Audio_Man> 4F 70 65 6E 41 4C 5F 41 75 64 69 6F 5F 4D 61 6E
{376} normal block at 0x000002893F68B690, 16 bytes long.
Data: <X\ > 58 5C A4 8B FA 7F 00 00 00 00 00 00 00 00 00 00
{375} normal block at 0x000002893F682E10, 48 bytes long.
Data: <C:\Users\Aybars\> 43 3A 5C 55 73 65 72 73 5C 41 79 62 61 72 73 5C
{374} normal block at 0x000002893F68ADD0, 16 bytes long.
Data: <0\ > 30 5C A4 8B FA 7F 00 00 00 00 00 00 00 00 00 00
Object dump complete.
The thread 35868 has exited with code 0 (0x0).
The thread 24240 has exited with code 0 (0x0).
The thread 15068 has exited with code 0 (0x0).
The thread 1676 has exited with code 0 (0x0).
The thread 42692 has exited with code 0 (0x0).
The program '[12436] OpenAL_Audio_Manager.exe' has exited with code 0 (0x0).
OpenAL Soft allocates some stuff when initializing, e.g. a list of known devices as well as a watcher to add and remove known devices as they're added to and removed from the system. Since the OpenAL API doesn't have explicit library init/deinit functions the app needs to call, there are some allocations that need to hang around since it doesn't know when the app may need them. For example, if the app calls
default_name = alcGetString(NULL, ALC_DEFAULT_ALL_DEVICES_SPECIFIER);
the returned string is dynamically allocated with a name that reflects the current state of the system, and the pointer must remain valid after the function returns since there's no telling when the app may access it. The only time the returned pointer becomes invalid is when re-querying the string, returning a possibly different pointer with a possibly different name to replace the old one. That returned pointer needs to remain valid until its replaced, since the app could use it at any time, including during its shutdown procedure.
Only once the library is unloaded will the returned string be freed and become invalid without a replacement. And since the library is pinned to prevent premature unloading, it won't unload until process termination, where the unloading happens in dependency order (i.e. the EXE has its shutdown procedures run first to ensure the DLLs its dependent on remain valid, allowing you to call stuff like printf
or memset
from the C runtime DLL during EXE shutdown, and the DLL's allocations won't be freed until the stuff using it is done first).
I see, so you basically say there are global/static variables lingering around. I switched the start point of memState1 as follows, and i still get memory leak, is that expected too ?
#include <iostream>
#include <crtdbg.h>
#include "alc.h"
int main()
{
auto device = alcOpenDevice(nullptr);
if (!device)
{
std::cerr << "Failed to open device\n";
return 1;
}
_CrtMemState memState1, memState2, memState3;
_CrtMemCheckpoint(&memState1);
alcCloseDevice(device);
_CrtMemCheckpoint(&memState2);
if (_CrtMemDifference(&memState3, &memState1, &memState2))
{
_CrtMemDumpAllObjectsSince(&memState1);
std::cerr << "Memory leak detected!\n";
}
return 0;
}
Dump:
OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Users\Aybars\Desktop\Audio\build\Debug\OpenAL_Audio_Manager.exe'. Symbols loaded.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\ntdll.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\kernel32.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\KernelBase.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Users\Aybars\Desktop\Audio\build\Debug\OpenAL32.dll'. Symbols loaded.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\user32.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\win32u.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\gdi32.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\gdi32full.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\msvcp_win.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\ucrtbase.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\shell32.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\ole32.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\combase.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\rpcrt4.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\msvcp140d.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\msvcp140d_atomic_wait.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\advapi32.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\msvcrt.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\sechost.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\vcruntime140_1d.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\vcruntime140d.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\ucrtbased.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\avrt.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\winmm.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\imm32.dll'.
The thread 28280 has exited with code 0 (0x0).
[ALSOFT] (II) Initializing library v1.23.1-eee7eb7d master
[ALSOFT] (II) Supported backends: wasapi, dsound, winmm, null, wave
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\windows.storage.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\WinTypes.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\SHCore.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\shlwapi.dll'.
[ALSOFT] (II) Loading config C:\Users\Aybars\AppData\Roaming\alsoft.ini...
[ALSOFT] (II) Got binary: C:\Users\Aybars\Desktop\Audio\build\Debug, OpenAL_Audio_Manager.exe
[ALSOFT] (II) Loading config C:\Users\Aybars\Desktop\Audio\build\Debug\alsoft.ini...
[ALSOFT] (II) Vendor ID: ""
[ALSOFT] (II) Name: "12th Gen Intel(R) Core(TM) i5-12600KF"
[ALSOFT] (II) Extensions: +SSE +SSE2 +SSE3 +SSE4.1
[ALSOFT] (II) Starting message thread
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\kernel.appcore.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\bcryptprimitives.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\clbcatq.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\MMDevAPI.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\devobj.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\cfgmgr32.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\cfgmgr32.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Unloaded 'C:\Windows\System32\cfgmgr32.dll'
'OpenAL_Audio_Manager.exe' (Win32): Loaded 'C:\Windows\System32\cfgmgr32.dll'.
'OpenAL_Audio_Manager.exe' (Win32): Unloaded 'C:\Windows\System32\cfgmgr32.dll'
[ALSOFT] (II) Initialized backend "wasapi"
[ALSOFT] (II) Added "wasapi" for playback
[ALSOFT] (II) Added "wasapi" for capture
[ALSOFT] (II) Opening default playback device
[ALSOFT] (II) Got device "3 - LS49AG95 (3- AMD High Definition Audio Device)", "{1A78194C-30F5-4CA9-99F9-DD5CDC524DCF}", "{0.0.0.00000000}.{1a78194c-30f5-4ca9-99f9-dd5cdc524dcf}"
[ALSOFT] (II) Got device "Headset (Sound Blaster Tactic(3D) Omega)", "{A0AD32EB-0BA4-4B5A-BA5C-90D190E8E2B5}", "{0.0.0.00000000}.{a0ad32eb-0ba4-4b5a-ba5c-90d190e8e2b5}"
[ALSOFT] (II) Got device "Realtek Digital Output (Realtek(R) Audio)", "{D414344D-3CDB-418B-A403-1041C1BD5C54}", "{0.0.0.00000000}.{d414344d-3cdb-418b-a403-1041c1bd5c54}"
[ALSOFT] (II) Got device "Dijital Ses Arabirimi (2- MiraBox Capture)", "{08411398-CEDD-47F3-9541-6D59254A26B5}", "{0.0.1.00000000}.{08411398-cedd-47f3-9541-6d59254a26b5}"
[ALSOFT] (II) Got device "Headset Microphone (Sound Blaster Tactic(3D) Omega)", "{2838AB11-B7A5-410B-9F94-8BFE49AB8392}", "{0.0.1.00000000}.{2838ab11-b7a5-410b-9f94-8bfe49ab8392}"
[ALSOFT] (II) Starting message loop
[ALSOFT] (II) Got message "Open Device" (0x0000, this=000001711D2EF0A8, param="")
[ALSOFT] (II) Created device 000001711D2FDA60, "OpenAL Soft on Headset (Sound Blaster Tactic(3D) Omega)"
[ALSOFT] (II) Freeing device 000001711D2FDA60
[ALSOFT] (II) Got message "Close Device" (0x0004, this=000001711D2EF0A8, param="")
Dumping objects ->
Object dump complete.
The thread 13176 has exited with code 0 (0x0).
The thread 21316 has exited with code 0 (0x0).
The thread 11268 has exited with code 0 (0x0).
The thread 35184 has exited with code 0 (0x0).
The thread 40988 has exited with code 0 (0x0).
The program '[22252] OpenAL_Audio_Manager.exe' has exited with code 0 (0x0).
I switched the start point of memState1 as follows, and i still get memory leak, is that expected too ?
I'm not seeing any leaks reported there:
Dumping objects -> Object dump complete.
An alternative check may be to do something like this:
std::ignore = alcGetString(nullptr, ALC_DEFAULT_ALL_DEVICES_SPECIFIER);
_CrtMemState memState1, memState2, memState3;
_CrtMemCheckpoint(&memState1);
auto device = alcOpenDevice(nullptr);
...
alcCloseDevice(device);
_CrtMemCheckpoint(&memState2);
...
Querying ALC_DEFAULT_ALL_DEVICES_SPECIFIER
should initialize the library and get it into a default state, then you can check if opening and closing a device leaves anything behind (which there could be a small amount, since some things are stored in a global vector when a device is opened that's removed when closed, but std::vector
doesn't guarantee the memory is shrunk/freed right away when removing elements).
The if clause returns true thus the dump is generated, normally it shouldn't.
OpenAL Soft 1.22.2 Visual Studio 2022 x86 Debug build Windows 11 21H2 (22000.1098) RealTek audio driver 10.0.22000.653 (4/28/22)
For this code
A single memory leak is reported on app exit
Memory leak reporting can be enabled by adding one include and two lines of code at the top of winmain
Run with debugger from Visual Studio, and on app exit, memory leaks will be printed to the Visual Studio output window.