The debug probes are mainly focused toward Interop Services debugging. The logging goes to OutputDebugString. To enable this logging, you must add entries in Machine.config and App.config.
In the Machine.config file, add the following:
<runtime>
<developerSettings
CDP.AllowDebugProbes="true"
CDP.AllowDebugBreak="true"
/>
</runtime>
In your App.config file, add the following:
<runtime>
<developerSettings
CDP.Apartment="true"
CDP.BufferOverrun="true"
CDP.CollectedDelegate="true"
CDP.DisconnectedContext="true"
CDP.NotMarshalable="true"
CDP.InvalidIUnknown="true"
CDP.ObjNotKeptAlive="true"
CDP.PInvokeCallConvMismatch="true"
CDP.Marshaling="true"
/>
</runtime>
Adam Nathan has written a tool called CLRSpy, which can be very helpful in setting up logging for Interop Services debugging. It can be found at http://www.gotdotnet.com.
There is also a GUI tool called GUI Spy, which you can use to set these settings. It can be downloaded from the following URL:
http://www.gotdotnet.com/Community/UserSamples/ Details.aspx?SampleGuid=C7B955C7-231A-406C-9FA5-AD09EF3BB37F
The following sections describe the debug probes.
The . BufferOverrun probe triggers a garbage collection after a call from managed to unmanaged code.
Unmanaged code can corrupt the state of the runtime. This frequently happens as a result of a stack buffer overrun during a call from managed to unmanaged code. If a pointer the GC is responsible for has been corrupted, when the GC compresses the heap, it will attempt to dereference the corrupted pointer, likely resulting in an access violation. The . BufferOverrun probe attempts to reduce the time between the corruption of the GC pointer and the access violation.
The . CollectedDelegate probe prevents the collection of stubs created when a delegate is marshaled as a function pointer. This way, if unmanaged code attempts to place a callback on a function pointer to a collected delegate, the call can be intercepted by this probe. This probe will raise a debug break if these breaks are enabled.
When delegates are marshaled to unmanaged code as function pointers, the runtime is unable to keep track of when the unmanaged code is finished with the function pointer. This is because the unmanaged code has no way to report its references to the GC. If the managed code neglects to keep a reference to the delegate for the lifetime of the function pointer, a garbage collection could collect the delegate and invalidate the function pointer, causing calls on that function pointer to fail.
The . DisconnectedContext probe fires if the runtime cannot place a call in the correct apartment because the apartment thread is dead.
The . NotMarshalable probe reports if an IMarshal provided by a COM object fails when the runtime attempts to marshal the object.
The . ObjNotKeptAlive probe forces a garbage collection just before a call from managed to unmanaged code is made, in an effort to collect all COM objects whose counts have fallen to zero.
If unmanaged code neglects to reference count a COM object correctly, the count could drop to zero prematurely. In this case, the object will delete itself and the next access to the object will fail. These bugs are difficult to track down because it's not clear which AddRef does not have a corresponding release. This problem is even harder to track down when the COM object is an exported managed object, because these objects are not deleted when their reference count drops to zero, but rather during the next garbage collection.
On x86, the = . PInvokeCallConvMismatch probe compares the EBP register before and after a PInvoke call to determine if the unmanaged function executed the calling convention specified by the DllImport custom attribute. This probe will raise a debug break if the calling conventions do not match.
The common calling conventions— stdcall, cdecl, and thiscall —specify if the callee or the caller should remove the arguments from the stack and whether the this pointer should be passed on the stack or in ECX. If the caller and callee do not agree on the calling convention, the stack could become corrupted.
The . Marshaling probe logs which unmanaged type a managed object is marshaled as when an unmanaged function is called from managed code. The filter can be used to limit which functions have their marshaling logged.
When the runtime tries to marshal an object, it first looks to see if the object implements IMarshal and then, if not, looks for a registered stub. If the runtime cannot marshal the object, for whatever reason, it simply breaks COM protocol and places the call using whatever thread requested the Remote Procedure Call (RPC). The . Marshaling probe reports when that protocol is broken in the case of IMarshal failing.
The . Apartment probe causes the runtime to check to see if the apartment state of a thread has changed without the runtime being aware of the change.
COM identifies a thread as STA or MTA by storing a bit of information in the thread local storage for the thread. COM clients set that bit when they call CoInitialize and CoUninitialize. The runtime also identifies a thread as STA or MTA by storing a bit of information in a private data structure. Currently, COM does not provide notification when a thread's state changes from MTA to STA, and hence those two bits of information can become inconsistent.