Memory leak caused by using ExtensionsVisitor

Hi, JUCE developers.

In the VST3 hosted app I’ m developing, I want to get/set the vst plugin data directly.
So we tried to implement this using ExtensionsVisitor.
An excerpt of the code is below.

class SetVisitor : public ExtensionsVisitor {
   public:
    SetVisitor(const MemoryBlock& data) : data_(data) {}
    ~SetVisitor() {}

    void visitVST3Client(const VST3Client& client) override { client.setPreset(data_); }

   private:
    const MemoryBlock& data_;
};

class GetVisitor : public ExtensionsVisitor {
   public:
    GetVisitor(MemoryBlock& data) : data_(data) {}
    ~GetVisitor() {}

    void visitVST3Client(const VST3Client& client) override { data_ = client.getPreset(); }

   private:
    MemoryBlock& data_;
};

The class defined above is used in this way. (*This is an excerpt)

(omitted)
    if (auto* processor = dynamic_cast<AudioPluginInstance*>( node->getProcessor())) {
        MemoryBlock data;
        GetVisitor visitor(data);
        processor->getExtensions(visitor);
        // Process of referring information (omitted)
    }
(omitted)

    if (auto* processor = dynamic_cast<AudioPluginInstance*>(node->getProcessor())) {
        if (MemoryBlock* data = DataClass.getBinaryData()) {
            SetVisitor visitor(*data);
            processor->getExtensions(visitor);
        }
    }
(omitted)

