|
12-24-2010, 06:36 AM
|
#1
|
Human being with feelings
Join Date: Apr 2009
Location: Berlin, Germany
Posts: 1,248
|
IPlug AU leaks
running apple's leaks test on the iplug example compiled from the cockos wdl shows that it is leaking memory (albeit not much) (see attached txt file). This seems to be due to problems in IPlugAU::GetProperty().
It looks like strings allocated with MakeCFString() are not getting released, amongst other things.
see:
http://developer.apple.com/library/m...R_MEMORY_LEAKS
I'm investigating the problem, but wondered if anyone else has looked at it?
merry christmas,
oli
|
|
|
12-24-2010, 11:30 AM
|
#2
|
Human being with feelings
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
|
I also recently "discovered" the AU Validation Tool. When I first ran it, the tool reported similar memory leaks (on my ComboV project). I don't remember why, but I initially also thought the problem was IPlug. However, it turned out to be code in my project, not in IPlug itself. Now that I have fixed my code, my project passes all of auval's tests.
Happy holidays!
|
|
|
12-24-2010, 11:58 AM
|
#3
|
Human being with feelings
Join Date: Apr 2009
Location: Berlin, Germany
Posts: 1,248
|
Just to clarify - When you say it passed all auvals tests, did you do the
Specific leaks test explained in the link above?
|
|
|
12-24-2010, 01:27 PM
|
#4
|
Human being with feelings
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
|
Quote:
Originally Posted by olilarkin
Just to clarify - When you say it passed all auvals tests, did you do the
Specific leaks test explained in the link above?
|
Clarification was badly needed, because I only did the standard auval tests...
Anyway, I have just did the leaks test, and yeah, leaking all over the place. I have also checked two of Apple's own plug-ins, no leaks at all. However, I then checked Pianoteq Play, and it leaks just as much as ComboV/IPlug.
|
|
|
12-25-2010, 03:49 PM
|
#5
|
Human being with feelings
Join Date: Apr 2008
Posts: 510
|
Quote:
Originally Posted by olilarkin
leaks.txt
|
i'm not very familiar with WDL/IPlug or AU's (and Macs) in general, but on a brief look at the log and IPlugAU.h i think there are 3 types of small leaks here:
1)
instead of using MakeCFString(), CFStrLocal should be used imo, for example:
Code:
CFStrLocal CF_cStr(cStr);
pIDName->outName = CF_cStr.mCFStr;
this way, when out of scope the strings will be released with CFRelease() - hopefully..
2)
despite the above idea it seems that mRenderNotify and mPropertyListeners lists are not being cleared with the IPlugAU destructor.
or at least why WDL_PtrList.Empty() does not clear the allocated "pNew" in PtrListAddFromStack() as expected...hmm
3)
it looks like the presetArray (CFArrayCreateMutable()) is not being released. the return can be stored in a private member instead:
Code:
// IPlugAU.h
// class IPlugAU
private:
CFMutableArrayRef mPresetArray;
// IPlugAU.cpp
// IPlugAU::GetProperty()
// case 24:
mPresetArray = CFArrayCreateMutable(0, n, 0);
// ~IPlugAU
CFRelease(mPresetArray); // this _should_ iterate/clear all
i'm not sure how "auvaltool" works exactly and how well does it "wraps" everything up, but mind that such non-local leak detectors may give some false positives and/or miss something.
happy holidays! :-)
---
Last edited by liteon; 12-25-2010 at 06:57 PM.
Reason: typos
|
|
|
12-26-2010, 02:38 AM
|
#6
|
Human being with feelings
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
|
According to Apple's documentation the host is responsible for releasing most pointers. However, when Apple says the host should release an array, I guess it does not mean the host will release the pointers inside each of the items in the array, right? So anyway, changing MakeCFString() to CFStrLocal worked for the presets; I don't see my preset names anymore in the leaks report.
EDIT: Belay that, CFStrLocal doesn't work. In fact it crashes the AU Validation Tool (unless you specify -q, which I did earlier).
Leaks now only reports loads of 16-byte blocks, so I guess I will have to investigate the WDL_PtrList stuff now.
BTW, I (now?) also get a warning from the AU Validaton Tool itself, saying "Preset name is not retained in retrieved class data". I haven't yet looked into this though.
Last edited by Tale; 12-26-2010 at 04:36 AM.
|
|
|
12-26-2010, 07:06 AM
|
#7
|
Administrator
Join Date: Mar 2007
Location: NY
Posts: 15,823
|
I haven't looked specifically at this, but I do recall that it used to be poorly defined whether the plugin or the host owned some strings or structures, and there was at least one flag added to the AU spec at a later point for the plugin to tell the host who should be responsible for freeing something (sorry to be vague, that's all I remember without digging into it). These are probably real leaks (the plugin not freeing structures that it should) but it's also possible AUVal, which is an old tool, isn't cleaning up properly.
|
|
|
12-26-2010, 12:48 PM
|
#8
|
Human being with feelings
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
|
Quote:
Originally Posted by schwa
I haven't looked specifically at this, but I do recall that it used to be poorly defined whether the plugin or the host owned some strings or structures, and there was at least one flag added to the AU spec at a later point for the plugin to tell the host who should be responsible for freeing something (sorry to be vague, that's all I remember without digging into it).
|
That would be kAudioUnitParameterFlag_CFNameRelease, which tells the host to release the parameter name and unit.
|
|
|
10-09-2011, 07:12 AM
|
#9
|
Human being with feelings
Join Date: Apr 2009
Location: Berlin, Germany
Posts: 1,248
|
tale - did you manage to fix these leaks?
|
|
|
10-09-2011, 12:41 PM
|
#10
|
Human being with feelings
Join Date: Apr 2009
Location: Berlin, Germany
Posts: 1,248
|
i managed to get rid of the leaks for preset names, with a combination of the dfx au preset utilites and CFSTRLocal instead of MakeCFString()
http://destroyfx.smartelectronix.com...rray_callbacks
but then I realised that all the enum parameter list items were also leaking. I couldn't fix that
bah... will stick with the leaks for now
|
|
|
10-09-2011, 04:03 PM
|
#11
|
Human being with feelings
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
|
Quote:
Originally Posted by olilarkin
tale - did you manage to fix these leaks?
|
No, I kinda gave up on it.
|
|
|
10-10-2011, 02:51 PM
|
#12
|
Human being with feelings
Join Date: Apr 2009
Location: Berlin, Germany
Posts: 1,248
|
here's how I managed to stop the preset name leaks using the dfx-au-utilites
Code:
case kAudioUnitProperty_FactoryPresets: { // 24, // listenable
*pDataSize = sizeof(CFArrayRef);
if (pData)
{
int i, n = NPresets();
CFMutableArrayRef presetArray = CFArrayCreateMutable(kCFAllocatorDefault, n, &kCFAUPresetArrayCallBacks);
if (presetArray == NULL)
return coreFoundationUnknownErr;
for (i = 0; i < n; ++i)
{
CFStrLocal presetName = CFStrLocal(GetPresetName(i));
CFAUPresetRef newPreset = CFAUPresetCreate(kCFAllocatorDefault, i, presetName.mCFStr); // todo should id be 0 based?
if (newPreset != NULL)
{
CFArrayAppendValue(presetArray, newPreset);
CFAUPresetRelease(newPreset);
}
}
*((CFMutableArrayRef*) pData) = presetArray;
// CFMutableArrayRef presetArray = CFArrayCreateMutable(0, n, 0);
// for (i = 0; i < n; ++i) {
// AUPreset* pAUPreset = new AUPreset;
// pAUPreset->presetNumber = i;
// pAUPreset->presetName = MakeCFString(GetPresetName(i));
// CFArrayAppendValue(presetArray, pAUPreset);
// }
// *((CFMutableArrayRef*) pData) = presetArray;
}
return noErr;
I still have similar errors for every enumerated list item though.
can't say i really understand the dfx stuff right now.
oli
|
|
|
10-10-2011, 04:14 PM
|
#13
|
Human being with feelings
Join Date: Apr 2009
Location: Berlin, Germany
Posts: 1,248
|
Code:
case kAudioUnitProperty_ParameterValueStrings: { // 16,
ASSERT_SCOPE(kAudioUnitScope_Global);
IParam* pParam = GetParam(element);
int n = pParam->GetNDisplayTexts();
if (!n) {
*pDataSize = 0;
return kAudioUnitErr_InvalidProperty;
}
*pDataSize = sizeof(CFArrayRef);
if (pData) {
CFMutableArrayRef nameArray = CFArrayCreateMutable(0, n, 0);
for (int i = 0; i < n; ++i) {
const char* str = pParam->GetDisplayText(i);
CFArrayAppendValue(nameArray, MakeCFString(str)); // leaks
}
*((CFArrayRef*) pData) = nameArray;
}
return noErr;
}
narrowed it down to this one which is the other MakeCFString() that leaks
|
|
|
10-10-2011, 11:26 PM
|
#14
|
Human being with feelings
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
|
Quote:
Originally Posted by olilarkin
can't say i really understand the dfx stuff right now.
|
Neither do I. However, this is how I understand it so far: The default call backs for CFMutableArrayRef don't releasing some of the elements in the array, because these elements are not CFType, so it can't call CFRelease() on them. The solution would be to use your own call backs, that do properly release all elements.
|
|
|
10-11-2011, 01:56 AM
|
#15
|
Human being with feelings
Join Date: Apr 2009
Location: Berlin, Germany
Posts: 1,248
|
OK.
I'm not sure why the CFStrings in kAudioUnitProperty_ParameterValueStrings are not released though... since it is not a CFArray of a non-CFType this time, i thought it would be ok.
by the way - the way i isolated the leak to kAudioUnitProperty_ParameterValueStrings was to change MakeCFString() to CFSTR("test"). When I did that the number of leaks dropped to two which are the ones liteon mentioned in point 2) of his post above.
there is a bit more information about the issue here...
http://lists.apple.com/archives/core.../msg00019.html (read whole thread)
Last edited by olilarkin; 10-11-2011 at 02:51 AM.
|
|
|
10-11-2011, 08:13 AM
|
#16
|
Human being with feelings
Join Date: Apr 2009
Location: Berlin, Germany
Posts: 1,248
|
ok fixed it!
Code:
case kAudioUnitProperty_ParameterValueStrings: { // 16,
ASSERT_SCOPE(kAudioUnitScope_Global);
IParam* pParam = GetParam(element);
int n = pParam->GetNDisplayTexts();
if (!n) {
*pDataSize = 0;
return kAudioUnitErr_InvalidProperty;
}
*pDataSize = sizeof(CFArrayRef);
if (pData) {
CFMutableArrayRef nameArray = CFArrayCreateMutable(kCFAllocatorDefault, n, &kCFTypeArrayCallBacks);
for (int i = 0; i < n; ++i) {
const char* str = pParam->GetDisplayText(i);
CFStrLocal cfstr = CFStrLocal(str);
CFArrayAppendValue(nameArray, cfstr.mCFStr);
}
*((CFArrayRef*) pData) = nameArray;
}
return noErr;
}
|
|
|
10-11-2011, 09:44 AM
|
#17
|
Human being with feelings
Join Date: Apr 2009
Location: Berlin, Germany
Posts: 1,248
|
so only 2 leaks left
Code:
Process 2638: 2 leaks for 32 total leaked bytes.
Leak: 0x1866c0 size=16 zone: DefaultMallocZone_0x74000
0x00053afc 0xc004f4b8 0x6c084c50 0x00189bc0 .:......PL.l....
Call stack: [thread 0xac9282c0]: | 0x520e1 | 0x5338b | 0x5300c | 0x607b2 | 0x57aa0 | _AT_AudioUnitAddRenderNotify | CallComponentDispatch | CallComponent | EndlessSeries_Entry | IPlugAU::IPlugAUEntry(ComponentParameters*, void*) | int PtrListAddFromStack<AURenderCallbackStruct>(WDL_PtrList<AURenderCallbackStruct>*, AURenderCallbackStruct*) | operator new(unsigned long) | malloc | malloc_zone_malloc
Leak: 0x189140 size=16 zone: DefaultMallocZone_0x74000
0x0000000e 0x00053730 0xc004f2a3 0x00000000 ....07..........
Call stack: [thread 0xac9282c0]: | 0x520e1 | 0x5338b | 0x5300c | 0x6044e | 0x5ced9 | _AT_AudioUnitAddPropertyListener | CallComponentDispatch | CallComponent | EndlessSeries_Entry | IPlugAU::IPlugAUEntry(ComponentParameters*, void*) | int PtrListAddFromStack<IPlugAU::PropertyListener>(WDL_PtrList<IPlugAU::PropertyListener>*, IPlugAU::PropertyListener*) | operator new(unsigned long) | malloc | malloc_zone_malloc
|
|
|
10-11-2011, 11:55 AM
|
#18
|
Human being with feelings
Join Date: Apr 2008
Posts: 510
|
you could test if WDL_PtrList::Empty is clearing everything correctly (which i'm pretty sure is). if yes, then perhaps the leak detector tool leaves a false positive.
Code:
// in ptrlist.h: WDL_PtrList::Empty
if (wantDelete)
{
// .......
}
// comment out the HB resize to 0 here for this test
// m_hb.Resize(0, false);
Code:
IPlugAU::~IPlugAU()
{
int sz;
PropertyListener p;
mRenderNotify.Empty(true);
mInBuses.Empty(true);
mOutBuses.Empty(true);
mInBusConnections.Empty(true);
mPropertyListeners.Empty(true);
sz = mPropertyListeners.GetSize();
Trace(TRACELOC, "~IPlugAU : mPropertyListeners.GetSize() : %d", sz);
if (sz == 1) // assuming one element leaks here (16 bytes ?)
{
p = mPropertyListeners.Get(0);
if (p)
delete p;
}
// same for mRenderNotify
}
--
|
|
|
10-11-2011, 01:11 PM
|
#19
|
Human being with feelings
Join Date: Apr 2009
Location: Berlin, Germany
Posts: 1,248
|
thanks. I did exactly what you said and the two leaks remain. So i suppose it's a false positive?
oli
|
|
|
10-11-2011, 05:06 PM
|
#20
|
Human being with feelings
Join Date: Apr 2008
Posts: 510
|
this might be the case, but i'm not certain. i think the odd part here is the reported size of the leaks.
the tool reports operator "new" from PtrListAddFromStack (which is then implemented with malloc / malloc_zone_malloc), but for both cases - PropertyListener and AURenderCallbackStruct, the allocated size for an object (template C) should be more than 16 bytes - e.g. sizeof(PropertyListener)
WDL_HEAPBUF_TRACE could be enabled (+ a trace/output modification in heapbuf.h for osx/linux) to monitor what's happening behind WDL_PtrList, but i doubt it will show anything out of the ordinary, since i think this code is tested well and used a lot.
--
|
|
|
10-11-2011, 11:28 PM
|
#21
|
Human being with feelings
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
|
Quote:
Originally Posted by liteon
but for both cases - PropertyListener and AURenderCallbackStruct, the allocated size for an object (template C) should be more than 16 bytes - e.g. sizeof(PropertyListener)
|
I don't know about PropertyListener, but AURenderCallbackStruct holds only two pointers, so its size would probably be 8 bytes on 32-bit, and 16 bytes on 64-bit.
|
|
|
10-12-2011, 01:50 AM
|
#22
|
Human being with feelings
Join Date: Apr 2009
Location: Berlin, Germany
Posts: 1,248
|
here are the leaks reports for 32 and 64 bit versions
32-bit:
Code:
leaks Report Version: 2.0
Process: auvaltool [488]
Path: /usr/bin/auvaltool
Load Address: 0xb8000
Identifier: auvaltool
Version: ??? (???)
Code Type: X86 (Native)
Parent Process: sh [487]
Date/Time: 2011-10-12 09:46:09.251 +0100
OS Version: Mac OS X 10.7.1 (11B26)
Report Version: 7
Process 488: 6976 nodes malloced for 3590 KB
Process 488: 2 leaks for 32 total leaked bytes.
Leak: 0x196ce0 size=16 zone: DefaultMallocZone_0xdb000
0x000baafc 0xc00b6498 0x00189460 0x6b26f640 .....d..`...@.&k
Call stack: [thread 0xac9282c0]: | 0xb90e1 | 0xba38b | 0xba00c | 0xc77b2 | 0xbeaa0 | _AT_AudioUnitAddRenderNotify | CallComponentDispatch | CallComponent | IPlugAU::IPlugAUEntry(ComponentParameters*, void*) | int PtrListAddFromStack<AURenderCallbackStruct>(WDL_PtrList<AURenderCallbackStruct>*, AURenderCallbackStruct*) | operator new(unsigned long) | malloc | malloc_zone_malloc
Leak: 0x6b26db40 size=16 zone: DefaultMallocZone_0xdb000
0x0000000e 0x000ba730 0xc00b6283 0x00746e65 ....0....b..ent.
Call stack: [thread 0xac9282c0]: | 0xb90e1 | 0xba38b | 0xba00c | 0xc744e | 0xc3ed9 | _AT_AudioUnitAddPropertyListener | CallComponentDispatch | CallComponent | IPlugAU::IPlugAUEntry(ComponentParameters*, void*) | int PtrListAddFromStack<IPlugAU::PropertyListener>(WDL_PtrList<IPlugAU::PropertyListener>*, IPlugAU::PropertyListener*) | operator new(unsigned long) | malloc | malloc_zone_malloc
64-bit:
Code:
leaks Report Version: 2.0
Process: auvaltool [518]
Path: /usr/bin/auvaltool
Load Address: 0x10c388000
Identifier: auvaltool
Version: ??? (???)
Code Type: X86-64 (Native)
Parent Process: sh [517]
Date/Time: 2011-10-12 09:48:14.396 +0100
OS Version: Mac OS X 10.7.1 (11B26)
Report Version: 7
Process 518: 7255 nodes malloced for 4069 KB
Process 518: 2 leaks for 48 total leaked bytes.
Leak: 0x10c43a9e0 size=32 zone: DefaultMallocZone_0x10c3aa000
0x0000000e 0x00007fff 0x0c38aa58 0x00000001 ........X.8.....
0x6bf8707f 0x00007fff 0x72745320 0x00026165 .p.k.... Strea..
Call stack: [thread 0x7fff76ad4960]: | 0x7 | 0x10c3895a4 | 0x10c38a71b | 0x10c38a3f5 | 0x10c395615 | 0x10c392687 | _AT_AudioUnitAddPropertyListener | IPlugAU::IPlugAUEntry(ComponentParameters*, void*) | int PtrListAddFromStack<IPlugAU::PropertyListener>(WDL_PtrList<IPlugAU::PropertyListener>*, IPlugAU::PropertyListener*) | operator new(unsigned long) | malloc | malloc_zone_malloc
Leak: 0x10c40a000 size=16 zone: DefaultMallocZone_0x10c3aa000
0x0c38ae36 0x00000001 0x6bf872b8 0x00007fff 6.8......r.k....
Call stack: [thread 0x7fff76ad4960]: | 0x7 | 0x10c3895a4 | 0x10c38a71b | 0x10c38a3f5 | 0x10c395885 | 0x10c38e1e4 | _AT_AudioUnitAddRenderNotify | IPlugAU::IPlugAUEntry(ComponentParameters*, void*) | int PtrListAddFromStack<AURenderCallbackStruct>(WDL_PtrList<AURenderCallbackStruct>*, AURenderCallbackStruct*) | operator new(unsigned long) | malloc | malloc_zone_malloc
|
|
|
10-12-2011, 06:04 AM
|
#23
|
Human being with feelings
Join Date: Apr 2008
Posts: 510
|
Quote:
Originally Posted by Tale
I don't know about PropertyListener, but AURenderCallbackStruct holds only two pointers, so its size would probably be 8 bytes on 32-bit, and 16 bytes on 64-bit.
|
ah yes,
not sure why i assumed these are larger objects.
both seem to only hold some pointers...
but the leak reports still don't make much sense i think.
i would try passing "delfunc" (containing free) to WDL_PtrList::
void Delete(int index, bool wantDelete=false, void (*delfunc)(void *)=NULL)
void Empty(bool wantDelete=false, void (*delfunc)(void *)=NULL)
, then modify IPlugAU.cpp to use the malloc/free scheme and perhaps also add some sparse memory allocations in there to see what the tool will report.
but i can't really guess further without compiling.
--
|
|
|
10-18-2011, 03:51 AM
|
#24
|
Human being with feelings
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
|
I haven't really had the time to look into this until now, but I think I have fixed the last two leaks. The pointers in the mPropertyListeners and mRenderNotify lists are removed from the lists, but they are not deallocated. To fix this find these three lines:
Code:
_this->mPropertyListeners.Delete(i);
_this->mPropertyListeners.Delete(i);
_this->mRenderNotify.Delete(i);
And change them into:
Code:
_this->mPropertyListeners.Delete(i, true);
_this->mPropertyListeners.Delete(i, true);
_this->mRenderNotify.Delete(i, true);
|
|
|
10-18-2011, 04:09 AM
|
#25
|
Human being with feelings
Join Date: Apr 2009
Location: Berlin, Germany
Posts: 1,248
|
yey! well done tale!
|
|
|
10-18-2011, 04:55 AM
|
#26
|
Human being with feelings
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
|
And a very well done Oli for already fixing the other leaks.
|
|
|
Thread Tools |
|
Display Modes |
Linear Mode
|
Posting Rules
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is Off
|
|
|
All times are GMT -7. The time now is 03:42 PM.
|