Code:
/ FX-1434 / FX-1434 / 1.0 / untmp / whidbey / REDBITS / ndp / fx / src / Misc / DebugHandleTracker.cs / 1 / DebugHandleTracker.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- /* */ namespace System.Internal { using Microsoft.Win32; using System; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using Hashtable = System.Collections.Hashtable; ////// /// The job of this class is to collect and track handle usage in /// windows forms. Ideally, a developer should never have to call dispose() on /// any windows forms object. The problem in making this happen is in objects that /// are very small to the VM garbage collector, but take up huge amounts /// of resources to the system. A good example of this is a Win32 region /// handle. To the VM, a Region object is a small six ubyte object, so there /// isn't much need to garbage collect it anytime soon. To Win32, however, /// a region handle consumes expensive USER and GDI resources. Ideally we /// would like to be able to mark an object as "expensive" so it uses a different /// garbage collection algorithm. In absence of that, we use the HandleCollector class, which /// runs a daemon thread to garbage collect when handle usage goes up. /// ///internal class DebugHandleTracker { //#if DEBUG private static Hashtable handleTypes = new Hashtable(); private static DebugHandleTracker tracker; static DebugHandleTracker() { tracker = new DebugHandleTracker(); if (CompModSwitches.HandleLeak.Level > TraceLevel.Off || CompModSwitches.TraceCollect.Enabled) { System.Internal.HandleCollector.HandleAdded += new System.Internal.HandleChangeEventHandler(tracker.OnHandleAdd); System.Internal.HandleCollector.HandleRemoved += new System.Internal.HandleChangeEventHandler(tracker.OnHandleRemove); } } private DebugHandleTracker() { } private static object internalSyncObject = new object(); /// /// /// All handles available at this time will be not be considered as leaks /// when CheckLeaks is called to report leaks. /// /** @conditional(DEBUG) */ public static void IgnoreCurrentHandlesAsLeaks() { lock(internalSyncObject) { if (CompModSwitches.HandleLeak.Level >= TraceLevel.Warning) { HandleType[] types = new HandleType[handleTypes.Values.Count]; handleTypes.Values.CopyTo(types, 0); for (int i = 0; i < types.Length; i++) { if (types[i] != null) { types[i].IgnoreCurrentHandlesAsLeaks(); } } } } } ////// /// Called at shutdown to check for handles that are currently allocated. /// Normally, there should be none. This will print a list of all /// handle leaks. /// /** @conditional(DEBUG) */ [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")] public static void CheckLeaks() { lock(internalSyncObject) { if (CompModSwitches.HandleLeak.Level >= TraceLevel.Warning) { GC.Collect(); GC.WaitForPendingFinalizers(); HandleType[] types = new HandleType[handleTypes.Values.Count]; handleTypes.Values.CopyTo(types, 0); Debug.WriteLine("------------Begin--CheckLeaks--------------------"); for (int i = 0; i < types.Length; i++) { if (types[i] != null) { types[i].CheckLeaks(); } } Debug.WriteLine("-------------End--CheckLeaks---------------------"); } } } ////// /// Ensures leak detection has been initialized. /// /** @conditional(DEBUG) */ public static void Initialize() { // Calling this method forces the class to be loaded, thus running the // static constructor which does all the work. } ////// /// Called by the Win32 handle collector when a new handle is created. /// /** @conditional(DEBUG) */ private void OnHandleAdd(string handleName, IntPtr handle, int handleCount) { HandleType type = (HandleType)handleTypes[handleName]; if (type == null) { type = new HandleType(handleName); handleTypes[handleName] = type; } type.Add(handle); } ////// /// Called by the Win32 handle collector when a new handle is created. /// /** @conditional(DEBUG) */ private void OnHandleRemove(string handleName, IntPtr handle, int HandleCount) { HandleType type = (HandleType)handleTypes[handleName]; bool removed = false; if (type != null) { removed = type.Remove(handle); } if (!removed) { if (CompModSwitches.HandleLeak.Level >= TraceLevel.Error) { // It seems to me we shouldn't call HandleCollector.Remove more than once // for a given handle, but we do just that for HWND's (NativeWindow.DestroyWindow // and Control.WmNCDestroy). Debug.WriteLine("*************************************************"); Debug.WriteLine("While removing, couldn't find handle: " + Convert.ToString((int)handle, 16)); Debug.WriteLine("Handle Type : " + handleName); Debug.WriteLine(Environment.StackTrace); Debug.WriteLine("-------------------------------------------------"); } } } ////// /// Represents a specific type of handle. /// private class HandleType { public readonly string name; private int handleCount; private HandleEntry[] buckets; private const int BUCKETS = 10; ////// /// Creates a new handle type. /// public HandleType(string name) { this.name = name; this.buckets = new HandleEntry[BUCKETS]; } ////// /// Adds a handle to this handle type for monitoring. /// public void Add(IntPtr handle) { lock(this) { int hash = ComputeHash(handle); if (CompModSwitches.HandleLeak.Level >= TraceLevel.Info) { Debug.WriteLine("-------------------------------------------------"); Debug.WriteLine("Handle Allocating: " + Convert.ToString((int)handle, 16)); Debug.WriteLine("Handle Type : " + name); if (CompModSwitches.HandleLeak.Level >= TraceLevel.Verbose) Debug.WriteLine(Environment.StackTrace); } HandleEntry entry = buckets[hash]; while (entry != null) { Debug.Assert(entry.handle != handle, "Duplicate handle of type " + name); entry = entry.next; } buckets[hash] = new HandleEntry(buckets[hash], handle); handleCount++; } } ////// /// Checks and reports leaks for handle monitoring. /// public void CheckLeaks() { lock(this) { bool reportedFirstLeak = false; if (handleCount > 0) { for (int i = 0; i < BUCKETS; i++) { HandleEntry e = buckets[i]; while (e != null) { if (!e.ignorableAsLeak) { if (!reportedFirstLeak) { Debug.WriteLine("\r\nHandle leaks detected for handles of type " + name + ":"); reportedFirstLeak = true; } Debug.WriteLine(e.ToString(this)); } e = e.next; } } } } } ////// /// Marks all the handles currently stored, as ignorable, so that they will not be reported as leaks later. /// public void IgnoreCurrentHandlesAsLeaks() { lock(this) { if (handleCount > 0) { for (int i = 0; i < BUCKETS; i++) { HandleEntry e = buckets[i]; while (e != null) { e.ignorableAsLeak = true; e = e.next; } } } } } ////// /// Computes the hash bucket for this handle. /// private int ComputeHash(IntPtr handle) { return((int)handle & 0xFFFF) % BUCKETS; } ////// /// Removes the given handle from our monitor list. /// public bool Remove(IntPtr handle) { lock(this) { int hash = ComputeHash(handle); if (CompModSwitches.HandleLeak.Level >= TraceLevel.Info) { Debug.WriteLine("-------------------------------------------------"); Debug.WriteLine("Handle Releaseing: " + Convert.ToString((int)handle, 16)); Debug.WriteLine("Handle Type : " + name); if (CompModSwitches.HandleLeak.Level >= TraceLevel.Verbose) Debug.WriteLine(Environment.StackTrace); } HandleEntry e = buckets[hash]; HandleEntry last = null; while (e != null && e.handle != handle) { last = e; e = e.next; } if (e != null) { if (last == null) { buckets[hash] = e.next; } else { last.next = e.next; } handleCount--; return true; } return false; } } ////// /// Denotes a single entry in our handle list. /// [SuppressMessage("Microsoft.Design", "CA1049:TypesThatOwnNativeResourcesShouldBeDisposable")] private class HandleEntry { [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] public readonly IntPtr handle; public HandleEntry next; public readonly string callStack; public bool ignorableAsLeak; ////// /// Creates a new handle entry /// public HandleEntry(HandleEntry next, IntPtr handle) { this.handle = handle; this.next = next; if (CompModSwitches.HandleLeak.Level > TraceLevel.Off) { this.callStack = Environment.StackTrace; } else { this.callStack = null; } } ////// /// Converts this handle to a printable string. the string consists /// of the handle value along with the callstack for it's /// allocation. /// public string ToString(HandleType type) { StackParser sp = new StackParser(callStack); // Discard all of the stack up to and including the "Handle.create" call // sp.DiscardTo("HandleCollector.Add"); // Skip the next call as it is always a debug wrapper // sp.DiscardNext(); // Now recreate the leak list with a lot of stack entries // sp.Truncate(40); string description = ""; /*if (type.name.Equals("GDI") || type.name.Equals("HDC")) { int objectType = UnsafeNativeMethods.GetObjectType(new HandleRef(null, handle)); switch (objectType) { case NativeMethods.OBJ_DC: description = "normal DC"; break; case NativeMethods.OBJ_MEMDC: description = "memory DC"; break; case NativeMethods.OBJ_METADC: description = "metafile DC"; break; case NativeMethods.OBJ_ENHMETADC: description = "enhanced metafile DC"; break; case NativeMethods.OBJ_PEN: description = "Pen"; break; case NativeMethods.OBJ_BRUSH: description = "Brush"; break; case NativeMethods.OBJ_PAL: description = "Palette"; break; case NativeMethods.OBJ_FONT: description = "Font"; break; case NativeMethods.OBJ_BITMAP: description = "Bitmap"; break; case NativeMethods.OBJ_REGION: description = "Region"; break; case NativeMethods.OBJ_METAFILE: description = "Metafile"; break; case NativeMethods.OBJ_EXTPEN: description = "Extpen"; break; default: description = "?"; break; } description = " (" + description + ")"; }*/ return Convert.ToString((int)handle, 16) + description + ": " + sp.ToString(); } ////// /// Simple stack parsing class to manipulate our callstack. /// private class StackParser { internal string releventStack; internal int startIndex; internal int endIndex; internal int length; ////// /// Creates a new stackparser with the given callstack /// public StackParser(string callStack) { releventStack = callStack; length = releventStack.Length; } ////// /// Determines if the given string contains token. This is a case /// sensitive match. /// private static bool ContainsString(string str, string token) { int stringLength = str.Length; int tokenLength = token.Length; for (int s = 0; s < stringLength; s++) { int t = 0; while (t < tokenLength && str[s + t] == token[t]) { t++; } if (t == tokenLength) { return true; } } return false; } ////// /// Discards the next line of the stack trace. /// public void DiscardNext() { GetLine(); } ////// /// Discards all lines up to and including the line that contains /// discardText. /// public void DiscardTo(string discardText) { while (startIndex < length) { string line = GetLine(); if (line == null || ContainsString(line, discardText)) { break; } } } ////// /// Retrieves the next line of the stack. /// private string GetLine() { endIndex = releventStack.IndexOf('\r', startIndex); if (endIndex < 0) { endIndex = length - 1; } string line = releventStack.Substring(startIndex, endIndex - startIndex); char ch; while (endIndex < length && ((ch = releventStack[endIndex]) == '\r' || ch == '\n')) { endIndex++; } if (startIndex == endIndex) return null; startIndex = endIndex; line = line.Replace('\t', ' '); return line; } ////// /// Rereives the string of the parsed stack trace /// public override string ToString() { return releventStack.Substring(startIndex); } ////// /// Truncates the stack trace, saving the given # of lines. /// public void Truncate(int lines) { string truncatedStack = ""; while (lines-- > 0 && startIndex < length) { if (truncatedStack == null) { truncatedStack = GetLine(); } else { truncatedStack += ": " + GetLine(); } truncatedStack += Environment.NewLine; } releventStack = truncatedStack; startIndex = 0; endIndex = 0; length = releventStack.Length; } } } } //#endif // DEBUG } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- LinearKeyFrames.cs
- SafeCryptoHandles.cs
- MouseGesture.cs
- XPathMessageFilterElementComparer.cs
- GeometryHitTestResult.cs
- ArgumentException.cs
- XmlArrayItemAttributes.cs
- CuspData.cs
- WindowInteropHelper.cs
- OdbcUtils.cs
- EntityDataSource.cs
- DetailsViewUpdatedEventArgs.cs
- ComEventsHelper.cs
- SudsCommon.cs
- EncryptedPackageFilter.cs
- SqlCacheDependencyDatabaseCollection.cs
- querybuilder.cs
- peersecuritysettings.cs
- ProfileServiceManager.cs
- FontConverter.cs
- ResizeGrip.cs
- RequestBringIntoViewEventArgs.cs
- ConfigurationStrings.cs
- ObjectHelper.cs
- DataControlFieldCell.cs
- DispatcherObject.cs
- XMLUtil.cs
- NativeObjectSecurity.cs
- UriSection.cs
- IndexedSelectQueryOperator.cs
- TypedReference.cs
- ReflectionUtil.cs
- HttpHandlerAction.cs
- XmlSchemaSimpleTypeRestriction.cs
- OdbcConnection.cs
- CommandConverter.cs
- ActivityBindForm.cs
- CollectionViewGroupRoot.cs
- WebContentFormatHelper.cs
- RectAnimationClockResource.cs
- SQLGuidStorage.cs
- ImageClickEventArgs.cs
- DataList.cs
- XPathDescendantIterator.cs
- SystemIcmpV6Statistics.cs
- DataTemplateKey.cs
- DocumentAutomationPeer.cs
- HMACSHA1.cs
- MapPathBasedVirtualPathProvider.cs
- _NegoState.cs
- DragStartedEventArgs.cs
- PropertyCollection.cs
- NativeCppClassAttribute.cs
- ItemsControlAutomationPeer.cs
- Activity.cs
- PersistChildrenAttribute.cs
- LineBreak.cs
- MimeWriter.cs
- DockPattern.cs
- ConfigurationSectionGroupCollection.cs
- HwndTarget.cs
- PerformanceCounterPermissionEntryCollection.cs
- BitmapEffectInput.cs
- BaseValidatorDesigner.cs
- EditableRegion.cs
- SplitContainer.cs
- UIEndRequest.cs
- XamlInt32CollectionSerializer.cs
- GeometryDrawing.cs
- CustomCategoryAttribute.cs
- EventManager.cs
- SignedPkcs7.cs
- ConsumerConnectionPointCollection.cs
- DataGridViewColumnCollectionEditor.cs
- GeometryHitTestResult.cs
- PenLineCapValidation.cs
- ProfilePropertySettingsCollection.cs
- EntityDataSourceContextCreatingEventArgs.cs
- DateTimeUtil.cs
- PerfCounters.cs
- Variant.cs
- FormViewPageEventArgs.cs
- HttpResponseHeader.cs
- CFStream.cs
- SqlUDTStorage.cs
- SqlBulkCopyColumnMapping.cs
- Transform3DGroup.cs
- DrawingState.cs
- ResourceProperty.cs
- TextTreeDeleteContentUndoUnit.cs
- TypeResolver.cs
- SqlBooleanizer.cs
- ClientUtils.cs
- ToolboxItem.cs
- NamespaceQuery.cs
- NameTable.cs
- TypeInfo.cs
- RichTextBox.cs
- CallTemplateAction.cs
- InkSerializer.cs