COCKOS
CONFEDERATED FORUMS
Cockos : REAPER : NINJAM : Forums
Forum Home : Register : FAQ : Members List : Search :
Old 12-24-2010, 06:36 AM   #1
olilarkin
Human being with feelings
 
Join Date: Apr 2009
Location: Berlin, Germany
Posts: 1,248
Default 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
Attached Files
File Type: txt leaks.txt (38.3 KB, 885 views)
__________________
VirtualCZ | Endless Series | iPlug2 | Linkedin | Facebook
olilarkin is offline   Reply With Quote
Old 12-24-2010, 11:30 AM   #2
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
Default

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!
Tale is offline   Reply With Quote
Old 12-24-2010, 11:58 AM   #3
olilarkin
Human being with feelings
 
Join Date: Apr 2009
Location: Berlin, Germany
Posts: 1,248
Default

Just to clarify - When you say it passed all auvals tests, did you do the
Specific leaks test explained in the link above?
__________________
VirtualCZ | Endless Series | iPlug2 | Linkedin | Facebook
olilarkin is offline   Reply With Quote
Old 12-24-2010, 01:27 PM   #4
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
Default

Quote:
Originally Posted by olilarkin View Post
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.
Tale is offline   Reply With Quote
Old 12-25-2010, 03:49 PM   #5
liteon
Human being with feelings
 
liteon's Avatar
 
Join Date: Apr 2008
Posts: 510
Default

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
liteon is offline   Reply With Quote
Old 12-26-2010, 02:38 AM   #6
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
Default

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.
Tale is offline   Reply With Quote
Old 12-26-2010, 07:06 AM   #7
schwa
Administrator
 
schwa's Avatar
 
Join Date: Mar 2007
Location: NY
Posts: 15,823
Default

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.
schwa is offline   Reply With Quote
Old 12-26-2010, 12:48 PM   #8
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
Default

Quote:
Originally Posted by schwa View Post
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.
Tale is offline   Reply With Quote
Old 10-09-2011, 07:12 AM   #9
olilarkin
Human being with feelings
 
Join Date: Apr 2009
Location: Berlin, Germany
Posts: 1,248
Default

tale - did you manage to fix these leaks?
__________________
VirtualCZ | Endless Series | iPlug2 | Linkedin | Facebook
olilarkin is offline   Reply With Quote
Old 10-09-2011, 12:41 PM   #10
olilarkin
Human being with feelings
 
Join Date: Apr 2009
Location: Berlin, Germany
Posts: 1,248
Default

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
__________________
VirtualCZ | Endless Series | iPlug2 | Linkedin | Facebook
olilarkin is offline   Reply With Quote
Old 10-09-2011, 04:03 PM   #11
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
Default

Quote:
Originally Posted by olilarkin View Post
tale - did you manage to fix these leaks?
No, I kinda gave up on it.
Tale is offline   Reply With Quote
Old 10-10-2011, 02:51 PM   #12
olilarkin
Human being with feelings
 
Join Date: Apr 2009
Location: Berlin, Germany
Posts: 1,248
Default

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
__________________
VirtualCZ | Endless Series | iPlug2 | Linkedin | Facebook
olilarkin is offline   Reply With Quote
Old 10-10-2011, 04:14 PM   #13
olilarkin
Human being with feelings
 
Join Date: Apr 2009
Location: Berlin, Germany
Posts: 1,248
Default

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
__________________
VirtualCZ | Endless Series | iPlug2 | Linkedin | Facebook
olilarkin is offline   Reply With Quote
Old 10-10-2011, 11:26 PM   #14
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
Default

Quote:
Originally Posted by olilarkin View Post
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.
Tale is offline   Reply With Quote
Old 10-11-2011, 01:56 AM   #15
olilarkin
Human being with feelings
 
Join Date: Apr 2009
Location: Berlin, Germany
Posts: 1,248
Default

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)
__________________
VirtualCZ | Endless Series | iPlug2 | Linkedin | Facebook

Last edited by olilarkin; 10-11-2011 at 02:51 AM.
olilarkin is offline   Reply With Quote
Old 10-11-2011, 08:13 AM   #16
olilarkin
Human being with feelings
 
Join Date: Apr 2009
Location: Berlin, Germany
Posts: 1,248
Default

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;
    }
__________________
VirtualCZ | Endless Series | iPlug2 | Linkedin | Facebook
olilarkin is offline   Reply With Quote
Old 10-11-2011, 09:44 AM   #17
olilarkin
Human being with feelings
 
Join Date: Apr 2009
Location: Berlin, Germany
Posts: 1,248
Default

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
__________________
VirtualCZ | Endless Series | iPlug2 | Linkedin | Facebook
olilarkin is offline   Reply With Quote
Old 10-11-2011, 11:55 AM   #18
liteon
Human being with feelings
 
liteon's Avatar
 
Join Date: Apr 2008
Posts: 510
Default

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
}
--
liteon is offline   Reply With Quote
Old 10-11-2011, 01:11 PM   #19
olilarkin
Human being with feelings
 
Join Date: Apr 2009
Location: Berlin, Germany
Posts: 1,248
Default

thanks. I did exactly what you said and the two leaks remain. So i suppose it's a false positive?

oli
__________________
VirtualCZ | Endless Series | iPlug2 | Linkedin | Facebook
olilarkin is offline   Reply With Quote
Old 10-11-2011, 05:06 PM   #20
liteon
Human being with feelings
 
liteon's Avatar
 
Join Date: Apr 2008
Posts: 510
Default

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.



--
liteon is offline   Reply With Quote
Old 10-11-2011, 11:28 PM   #21
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
Default

Quote:
Originally Posted by liteon View Post
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.
Tale is offline   Reply With Quote
Old 10-12-2011, 01:50 AM   #22
olilarkin
Human being with feelings
 
Join Date: Apr 2009
Location: Berlin, Germany
Posts: 1,248
Default

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
__________________
VirtualCZ | Endless Series | iPlug2 | Linkedin | Facebook
olilarkin is offline   Reply With Quote
Old 10-12-2011, 06:04 AM   #23
liteon
Human being with feelings
 
liteon's Avatar
 
Join Date: Apr 2008
Posts: 510
Default

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.


--
liteon is offline   Reply With Quote
Old 10-18-2011, 03:51 AM   #24
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
Default

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);
Tale is offline   Reply With Quote
Old 10-18-2011, 04:09 AM   #25
olilarkin
Human being with feelings
 
Join Date: Apr 2009
Location: Berlin, Germany
Posts: 1,248
Default

yey! well done tale!
__________________
VirtualCZ | Endless Series | iPlug2 | Linkedin | Facebook
olilarkin is offline   Reply With Quote
Old 10-18-2011, 04:55 AM   #26
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,653
Default

And a very well done Oli for already fixing the other leaks.
Tale is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 03:42 PM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.