DebugHandleTracker.cs source code in C# .NET

Source code for the .NET framework in C#

                        

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

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK