Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Misc / DebugHandleTracker.cs / 1305376 / 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.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- PointF.cs
- DefaultValueAttribute.cs
- SqlDataSourceQueryConverter.cs
- DocumentSchemaValidator.cs
- ApplicationManager.cs
- columnmapfactory.cs
- UnsafeNativeMethods.cs
- ObjectParameter.cs
- ProviderUtil.cs
- XPathPatternBuilder.cs
- SignatureToken.cs
- XmlSchemaParticle.cs
- OutKeywords.cs
- LocalizationParserHooks.cs
- TextElement.cs
- HtmlTitle.cs
- WarningException.cs
- Publisher.cs
- XmlQueryRuntime.cs
- WindowsEditBox.cs
- TypeDescriptionProvider.cs
- XmlBinaryReader.cs
- QueryCorrelationInitializer.cs
- Cell.cs
- CompressStream.cs
- MessageTransmitTraceRecord.cs
- RegexStringValidator.cs
- DataServiceClientException.cs
- MemberHolder.cs
- KeyTimeConverter.cs
- ImplicitInputBrush.cs
- DecoderFallback.cs
- StateBag.cs
- HostedElements.cs
- InfoCardConstants.cs
- ACL.cs
- Control.cs
- _HeaderInfo.cs
- DuplexClientBase.cs
- ColumnMapVisitor.cs
- BitStack.cs
- Tokenizer.cs
- FixedTextPointer.cs
- LinqTreeNodeEvaluator.cs
- TableCellAutomationPeer.cs
- CompressedStack.cs
- SqlConnectionHelper.cs
- PropertyContainer.cs
- ListViewGroupCollectionEditor.cs
- DBCommand.cs
- Quaternion.cs
- GridToolTip.cs
- AppDomainResourcePerfCounters.cs
- AdornerLayer.cs
- WeakEventManager.cs
- SafeLibraryHandle.cs
- MembershipSection.cs
- Point3DValueSerializer.cs
- InheritanceRules.cs
- NetworkInterface.cs
- XamlStream.cs
- SignedInfo.cs
- TraceSection.cs
- DataChangedEventManager.cs
- SystemIcmpV6Statistics.cs
- CommonDialog.cs
- ApplicationTrust.cs
- GridViewEditEventArgs.cs
- WCFServiceClientProxyGenerator.cs
- Int64.cs
- SqlClientWrapperSmiStream.cs
- TextSimpleMarkerProperties.cs
- TimeoutHelper.cs
- HttpApplication.cs
- StorageAssociationTypeMapping.cs
- OverlappedAsyncResult.cs
- InvalidComObjectException.cs
- HttpContext.cs
- DocumentScope.cs
- TextParagraphView.cs
- DelegateCompletionCallbackWrapper.cs
- LogPolicy.cs
- HandleValueEditor.cs
- XPathParser.cs
- _AutoWebProxyScriptHelper.cs
- IndentTextWriter.cs
- IpcPort.cs
- XamlPointCollectionSerializer.cs
- XmlQueryCardinality.cs
- BaseValidator.cs
- ColorTransformHelper.cs
- BinaryParser.cs
- DataGridViewUtilities.cs
- ToolBar.cs
- InputManager.cs
- HealthMonitoringSectionHelper.cs
- ObjectListCommandCollection.cs
- FontNamesConverter.cs
- TableAutomationPeer.cs
- WebPartDisplayModeCancelEventArgs.cs