[Solved] Help with malloc error on AudioProcessor destructor

I’m trying to debug a crash on shutdown, initially picked up by the leak detector, and now hitting a malloc error:

malloc: *** error for object 0x610000067b00: pointer being freed was not allocated

Its coming from the AudioProcessor destructor:

Any pointers on how to debug this?

I’ve re-architected my app about 3 times this morning trying to solve this issue…!

Open the schemes window and then go to the “Diagnostics” tab.
Enable “Address Sanitiser”. It’s one of the most useful tools ever to have been created and doesn’t really slow your app down too much (I have it on pretty much permanently unless I’m profiling).

You’ll have to rebuild and reproduce the crash again but you’ll get a stack report when you do of where the object was allocated and where it was destroyed.

4 Likes

Thanks Dave, that definitely helped me to see what was going on… the AudioProcessorGraph::Node destructor was trying to delete already deallocated memory.

My mistake was instantiating AudioProcessors as class members - I’m now instantiating AudioProcessor pointers and handing them over to AudioProcessorGraph to manage.

Hi @adamwilson … I know this was a long time ago, but do you recall what it was that was actually causing the memory issue? It seems like simply making an AudioProcessor a member shouldn’t cause this kind of problem.

We’re getting a similar thing. Simply creating an AudioProcessor instance and allowing it to be destroyed causes memory corruption.

It doesn’t matter whether it’s allocated on the stack or heap, or which thread it’s created on.

Our application makes extensive use of other JUCE code including hosting plugins, and is working fine. It’s just AudioProcessor that seems to have the issue.

We get something like:

022-05-06 18:10:59.603537+0100 Lightworks[87841:1009683] ==87841==ERROR: AddressSanitizer: attempting free on address which was not malloc()-ed: 0x000157178858 in thread T8
#0 0x11081805d in wrap__ZdlPv+0x7d (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x5505d)
#1 0x10ff6a234 in std::__1::_DeallocateCaller::__do_call(void*) new:334
#2 0x10ff6a218 in std::__1::_DeallocateCaller::__do_deallocate_handle_size(void*, unsigned long) new:292
#3 0x156dc1c80 in std::__1::_DeallocateCaller::__do_deallocate_handle_size_align(void*, unsigned long, unsigned long) new:262
#4 0x156dc1c54 in std::__1::__libcpp_deallocate(void*, unsigned long, unsigned long) new:340
#5 0x156e2342d in std::__1::allocator<std::__1::__hash_node<juce::String, void*> >::deallocate(std::__1::__hash_node<juce::String, void*>*, unsigned long) memory:1673
#6 0x156e23374 in std::__1::allocator_traits<std::__1::allocator<std::__1::__hash_node<juce::String, void*> > >::deallocate(std::__1::allocator<std::__1::__hash_node<juce::String, void*> >&, std::__1::__hash_node<juce::String, void*>*, unsigned long) memory:1408
#7 0x156e23264 in std::__1::__hash_table<juce::String, std::__1::hash<juce::String>, std::__1::equal_to<juce::String>, std::__1::allocator<juce::String> >::__deallocate_node(std::__1::__hash_node_base<std::__1::__hash_node<juce::String, void*>*>*) __hash_table:1603
#8 0x156e231c1 in std::__1::__hash_table<juce::String, std::__1::hash<juce::String>, std::__1::equal_to<juce::String>, std::__1::allocator<juce::String> >::~__hash_table() __hash_table:1541
#9 0x156e23184 in std::__1::__hash_table<juce::String, std::__1::hash<juce::String>, std::__1::equal_to<juce::String>, std::__1::allocator<juce::String> >::~__hash_table() __hash_table:1533
#10 0x156e23164 in std::__1::unordered_set<juce::String, std::__1::hash<juce::String>, std::__1::equal_to<juce::String>, std::__1::allocator<juce::String> >::~unordered_set() unordered_set:495
#11 0x156d9ff14 in std::__1::unordered_set<juce::String, std::__1::hash<juce::String>, std::__1::equal_to<juce::String>, std::__1::allocator<juce::String> >::~unordered_set() unordered_set:493
#12 0x156da0118 in juce::AudioProcessor::~AudioProcessor() juce_AudioProcessor.cpp:65
#13 0x156a70bba in TestAudioProcessor::~TestAudioProcessor() Processor.cpp:79

This is from simply calling a function like:

void testCreate()
{
     TestAudioProcessor proc;
}

Is allocating AudioProcessor on the heap creating the same stack trace?

The unordered_set members in AudioProcessor are only enabled when JUCE_DEBUG is on. Are you using a custom build system, JUCE staticlib or similar? I wonder whether some of your TUs are including the AudioProcessor header with conflicting preprocessor definitions.

Just a guess, and I’m not sure whether that would cause the issue you’re seeing - but if AudioProcessor.cpp was built with JUCE_DEBUG, then the generated destructor would try to destroy the unordered_set at a specific offset in the object. If your testCreate function is compiled without JUCE_DEBUG enabled, then the TestAudioProcessor instance will be constructed without the unordered_set members, but the destructor will still attempt to clean up the (non-existent) unordered_sets.

It might be worth looking through the build flags for all of your translation units and checking that they’re all consistent.

2 Likes

Thanks so much for this.

It turns out I had JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING set in one TU but not others and it was throwing the offset out as you described.

All working now. Cheers.

1 Like