Add support for collectible types to SOS (#19842)
authorJan Vorlicek <janvorli@microsoft.com>
Thu, 6 Sep 2018 08:15:21 +0000 (10:15 +0200)
committerGitHub <noreply@github.com>
Thu, 6 Sep 2018 08:15:21 +0000 (10:15 +0200)
* Add support for collectible types to SOS

Collectible types indirectly reference managed LoaderAllocator via
pointer to native AssemblyLoaderAllocator stored in their MethodTable.
GC uses this relation when scanning object graph to determine which
objects are rooted and which ones are not.
The gcroot command in SOS doesn't understand this relation and so it
is unable to find all roots for LoaderAllocator.
This change fixes it.

* PR feedback

Make the failure to get the collectible info non-fatal to make it
compatible with older runtimes.

12 files changed:
src/ToolBox/SOS/Strike/eeheap.cpp
src/ToolBox/SOS/Strike/gcroot.cpp
src/ToolBox/SOS/Strike/sos.cpp
src/ToolBox/SOS/Strike/sos.h
src/ToolBox/SOS/Strike/util.h
src/debug/daccess/daccess.cpp
src/debug/daccess/dacimpl.h
src/debug/daccess/request.cpp
src/inc/dacprivate.h
src/inc/sospriv.idl
src/pal/prebuilt/idl/sospriv_i.cpp
src/pal/prebuilt/inc/sospriv.h

index 5a5680f..c28c964 100644 (file)
@@ -868,8 +868,7 @@ DWORD GetNumComponents(TADDR obj)
     return Value;
 }
 