If I run it with this kind of code, I get a memory leak.
Using the “Visual Leak Detector”( Visual Leak Detector | Enhanced Memory Leak Detection for Visual C++ ) to find the location of the memory leak, we get the following stack trace (*The following is an excerpt)

  Call Stack (TID 9600):
    ucrtbased.dll!malloc()
    d:\a01\_work\20\s\src\vctools\crt\vcstartup\src\heap\new_scalar.cpp (35): VST3HostApp.exe!operator new() + 0xA bytes
    JUCE\modules\juce_audio_processors\format_types\juce_VST3PluginFormat.cpp (2906): VST3HostApp.exe!juce::VST3PluginInstance::getStateForPresetFile() + 0xA bytes
    JUCE\modules\juce_audio_processors\format_types\juce_VST3PluginFormat.cpp (2286): VST3HostApp.exe!`juce::VST3PluginInstance::getExtensions'::`2'::Extensions::getPreset() + 0x29 bytes
    VST3HostApp\Source\AudioController.cpp (42): VST3HostApp.exe!GetVisitor::visitVST3Client() + 0x23 bytes
    JUCE\modules\juce_audio_processors\format_types\juce_VST3PluginFormat.cpp (2296): VST3HostApp.exe!juce::VST3PluginInstance::getExtensions() + 0x33 bytes
    ...

  Call Stack (TID 9600):
    ucrtbased.dll!malloc()
    d:\a01\_work\20\s\src\vctools\crt\vcstartup\src\heap\new_scalar.cpp (35): VST3HostApp.exe!operator new() + 0xA bytes
    JUCE\modules\juce_audio_processors\format_types\juce_VST3PluginFormat.cpp (2925): VST3HostApp.exe!juce::VST3PluginInstance::setStateFromPresetFile() + 0xA bytes
    JUCE\modules\juce_audio_processors\format_types\juce_VST3PluginFormat.cpp (2291): VST3HostApp.exe!`juce::VST3PluginInstance::getExtensions'::`2'::Extensions::setPreset()
    VST3HostApp\Source\AudioController.cpp (31): VST3HostApp.exe!SetVisitor::visitVST3Client() + 0x27 bytes
    JUCE\modules\juce_audio_processors\format_types\juce_VST3PluginFormat.cpp (2296): VST3HostApp.exe!juce::VST3PluginInstance::getExtensions() + 0x33 bytes
    ...

  Call Stack (TID 9600):
    ucrtbased.dll!realloc()
    JUCE\modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\common\memorystream.cpp (228): VST3HostApp.exe!Steinberg::MemoryStream::setSize() + 0x14 bytes
    JUCE\modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\common\memorystream.cpp (132): VST3HostApp.exe!Steinberg::MemoryStream::write() + 0xF bytes
    (Module name unavailable)!0x00007FFEC8FCDDF5()
    JUCE\modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\vstpresetfile.cpp (476): VST3HostApp.exe!Steinberg::Vst::PresetFile::storeComponentState() + 0x35 bytes
    JUCE\modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\vstpresetfile.cpp (110): VST3HostApp.exe!Steinberg::Vst::PresetFile::savePreset() + 0x12 bytes
    JUCE\modules\juce_audio_processors\format_types\juce_VST3PluginFormat.cpp (2911): VST3HostApp.exe!juce::VST3PluginInstance::getStateForPresetFile() + 0x98 bytes
    JUCE\modules\juce_audio_processors\format_types\juce_VST3PluginFormat.cpp (2286): VST3HostApp.exe!`juce::VST3PluginInstance::getExtensions'::`2'::Extensions::getPreset() + 0x29 bytes
    VST3HostApp\Source\AudioController.cpp (42): VST3HostApp.exe!GetVisitor::visitVST3Client() + 0x23 bytes
    JUCE\modules\juce_audio_processors\format_types\juce_VST3PluginFormat.cpp (2296): VST3HostApp.exe!juce::VST3PluginInstance::getExtensions() + 0x33 bytes
    ...

  Call Stack (TID 9600):
    ucrtbased.dll!malloc()
    d:\a01\_work\20\s\src\vctools\crt\vcstartup\src\heap\new_scalar.cpp (35): VST3HostApp.exe!operator new() + 0xA bytes
    JUCE\modules\juce_audio_processors\format_types\juce_VST3PluginFormat.cpp (2906): VST3HostApp.exe!juce::VST3PluginInstance::getStateForPresetFile() + 0xA bytes
    JUCE\modules\juce_audio_processors\format_types\juce_VST3PluginFormat.cpp (2286): VST3HostApp.exe!`juce::VST3PluginInstance::getExtensions'::`2'::Extensions::getPreset() + 0x29 bytes
    VST3HostApp\Source\AudioController.cpp (42): VST3HostApp.exe!GetVisitor::visitVST3Client() + 0x23 bytes
    JUCE\modules\juce_audio_processors\format_types\juce_VST3PluginFormat.cpp (2296): VST3HostApp.exe!juce::VST3PluginInstance::getExtensions() + 0x33 bytes
    ...

Is this a bug in JUCE? Or is it a bad implementation on my part?
I would be glad to get some advice.

Thanks.

Thanks for reporting. This looks like a JUCE bug, we’ll put out a fix shortly.

1 Like

After modifying the JUCE code as follows, the memory leak does not occur anymore.
I hope this is helpful.

<juce_VST3PluginFormat.cpp>

MemoryBlock getStateForPresetFile() const
{
    Steinberg::MemoryStream memoryStream; // change to not use VSTComSmartPtr

    if (holder->component == nullptr)
        return {};

    const auto saved = Steinberg::Vst::PresetFile::savePreset (&memoryStream,
                                                               holder->cidOfComponent,
                                                               holder->component,
                                                               editController);

    if (saved)
        return { memoryStream.getData(), static_cast<size_t> (memoryStream.getSize()) };

    return {};
}

bool setStateFromPresetFile (const MemoryBlock& rawData) const
{
    MemoryBlock rawDataCopy (rawData);
    Steinberg::MemoryStream memoryStream(rawDataCopy.getData(), (int) rawDataCopy.getSize()); // change to not use VSTComSmartPtr

    if (holder->component == nullptr)
        return false;

    return Steinberg::Vst::PresetFile::loadPreset (&memoryStream, holder->cidOfComponent,
                                                   holder->component, editController, nullptr);
}

Thanks for reporting. The issue is fixed on develop:

1 Like