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
- PerformanceCounterNameAttribute.cs
- BackStopAuthenticationModule.cs
- GridToolTip.cs
- HtmlInputCheckBox.cs
- LeaseManager.cs
- BuildProvidersCompiler.cs
- DataGridRowClipboardEventArgs.cs
- COM2ExtendedUITypeEditor.cs
- SqlParameterCollection.cs
- CustomAttributeFormatException.cs
- PointAnimationUsingPath.cs
- ActivityTypeDesigner.xaml.cs
- DriveInfo.cs
- XmlTextReader.cs
- WebReferencesBuildProvider.cs
- ToolStripLabel.cs
- CachedRequestParams.cs
- AssemblySettingAttributes.cs
- ISFClipboardData.cs
- ColumnMap.cs
- ResetableIterator.cs
- ScriptModule.cs
- FontNamesConverter.cs
- BindingMemberInfo.cs
- FragmentQueryKB.cs
- XmlTextReaderImplHelpers.cs
- TraceLevelStore.cs
- ToolStripItem.cs
- WebPartUtil.cs
- RootCodeDomSerializer.cs
- InvariantComparer.cs
- BulletedListDesigner.cs
- EntityClientCacheKey.cs
- BitmapFrameEncode.cs
- CodeMemberProperty.cs
- ColorPalette.cs
- FontWeight.cs
- RegisteredHiddenField.cs
- ResourcePermissionBase.cs
- WizardSideBarListControlItemEventArgs.cs
- ElementUtil.cs
- DragDrop.cs
- TableLayoutSettings.cs
- _ProxyChain.cs
- metadatamappinghashervisitor.hashsourcebuilder.cs
- LazyTextWriterCreator.cs
- RpcCryptoRequest.cs
- CheckBoxAutomationPeer.cs
- CryptoHelper.cs
- RoleServiceManager.cs
- ProfileService.cs
- ListViewEditEventArgs.cs
- ListViewTableCell.cs
- ArgIterator.cs
- ImplicitInputBrush.cs
- InterleavedZipPartStream.cs
- BitmapEffectvisualstate.cs
- BufferedGraphicsContext.cs
- LocatorGroup.cs
- DBCommandBuilder.cs
- ImageIndexConverter.cs
- Empty.cs
- MenuBase.cs
- DrawingServices.cs
- HyperLinkStyle.cs
- ACL.cs
- ReferenceEqualityComparer.cs
- SimpleWebHandlerParser.cs
- ButtonField.cs
- KerberosSecurityTokenProvider.cs
- MeasurementDCInfo.cs
- FormViewRow.cs
- QuaternionRotation3D.cs
- PinnedBufferMemoryStream.cs
- MemberMaps.cs
- KeyValueConfigurationElement.cs
- CancellationScope.cs
- ConnectionInterfaceCollection.cs
- ButtonFieldBase.cs
- DetailsViewInsertedEventArgs.cs
- DataGridViewCellPaintingEventArgs.cs
- IDQuery.cs
- ListViewInsertionMark.cs
- ConfigUtil.cs
- ScopedMessagePartSpecification.cs
- LayoutManager.cs
- WSSecureConversationDec2005.cs
- TypeSystemProvider.cs
- recordstate.cs
- QuaternionRotation3D.cs
- PathNode.cs
- KeyEventArgs.cs
- WindowVisualStateTracker.cs
- RequestNavigateEventArgs.cs
- PathSegment.cs
- Int16AnimationUsingKeyFrames.cs
- XmlSchemaAnnotated.cs
- querybuilder.cs
- RecognizedPhrase.cs
- SecurityException.cs