-BOOL GetSizeEfficient(DWORD_PTR dwAddrCurrObj, 
-    DWORD_PTR dwAddrMethTable, BOOL bLarge, size_t& s, BOOL& bContainsPointers)
+static MethodTableInfo* GetMethodTableInfo(DWORD_PTR dwAddrMethTable)
 {
     // Remove lower bits in case we are in mark phase
     dwAddrMethTable = dwAddrMethTable & ~3;
@@ -880,12 +879,34 @@ BOOL GetSizeEfficient(DWORD_PTR dwAddrCurrObj,
         // from the target
         DacpMethodTableData dmtd;
         // see code:ClrDataAccess::RequestMethodTableData for details
-        if (dmtd.Request(g_sos,dwAddrMethTable) != S_OK)
-            return FALSE;
+        if (dmtd.Request(g_sos, dwAddrMethTable) != S_OK)
+            return NULL;
+
 
         info->BaseSize = dmtd.BaseSize;
         info->ComponentSize = dmtd.ComponentSize;
         info->bContainsPointers = dmtd.bContainsPointers;
+
+        // The following request doesn't work on older runtimes. For those, the
+        // objects would just look like non-collectible, which is acceptable.
+        DacpMethodTableCollectibleData dmtcd;
+        if (SUCCEEDED(dmtcd.Request(g_sos, dwAddrMethTable)))
+        {
+            info->bCollectible = dmtcd.bCollectible;
+            info->LoaderAllocatorObjectHandle = TO_TADDR(dmtcd.LoaderAllocatorObjectHandle);
+        }
+    }
+
+    return info;
+}
+
+BOOL GetSizeEfficient(DWORD_PTR dwAddrCurrObj, 
+    DWORD_PTR dwAddrMethTable, BOOL bLarge, size_t& s, BOOL& bContainsPointers)
+{
+    MethodTableInfo* info = GetMethodTableInfo(dwAddrMethTable);
+    if (info == NULL)
+    {
+        return FALSE;
     }
         
     bContainsPointers = info->bContainsPointers;
@@ -911,6 +932,20 @@ BOOL GetSizeEfficient(DWORD_PTR dwAddrCurrObj,
     return TRUE;
 }
 
+BOOL GetCollectibleDataEfficient(DWORD_PTR dwAddrMethTable, BOOL& bCollectible, TADDR& loaderAllocatorObjectHandle)
+{
+    MethodTableInfo* info = GetMethodTableInfo(dwAddrMethTable);
+    if (info == NULL)
+    {
+        return FALSE;
+    }
+
+    bCollectible = info->bCollectible;
+    loaderAllocatorObjectHandle = info->LoaderAllocatorObjectHandle;
+
+    return TRUE;
+}
+
 // This function expects stat to be valid, and ready to get statistics.
 void GatherOneHeapFinalization(DacpGcHeapDetails& heapDetails, HeapStat *stat, BOOL bAllReady, BOOL bShort)
 {
index e426220..cd13719 100644 (file)
@@ -1009,7 +1009,7 @@ GCRootImpl::RootNode *GCRootImpl::GetGCRefs(RootNode *path, RootNode *node)
 
     // Only calculate the size if we need it.
     size_t objSize = 0;
-    if (mSize || node->MTInfo->ContainsPointers)
+    if (mSize || node->MTInfo->ContainsPointers || node->MTInfo->Collectible)
     {
         objSize = GetSizeOfObject(obj, node->MTInfo);
         
@@ -1027,7 +1027,7 @@ GCRootImpl::RootNode *GCRootImpl::GetGCRefs(RootNode *path, RootNode *node)
     }
     
     // Early out:  If the object doesn't contain any pointers, return.
-    if (!node->MTInfo->ContainsPointers)
+    if (!node->MTInfo->ContainsPointers && !node->MTInfo->Collectible)
         return NULL;
     
     // Make sure we have the object's data in the cache.
@@ -1139,6 +1139,15 @@ GCRootImpl::MTInfo *GCRootImpl::GetMTInfo(TADDR mt)
     curr->ComponentSize = (size_t)dmtd.ComponentSize;
     curr->ContainsPointers = dmtd.bContainsPointers ? true : false;
 
+    // The following request doesn't work on older runtimes. For those, the
+    // objects would just look like non-collectible, which is acceptable.
+    DacpMethodTableCollectibleData dmtcd;
+    if (SUCCEEDED(dmtcd.Request(g_sos, mt)))
+    {
+        curr->Collectible = dmtcd.bCollectible ? true : false;
+        curr->LoaderAllocatorObjectHandle = TO_TADDR(dmtcd.LoaderAllocatorObjectHandle);
+    }
+
     // If this method table contains pointers, fill out and cache the GCDesc.
     if (curr->ContainsPointers)
     {
index 64ee4b9..5ff77a9 100644 (file)
@@ -180,6 +180,15 @@ namespace sos
             info->BaseSize = mMTData->BaseSize;
             info->ComponentSize = mMTData->ComponentSize;
             info->bContainsPointers = mMTData->bContainsPointers;
+
+            // The following request doesn't work on older runtimes. For those, the
+            // objects would just look like non-collectible, which is acceptable.
+            DacpMethodTableCollectibleData mtcd;
+            if (SUCCEEDED(mtcd.Request(g_sos, GetMT())))
+            {
+                info->bCollectible = mtcd.bCollectible;
+                info->LoaderAllocatorObjectHandle = TO_TADDR(mtcd.LoaderAllocatorObjectHandle);
+            }
         }
         
         if (mSize == (size_t)~0)
@@ -380,14 +389,14 @@ namespace sos
 
 
     RefIterator::RefIterator(TADDR obj, LinearReadCache *cache)
-        : mCache(cache), mGCDesc(0), mArrayOfVC(false), mDone(false), mBuffer(0), mCurrSeries(0),
+        : mCache(cache), mGCDesc(0), mArrayOfVC(false), mDone(false), mBuffer(0), mCurrSeries(0), mLoaderAllocatorObjectHandle(0),
           i(0), mCount(0), mCurr(0), mStop(0), mObject(obj), mObjSize(0)
     {
         Init();
     }
 
     RefIterator::RefIterator(TADDR obj, CGCDesc *desc, bool arrayOfVC, LinearReadCache *cache)
-        : mCache(cache), mGCDesc(desc), mArrayOfVC(arrayOfVC), mDone(false), mBuffer(0), mCurrSeries(0),
+        : mCache(cache), mGCDesc(desc), mArrayOfVC(arrayOfVC), mDone(false), mBuffer(0), mCurrSeries(0), mLoaderAllocatorObjectHandle(0),
           i(0), mCount(0), mCurr(0), mStop(0), mObject(obj), mObjSize(0)
     {
         Init();
@@ -403,6 +412,13 @@ namespace sos
     {
         if (mDone)
             Throw<Exception>("Attempt to move past the end of the iterator.");
+
+        if (mCurr == mLoaderAllocatorObjectHandle)
+        {
+            // The mLoaderAllocatorObjectHandle is always the last reference returned
+            mDone = true;
+            return *this;
+        }
         
         if (!mArrayOfVC)
         {
@@ -440,6 +456,14 @@ namespace sos
                 mDone = true;
         }
         
+        if (mDone && mLoaderAllocatorObjectHandle != NULL)
+        {
+            // The iteration over all regular object references is done, but there is one more
+            // reference for collectible types - the LoaderAllocator for GC
+            mCurr = mLoaderAllocatorObjectHandle;
+            mDone = false;
+        }
+
         return *this;
     }
     
@@ -457,66 +481,89 @@ namespace sos
     {
         TADDR mt = ReadPointer(mObject);
         BOOL bContainsPointers = FALSE;
-        
+        BOOL bCollectible = FALSE;
+        TADDR loaderAllocatorObjectHandle;
+
         if (!GetSizeEfficient(mObject, mt, FALSE, mObjSize, bContainsPointers))
             Throw<DataRead>("Failed to get size of object.");
-        
-        if (!bContainsPointers)
+
+        if (!GetCollectibleDataEfficient(mt, bCollectible, loaderAllocatorObjectHandle))
+            Throw<DataRead>("Failed to get collectible info of object.");
+
+        if (!bContainsPointers && !bCollectible)
         {
             mDone = true;
             return;
         }
-        
-        if (!mGCDesc)
+
+        if (bContainsPointers)
         {
-            int entries = 0;
-            
-            if (FAILED(MOVE(entries, mt-sizeof(TADDR))))
-                Throw<DataRead>("Failed to request number of entries.");
-            
-            // array of vc?
-            if (entries < 0)
+            if (!mGCDesc)
             {
-                entries = -entries;
-                mArrayOfVC = true;
+                int entries = 0;
+
+                if (FAILED(MOVE(entries, mt-sizeof(TADDR))))
+                    Throw<DataRead>("Failed to request number of entries.");
+
+                // array of vc?
+                if (entries < 0)
+                {
+                    entries = -entries;
+                    mArrayOfVC = true;
+                }
+                else
+                {
+                    mArrayOfVC = false;
+                }
+
+                size_t slots = 1 + entries * sizeof(CGCDescSeries)/sizeof(TADDR);
+
+                ArrayHolder<TADDR> buffer = new TADDR[slots];
+
+                ULONG fetched = 0;
+                CLRDATA_ADDRESS address = TO_CDADDR(mt - slots*sizeof(TADDR));
+                if (FAILED(g_ExtData->ReadVirtual(address, buffer, (ULONG)(slots*sizeof(TADDR)), &fetched)))
+                    Throw<DataRead>("Failed to request GCDesc.");
+
+                mBuffer = buffer.Detach();
+                mGCDesc = (CGCDesc*)(mBuffer + slots);
+            }
+
+            mCurrSeries = mGCDesc->GetHighestSeries();
+
+            if (!mArrayOfVC)
+            {
+                mCurr = mObject + mCurrSeries->GetSeriesOffset();
+                mStop = mCurr + mCurrSeries->GetSeriesSize() + mObjSize;
             }
             else
             {
-                mArrayOfVC = false;
+                i = 0;
+                mCurr = mObject + mCurrSeries->startoffset;
+                mStop = mCurr + mCurrSeries->val_serie[i].nptrs * sizeof(TADDR);
+                mCount = (int)mGCDesc->GetNumSeries();
             }
-            
-            size_t slots = 1 + entries * sizeof(CGCDescSeries)/sizeof(TADDR);
-            
-            ArrayHolder<TADDR> buffer = new TADDR[slots];
-            
-            ULONG fetched = 0;
-            CLRDATA_ADDRESS address = TO_CDADDR(mt - slots*sizeof(TADDR));
-            if (FAILED(g_ExtData->ReadVirtual(address, buffer, (ULONG)(slots*sizeof(TADDR)), &fetched)))
-                Throw<DataRead>("Failed to request GCDesc.");
-            
-            mBuffer = buffer.Detach();
-            mGCDesc = (CGCDesc*)(mBuffer + slots);
+
+            if (mCurr == mStop)
+                operator++();
+            else if (mCurr >= mObject + mObjSize - plug_skew)
+                mDone = true;
         }
-        
-        mCurrSeries = mGCDesc->GetHighestSeries();
-        
-        if (!mArrayOfVC)
+        else
         {
-            mCurr = mObject + mCurrSeries->GetSeriesOffset();
-            mStop = mCurr + mCurrSeries->GetSeriesSize() + mObjSize;
+            mDone = true;
         }
-        else
+
+        if (bCollectible)
         {
-            i = 0;
-            mCurr = mObject + mCurrSeries->startoffset;
-            mStop = mCurr + mCurrSeries->val_serie[i].nptrs * sizeof(TADDR);
-            mCount = (int)mGCDesc->GetNumSeries();
+            mLoaderAllocatorObjectHandle = loaderAllocatorObjectHandle;
+            if (mDone)
+            {
+                // There are no object references, but there is still a reference for 
+                // collectible types - the LoaderAllocator for GC
+                mCurr = mLoaderAllocatorObjectHandle;
+            }
         }
-        
-        if (mCurr == mStop)
-          operator++();
-        else if (mCurr >= mObject + mObjSize - plug_skew)
-          mDone = true;
     }
 
 
index 3778235..ff5b53a 100644 (file)
@@ -501,6 +501,8 @@ namespace sos
         TADDR *mBuffer;
         CGCDescSeries *mCurrSeries;
         
+        TADDR mLoaderAllocatorObjectHandle;
+
         int i, mCount;
         
         TADDR mCurr, mStop, mObject;
index 7851654..ebad2e4 100644 (file)
@@ -1660,9 +1660,11 @@ struct MethodTableInfo
     DWORD BaseSize;           // Caching BaseSize and ComponentSize for a MethodTable
     DWORD ComponentSize;      // here has HUGE perf benefits in heap traversals.
     BOOL  bContainsPointers;
+    BOOL  bCollectible;
     DWORD_PTR* GCInfoBuffer;  // Start of memory of GC info
     CGCDesc* GCInfo;    // Just past GC info (which is how it is stored)
     bool  ArrayOfVC;
+    TADDR LoaderAllocatorObjectHandle;
 };
 
 class MethodTableCache
@@ -1680,9 +1682,11 @@ protected:
             info.BaseSize = 0;
             info.ComponentSize = 0;
             info.bContainsPointers = false;
+            info.bCollectible = false;
             info.GCInfo = NULL;
             info.ArrayOfVC = false;
             info.GCInfoBuffer = NULL;
+            info.LoaderAllocatorObjectHandle = NULL;
         }
     };
     Node *head;
@@ -1948,6 +1952,8 @@ size_t NextOSPageAddress (size_t addr);
 BOOL GetSizeEfficient(DWORD_PTR dwAddrCurrObj, 
     DWORD_PTR dwAddrMethTable, BOOL bLarge, size_t& s, BOOL& bContainsPointers);
 
+BOOL GetCollectibleDataEfficient(DWORD_PTR dwAddrMethTable, BOOL& bCollectible, TADDR& loaderAllocatorObjectHandle);
+
 // ObjSize now uses the methodtable cache for its work too.
 size_t ObjectSize (DWORD_PTR obj, BOOL fIsLargeObject=FALSE);
 size_t ObjectSize(DWORD_PTR obj, DWORD_PTR mt, BOOL fIsValueClass, BOOL fIsLargeObject=FALSE);
@@ -2856,8 +2862,10 @@ private:
         TADDR *Buffer;
         CGCDesc *GCDesc;
 
+        TADDR LoaderAllocatorObjectHandle;
         bool ArrayOfVC;
         bool ContainsPointers;
+        bool Collectible;
         size_t BaseSize;
         size_t ComponentSize;
         
@@ -2874,7 +2882,7 @@ private:
 
         MTInfo()
             : MethodTable(0), TypeName(0), Buffer(0), GCDesc(0),
-              ArrayOfVC(false), ContainsPointers(false), BaseSize(0), ComponentSize(0)
+              ArrayOfVC(false), ContainsPointers(false), Collectible(false), BaseSize(0), ComponentSize(0)
         {
         }
 
index 1ce452b..e7bef87 100644 (file)
@@ -3278,6 +3278,10 @@ ClrDataAccess::QueryInterface(THIS_
     {
         ifaceRet = static_cast<ISOSDacInterface5*>(this);
     }
+    else if (IsEqualIID(interfaceId, __uuidof(ISOSDacInterface6)))
+    {
+        ifaceRet = static_cast<ISOSDacInterface6*>(this);
+    }
     else
     {
         *iface = NULL;
index 6147de3..e05575c 100644 (file)
@@ -862,7 +862,8 @@ class ClrDataAccess
       public ISOSDacInterface2,
       public ISOSDacInterface3,
       public ISOSDacInterface4,
-      public ISOSDacInterface5
+      public ISOSDacInterface5,
+      public ISOSDacInterface6
 {
 public:
     ClrDataAccess(ICorDebugDataTarget * pTarget, ICLRDataTarget * pLegacyTarget=0);
@@ -1208,6 +1209,9 @@ public:
     // ISOSDacInterface5
     virtual HRESULT STDMETHODCALLTYPE GetTieredVersions(CLRDATA_ADDRESS methodDesc, int rejitId, struct DacpTieredVersionData *nativeCodeAddrs, int cNativeCodeAddrs, int *pcNativeCodeAddrs);
 
+    // ISOSDacInterface6
+    virtual HRESULT STDMETHODCALLTYPE GetMethodTableCollectibleData(CLRDATA_ADDRESS mt, struct DacpMethodTableCollectibleData *data);
+
     //
     // ClrDataAccess.
     //
index 12864e7..3a497b1 100644 (file)
@@ -1955,6 +1955,33 @@ ClrDataAccess::GetMethodTableFieldData(CLRDATA_ADDRESS mt, struct DacpMethodTabl
 }
 
 HRESULT
+ClrDataAccess::GetMethodTableCollectibleData(CLRDATA_ADDRESS mt, struct DacpMethodTableCollectibleData *data)
+{
+    if (mt == 0 || data == NULL)
+        return E_INVALIDARG;
+
+    SOSDacEnter();
+
+    MethodTable* pMT = PTR_MethodTable(TO_TADDR(mt));
+    BOOL bIsFree = FALSE;
+    if (!pMT || !DacValidateMethodTable(pMT, bIsFree))
+    {
+        hr = E_INVALIDARG;
+    }
+    else
+    {
+        data->bCollectible = pMT->Collectible();
+        if (data->bCollectible)
+        {
+            data->LoaderAllocatorObjectHandle = pMT->GetLoaderAllocatorObjectHandle();
+        }
+    }
+
+    SOSDacLeave();
+    return hr;
+}
+
+HRESULT
 ClrDataAccess::GetMethodTableTransparencyData(CLRDATA_ADDRESS mt, struct DacpMethodTableTransparencyData *pTransparencyData)
 {
     if (mt == 0 || pTransparencyData == NULL)
index 2f74826..47e79a1 100644 (file)
@@ -177,6 +177,25 @@ struct MSLAYOUT DacpMethodTableFieldData : ZeroInit<DacpMethodTableFieldData>
     }
 };
 
+struct MSLAYOUT DacpMethodTableCollectibleData : ZeroInit<DacpMethodTableCollectibleData>
+{
+    CLRDATA_ADDRESS LoaderAllocatorObjectHandle;
+    BOOL bCollectible;
+
+    HRESULT Request(ISOSDacInterface *sos, CLRDATA_ADDRESS addr)
+    {
+        HRESULT hr;
+        ISOSDacInterface6 *pSOS6 = NULL;
+        if (SUCCEEDED(hr = sos->QueryInterface(__uuidof(ISOSDacInterface6), (void**)&pSOS6)))
+        {
+            hr = pSOS6->GetMethodTableCollectibleData(addr, this);
+            pSOS6->Release();
+        }
+
+        return hr;
+    }
+};
+
 struct MSLAYOUT DacpMethodTableTransparencyData : ZeroInit<DacpMethodTableTransparencyData>
 {
     BOOL bHasCriticalTransparentInfo;
@@ -1043,5 +1062,6 @@ static_assert(sizeof(DacpGetModuleAddress) == 0x8, "Dacp structs cannot be modif
 static_assert(sizeof(DacpFrameData) == 0x8, "Dacp structs cannot be modified due to backwards compatibility.");
 static_assert(sizeof(DacpJitCodeHeapInfo) == 0x18, "Dacp structs cannot be modified due to backwards compatibility.");
 static_assert(sizeof(DacpExceptionObjectData) == 0x38, "Dacp structs cannot be modified due to backwards compatibility.");
+static_assert(sizeof(DacpMethodTableCollectibleData) == 0x10, "Dacp structs cannot be modified due to backwards compatibility.");
 
 #endif  // _DACPRIVATE_H_
index 5b71821..5896426 100644 (file)
@@ -367,3 +367,13 @@ interface ISOSDacInterface5 : IUnknown
 {
     HRESULT GetTieredVersions(CLRDATA_ADDRESS methodDesc, int rejitId, struct DacpTieredVersionData *nativeCodeAddrs, int cNativeCodeAddrs, int *pcNativeCodeAddrs);
 };
+
+[
+    object,
+    local,
+    uuid(11206399-4B66-4EDB-98EA-85654E59AD45)
+]
+interface ISOSDacInterface6 : IUnknown
+{
+    HRESULT GetMethodTableFieldData(CLRDATA_ADDRESS mt, struct DacpMethodTableFieldData *data);
+};
index 7c0cd69..290b9b7 100644 (file)
@@ -91,6 +91,10 @@ MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface4,0x74B9D34C,0xA612,0x4B07,0x93,0xDD,0
 
 MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface5,0x127d6abe,0x6c86,0x4e48,0x8e,0x7b,0x22,0x07,0x81,0xc5,0x81,0x01);
 
+
+MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface6,0x11206399,0x4b66,0x4edb,0x98,0xea,0x85,0x65,0x4e,0x59,0xad,0x45);
+
+
 #undef MIDL_DEFINE_GUID
 
 #ifdef __cplusplus
index 3b810cc..8896720 100644 (file)
@@ -2272,6 +2272,86 @@ EXTERN_C const IID IID_ISOSDacInterface5;
 
 #endif         /* __ISOSDacInterface5_INTERFACE_DEFINED__ */
 
+
+#ifndef __ISOSDacInterface6_INTERFACE_DEFINED__
+#define __ISOSDacInterface6_INTERFACE_DEFINED__
+
+    /* interface ISOSDacInterface6 */
+    /* [uuid][local][object] */
+
+
+    EXTERN_C const IID IID_ISOSDacInterface6;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+    MIDL_INTERFACE("11206399-4B66-4EDB-98EA-85654E59AD45")
+    ISOSDacInterface6 : public IUnknown
+    {
+    public:
+        virtual HRESULT STDMETHODCALLTYPE GetMethodTableCollectibleData(
+            CLRDATA_ADDRESS mt, 
+            struct DacpMethodTableCollectibleData *data) = 0;
+    };
+
+
+#else  /* C style interface */
+
+    typedef struct ISOSDacInterface6Vtbl
+    {
+        BEGIN_INTERFACE
+
+        HRESULT(STDMETHODCALLTYPE *QueryInterface)(
+            ISOSDacInterface5 * This,
+            /* [in] */ REFIID riid,
+            /* [annotation][iid_is][out] */
+            _COM_Outptr_  void **ppvObject);
+
+        ULONG(STDMETHODCALLTYPE *AddRef)(
+            ISOSDacInterface5 * This);
+
+        ULONG(STDMETHODCALLTYPE *Release)(
+            ISOSDacInterface5 * This);
+
+        HRESULT(STDMETHODCALLTYPE *GetMethodTableCollectibleData)(
+            CLRDATA_ADDRESS mt, 
+            struct DacpMethodTableCollectibleData *data);
+
+        END_INTERFACE
+    } ISOSDacInterface6Vtbl;
+
+    interface ISOSDacInterface6
+    {
+        CONST_VTBL struct ISOSDacInterface6Vtbl *lpVtbl;
+    };
+
+
+
+#ifdef COBJMACROS
+
+
+#define ISOSDacInterface6_QueryInterface(This,riid,ppvObject)  \
+    ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) 
+
+#define ISOSDacInterface6_AddRef(This) \
+    ( (This)->lpVtbl -> AddRef(This) ) 
+
+#define ISOSDacInterface6_Release(This)        \
+    ( (This)->lpVtbl -> Release(This) ) 
+
+
+#define ISOSDacInterface6_GetMethodTableCollectibleData(This,mt,data)  \
+    ( (This)->lpVtbl -> GetMethodTableCollectibleData(This,mt,data) ) 
+
+#endif /* COBJMACROS */
+
+
+#endif         /* C style interface */
+
+
+
+
+#endif         /* __ISOSDacInterface6_INTERFACE_DEFINED__ */
+
 /* Additional Prototypes for ALL interfaces */
 
 /* end of Additional Prototypes */