File: net\System\Net\_LoggingObject.cs
Project: ndp\fx\src\System.csproj (System)
//------------------------------------------------------------------------------
// <copyright file="_LoggingObject.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
 
//
//  We have function based stack and thread based logging of basic behavior.  We
//  also now have the ability to run a "watch thread" which does basic hang detection
//  and error-event based logging.   The logging code buffers the callstack/picture
//  of all COMNET threads, and upon error from an assert or a hang, it will open a file
//  and dump the snapsnot.  Future work will allow this to be configed by registry and
//  to use Runtime based logging.  We'd also like to have different levels of logging.
//
 
namespace System.Net {
    using System.Collections;
    using System.Collections.Generic;
    using System.IO;
    using System.Threading;
    using System.Diagnostics;
    using System.Security.Permissions;
    using System.Security.Principal;
    using System.Security;
    using Microsoft.Win32;
    using System.Runtime.ConstrainedExecution;
    using System.Globalization;
    using System.Configuration;
 
    //
    // BaseLoggingObject - used to disable logging,
    //  this is just a base class that does nothing.
    //
 
    internal class BaseLoggingObject {
 
        internal BaseLoggingObject() {
        }
 
        internal virtual void EnterFunc(string funcname) {
        }
 
        internal virtual void LeaveFunc(string funcname) {
        }
 
        internal virtual void DumpArrayToConsole() {
        }
 
        internal virtual void PrintLine(string msg) {
        }
 
        internal virtual void DumpArray(bool shouldClose) {
        }
 
        internal virtual void DumpArrayToFile(bool shouldClose) {
        }
 
        internal virtual void Flush() {
        }
 
        internal virtual void Flush(bool close) {
        }
 
        internal virtual void LoggingMonitorTick() {
        }
 
        internal virtual void Dump(byte[] buffer) {
        }
 
        internal virtual void Dump(byte[] buffer, int length) {
        }
 
        internal virtual void Dump(byte[] buffer, int offset, int length) {
        }
 
        internal virtual void Dump(IntPtr pBuffer, int offset, int length) {
        }
 
    } // class BaseLoggingObject
 
#if TRAVE
    /// <internalonly/>
    /// <devdoc>
    /// </devdoc>
    internal class LoggingObject : BaseLoggingObject {
        public ArrayList _Logarray;
        private Hashtable _ThreadNesting;
        private int _AddCount;
        private StreamWriter _Stream;
        private int _IamAlive;
        private int _LastIamAlive;
        private bool _Finalized = false;
        private double _NanosecondsPerTick;
        private int _StartMilliseconds;
        private long _StartTicks;
 
        internal LoggingObject() : base() {
            _Logarray      = new ArrayList();
            _ThreadNesting = new Hashtable();
            _AddCount      = 0;
            _IamAlive      = 0;
            _LastIamAlive  = -1;
 
            if (GlobalLog.s_UsePerfCounter) {
                long ticksPerSecond;
                SafeNativeMethods.QueryPerformanceFrequency(out ticksPerSecond);
                _NanosecondsPerTick = 10000000.0/(double)ticksPerSecond;
                SafeNativeMethods.QueryPerformanceCounter(out _StartTicks);
            } else {
                _StartMilliseconds = Environment.TickCount;
            }
        }
 
        //
        // LoggingMonitorTick - this function is run from the monitor thread,
        //  and used to check to see if there any hangs, ie no logging
        //  activitity
        //
 
        internal override void LoggingMonitorTick() {
            if ( _LastIamAlive == _IamAlive ) {
                PrintLine("================= Error TIMEOUT - HANG DETECTED =================");
                DumpArray(true);
            }
            _LastIamAlive = _IamAlive;
        }
 
        internal override void EnterFunc(string funcname) {
            if (_Finalized) {
                return;
            }
            IncNestingCount();
            ValidatePush(funcname);
            PrintLine(funcname);
        }
 
        internal override void LeaveFunc(string funcname) {
            if (_Finalized) {
                return;
            }
            PrintLine(funcname);
            DecNestingCount();
            ValidatePop(funcname);
        }
 
        internal override void DumpArrayToConsole() {
            for (int i=0; i < _Logarray.Count; i++) {
                Console.WriteLine((string) _Logarray[i]);
            }
        }
 
        internal override void PrintLine(string msg) {
            if (_Finalized) {
                return;
            }
            string spc = "";
 
            _IamAlive++;
 
            spc = GetNestingString();
 
            string tickString = "";
 
            if (GlobalLog.s_UsePerfCounter) {
                long nowTicks;
                SafeNativeMethods.QueryPerformanceCounter(out nowTicks);
                if (_StartTicks>nowTicks) { // counter reset, restart from 0
                    _StartTicks = nowTicks;
                }
                nowTicks -= _StartTicks;
                if (GlobalLog.s_UseTimeSpan) {
                    tickString = new TimeSpan((long)(nowTicks*_NanosecondsPerTick)).ToString();
                    // note: TimeSpan().ToString() doesn't return the uSec part
                    // if its 0. .ToString() returns [H*]HH:MM:SS:uuuuuuu, hence 16
                    if (tickString.Length < 16) {
                        tickString += ".0000000";
                    }
                }
                else {
                    tickString = ((double)nowTicks*_NanosecondsPerTick/10000).ToString("f3");
                }
            }
            else {
                int nowMilliseconds = Environment.TickCount;
                if (_StartMilliseconds>nowMilliseconds) {
                    _StartMilliseconds = nowMilliseconds;
                }
                nowMilliseconds -= _StartMilliseconds;
                if (GlobalLog.s_UseTimeSpan) {
                    tickString = new TimeSpan(nowMilliseconds*10000).ToString();
                    // note: TimeSpan().ToString() doesn't return the uSec part
                    // if its 0. .ToString() returns [H*]HH:MM:SS:uuuuuuu, hence 16
                    if (tickString.Length < 16) {
                        tickString += ".0000000";
                    }
                }
                else {
                    tickString = nowMilliseconds.ToString();
                }
            }
 
            uint threadId = 0;
 
            if (GlobalLog.s_UseThreadId) {
                try {
                    object threadData = Thread.GetData(GlobalLog.s_ThreadIdSlot);
                    if (threadData!= null) {
                        threadId = (uint)threadData;
                    }
 
                }
                catch(Exception exception) {
                    if (exception is ThreadAbortException || exception is StackOverflowException || exception is OutOfMemoryException) {
                        throw;
                    }
                }
                if (threadId == 0) {
                    threadId = UnsafeNclNativeMethods.GetCurrentThreadId();
                    Thread.SetData(GlobalLog.s_ThreadIdSlot, threadId);
                }
            }
            if (threadId == 0) {
                threadId = (uint)Thread.CurrentThread.GetHashCode();
            }
 
            string str = "[" + threadId.ToString("x8") + "]"  + " (" +tickString+  ") " + spc + msg;
 
            lock(this) {
                _AddCount++;
                _Logarray.Add(str);
                int MaxLines = GlobalLog.s_DumpToConsole ? 0 : GlobalLog.MaxLinesBeforeSave;
                if (_AddCount > MaxLines) {
                    _AddCount = 0;
                    DumpArray(false);
                    _Logarray = new ArrayList();
                }
            }
        }
 
        internal override void DumpArray(bool shouldClose) {
            if ( GlobalLog.s_DumpToConsole ) {
                DumpArrayToConsole();
            } else {
                DumpArrayToFile(shouldClose);
            }
        }
 
        internal unsafe override void Dump(byte[] buffer, int offset, int length) {
            //if (!GlobalLog.s_DumpWebData) {
            //    return;
            //}
            if (buffer==null) {
                PrintLine("(null)");
                return;
            }
            if (offset > buffer.Length) {
                PrintLine("(offset out of range)");
                return;
            }
            if (length > GlobalLog.s_MaxDumpSize) {
                PrintLine("(printing " + GlobalLog.s_MaxDumpSize.ToString() + " out of " + length.ToString() + ")");
                length = GlobalLog.s_MaxDumpSize;
            }
            if ((length < 0) || (length > buffer.Length - offset)) {
                length = buffer.Length - offset;
            }
            fixed (byte* pBuffer = buffer) {
                Dump((IntPtr)pBuffer, offset, length);
            }
        }
 
        internal unsafe override void Dump(IntPtr pBuffer, int offset, int length) {
            //if (!GlobalLog.s_DumpWebData) {
            //    return;
            //}
            if (pBuffer==IntPtr.Zero || length<0) {
                PrintLine("(null)");
                return;
            }
            if (length > GlobalLog.s_MaxDumpSize) {
                PrintLine("(printing " + GlobalLog.s_MaxDumpSize.ToString() + " out of " + length.ToString() + ")");
                length = GlobalLog.s_MaxDumpSize;
            }
            byte* buffer = (byte*)pBuffer + offset;
            Dump(buffer, length);
        }
 
        unsafe void Dump(byte* buffer, int length) {
            do {
                int offset = 0;
                int n = Math.Min(length, 16);
                string disp = ((IntPtr)buffer).ToString("X8") + " : " + offset.ToString("X8") + " : ";
                byte current;
                for (int i = 0; i < n; ++i) {
                    current = *(buffer + i);
                    disp += current.ToString("X2") + ((i == 7) ? '-' : ' ');
                }
                for (int i = n; i < 16; ++i) {
                    disp += "   ";
                }
                disp += ": ";
                for (int i = 0; i < n; ++i) {
                    current = *(buffer + i);
                    disp += ((current < 0x20) || (current > 0x7e)) ? '.' : (char)current;
                }
                PrintLine(disp);
                offset += n;
                buffer += n;
                length -= n;
            } while (length > 0);
        }
 
        // SECURITY: This is dev-debugging class and we need some permissions
        // to use it under trust-restricted environment as well.
        [PermissionSet(SecurityAction.Assert, Name="FullTrust")]
        internal override void DumpArrayToFile(bool shouldClose) {
            lock (this) {
                if (!shouldClose) {
                    if (_Stream==null) {
                        string mainLogFileRoot = GlobalLog.s_RootDirectory + "System.Net";
                        string mainLogFile = mainLogFileRoot;
                        for (int k=0; k<20; k++) {
                            if (k>0) {
                                mainLogFile = mainLogFileRoot + "." + k.ToString();
                            }
                            string fileName = mainLogFile + ".log";
                            if (!File.Exists(fileName)) {
                                try {
                                    _Stream = new StreamWriter(fileName);
                                    break;
                                }
                                catch (Exception exception) {
                                    if (exception is ThreadAbortException || exception is StackOverflowException || exception is OutOfMemoryException) {
                                        throw;
                                    }
                                    if (exception is SecurityException || exception is UnauthorizedAccessException) {
                                        // can't be CAS (we assert) this is an ACL issue
                                        break;
                                    }
                                }
                            }
                        }
                        if (_Stream==null) {
                            _Stream = StreamWriter.Null;
                        }
                        // write a header with information about the Process and the AppDomain
                        _Stream.Write("# MachineName: " + Environment.MachineName + "\r\n");
                        _Stream.Write("# ProcessName: " + Process.GetCurrentProcess().ProcessName + " (pid: " + Process.GetCurrentProcess().Id + ")\r\n");
                        _Stream.Write("# AppDomainId: " + AppDomain.CurrentDomain.Id + "\r\n");
                        _Stream.Write("# CurrentIdentity: " + WindowsIdentity.GetCurrent().Name + "\r\n");
                        _Stream.Write("# CommandLine: " + Environment.CommandLine + "\r\n");
                        _Stream.Write("# ClrVersion: " + Environment.Version + "\r\n");
                        _Stream.Write("# CreationDate: " + DateTime.Now.ToString("g") + "\r\n");
                    }
                }
                try {
                    if (_Logarray!=null) {
                        for (int i=0; i<_Logarray.Count; i++) {
                            _Stream.Write((string)_Logarray[i]);
                            _Stream.Write("\r\n");
                        }
 
                        if (_Logarray.Count > 0 && _Stream != null)
                            _Stream.Flush();
                    }
                }
                catch (Exception exception) {
                    if (exception is ThreadAbortException || exception is StackOverflowException || exception is OutOfMemoryException) {
                        throw;
                    }
                }
                if (shouldClose && _Stream!=null) {
                    try {
                        _Stream.Close();
                    }
                    catch (ObjectDisposedException) { }
                    _Stream = null;
                }
            }
        }
 
        internal override void Flush() {
            Flush(false);
        }
 
        internal override void Flush(bool close) {
            lock (this) {
                if (!GlobalLog.s_DumpToConsole) {
                    DumpArrayToFile(close);
                    _AddCount = 0;
                }
            }
        }
 
        private class ThreadInfoData {
            public ThreadInfoData(string indent) {
                Indent = indent;
                NestingStack = new Stack();
            }
            public string Indent;
            public Stack NestingStack;
        };
 
        string IndentString {
            get {
                string indent = " ";
                Object obj = _ThreadNesting[Thread.CurrentThread.GetHashCode()];
                if (!GlobalLog.s_DebugCallNesting) {
                    if (obj == null) {
                        _ThreadNesting[Thread.CurrentThread.GetHashCode()] = indent;
                    } else {
                        indent = (String) obj;
                    }
                } else {
                    ThreadInfoData threadInfo = obj as ThreadInfoData;
                    if (threadInfo == null) {
                        threadInfo = new ThreadInfoData(indent);
                        _ThreadNesting[Thread.CurrentThread.GetHashCode()] = threadInfo;
                    }
                    indent = threadInfo.Indent;
                }
                return indent;
            }
            set {
                Object obj = _ThreadNesting[Thread.CurrentThread.GetHashCode()];
                if (obj == null) {
                    return;
                }
                if (!GlobalLog.s_DebugCallNesting) {
                    _ThreadNesting[Thread.CurrentThread.GetHashCode()] = value;
                } else {
                    ThreadInfoData threadInfo = obj as ThreadInfoData;
                    if (threadInfo == null) {
                        threadInfo = new ThreadInfoData(value);
                        _ThreadNesting[Thread.CurrentThread.GetHashCode()] = threadInfo;
                    }
                    threadInfo.Indent = value;
                }
            }
        }
 
        [System.Diagnostics.Conditional("TRAVE")]
        private void IncNestingCount() {
            IndentString = IndentString + " ";
        }
 
        [System.Diagnostics.Conditional("TRAVE")]
        private void DecNestingCount() {
            string indent = IndentString;
            if (indent.Length>1) {
                try {
                    indent = indent.Substring(1);
                }
                catch {
                    indent = string.Empty;
                }
            }
            if (indent.Length==0) {
                indent = "< ";
            }
            IndentString = indent;
        }
 
        private string GetNestingString() {
            return IndentString;
        }
 
        [System.Diagnostics.Conditional("TRAVE")]
        private void ValidatePush(string name) {
            if (GlobalLog.s_DebugCallNesting) {
                Object obj = _ThreadNesting[Thread.CurrentThread.GetHashCode()];
                ThreadInfoData threadInfo = obj as ThreadInfoData;
                if (threadInfo == null) {
                    return;
                }
                threadInfo.NestingStack.Push(name);
            }
        }
 
        [System.Diagnostics.Conditional("TRAVE")]
        private void ValidatePop(string name) {
            if (GlobalLog.s_DebugCallNesting) {
                try {
                    Object obj = _ThreadNesting[Thread.CurrentThread.GetHashCode()];
                    ThreadInfoData threadInfo = obj as ThreadInfoData;
                    if (threadInfo == null) {
                        return;
                    }
                    if (threadInfo.NestingStack.Count == 0) {
                        PrintLine("++++====" + "Poped Empty Stack for :"+name);
                    }
                    string popedName = (string) threadInfo.NestingStack.Pop();
                    string [] parsedList = popedName.Split(new char [] {'(',')',' ','.',':',',','#'});
                    foreach (string element in parsedList) {
                        if (element != null && element.Length > 1 && name.IndexOf(element) != -1) {
                            return;
                        }
                    }
                    PrintLine("++++====" + "Expected:" + popedName + ": got :" + name + ": StackSize:"+threadInfo.NestingStack.Count);
                    // relevel the stack
                    while(threadInfo.NestingStack.Count>0) {
                        string popedName2 = (string) threadInfo.NestingStack.Pop();
                        string [] parsedList2 = popedName2.Split(new char [] {'(',')',' ','.',':',',','#'});
                        foreach (string element2 in parsedList2) {
                            if (element2 != null && element2.Length > 1 && name.IndexOf(element2) != -1) {
                                return;
                            }
                        }
                    }
                }
                catch {
                    PrintLine("++++====" + "ValidatePop failed for: "+name);
                }
            }
        }
 
 
        ~LoggingObject() {
            if(!_Finalized) {
                _Finalized = true;
                lock(this) {
                    DumpArray(true);
                }
            }
        }
 
 
    } // class LoggingObject
 
    internal static class TraveHelper {
        private static readonly string Hexizer = "0x{0:x}";
        internal static string ToHex(object value) {
            return String.Format(Hexizer, value);
        }
    }
#endif // TRAVE
 
#if TRAVE 
    internal class IntegerSwitch : BooleanSwitch {
        public IntegerSwitch(string switchName, string switchDescription) : base(switchName, switchDescription) {
        }
        public new int Value {
            get {
                return base.SwitchSetting;
            }
        }
    }
 
#endif
 
    [Flags]
    internal enum ThreadKinds
    {
        Unknown        = 0x0000,
 
        // Mutually exclusive.
        User           = 0x0001,     // Thread has entered via an API.
        System         = 0x0002,     // Thread has entered via a system callback (e.g. completion port) or is our own thread.
 
        // Mutually exclusive.
        Sync           = 0x0004,     // Thread should block.
        Async          = 0x0008,     // Thread should not block.
 
        // Mutually exclusive, not always known for a user thread.  Never changes.
        Timer          = 0x0010,     // Thread is the timer thread.  (Can't call user code.)
        CompletionPort = 0x0020,     // Thread is a ThreadPool completion-port thread.
        Worker         = 0x0040,     // Thread is a ThreadPool worker thread.
        Finalization   = 0x0080,     // Thread is the finalization thread.
        Other          = 0x0100,     // Unknown source.
 
        OwnerMask      = User | System,
        SyncMask       = Sync | Async,
        SourceMask     = Timer | CompletionPort | Worker | Finalization | Other,
 
        // Useful "macros"
        SafeSources    = SourceMask & ~(Timer | Finalization),  // Methods that "unsafe" sources can call must be explicitly marked.
        ThreadPool     = CompletionPort | Worker,               // Like Thread.CurrentThread.IsThreadPoolThread
    }
 
    /// <internalonly/>
    /// <devdoc>
    /// </devdoc>
    internal static class GlobalLog {
 
        //
        // Logging Initalization - I need to disable Logging code, and limit
        //  the effect it has when it is dissabled, so I use a bool here.
        //
        //  This can only be set when the logging code is built and enabled.
        //  By specifing the "CSC_DEFINES=/D:TRAVE" in the build environment,
        //  this code will be built and then checks against an enviroment variable
        //  and a BooleanSwitch to see if any of the two have enabled logging.
        //
 
        private static BaseLoggingObject Logobject = GlobalLog.LoggingInitialize();
#if TRAVE
        internal static LocalDataStoreSlot s_ThreadIdSlot;
        internal static bool s_UseThreadId;
        internal static bool s_UseTimeSpan;
        internal static bool s_DumpWebData;
        internal static bool s_UsePerfCounter;
        internal static bool s_DebugCallNesting;
        internal static bool s_DumpToConsole;
        internal static int s_MaxDumpSize;
        internal static string s_RootDirectory;
 
        //
        // Logging Config Variables -  below are list of consts that can be used to config
        //  the logging,
        //
 
        // Max number of lines written into a buffer, before a save is invoked
        // s_DumpToConsole disables.
        public const int MaxLinesBeforeSave = 0;
 
#endif
        [ReliabilityContract(Consistency.MayCorruptAppDomain, Cer.None)]
        private static BaseLoggingObject LoggingInitialize() {
 
#if DEBUG
            if (GetSwitchValue("SystemNetLogging", "System.Net logging module", false) &&
                GetSwitchValue("SystemNetLog_ConnectionMonitor", "System.Net connection monitor thread", false)) {
                InitConnectionMonitor();
            }
#endif // DEBUG
#if TRAVE
            // by default we'll log to c:\temp\ so that non interactive services (like w3wp.exe) that don't have environment
            // variables can easily be debugged, note that the ACLs of the directory might need to be adjusted
            if (!GetSwitchValue("SystemNetLog_OverrideDefaults", "System.Net log override default settings", false)) {
                s_ThreadIdSlot = Thread.AllocateDataSlot();
                s_UseThreadId = true;
                s_UseTimeSpan = true;
                s_DumpWebData = true;
                s_MaxDumpSize = 256;
                s_UsePerfCounter = true;
                s_DebugCallNesting = true;
                s_DumpToConsole = false;
                s_RootDirectory = "C:\\Temp\\";
                return new LoggingObject();
            }
            if (GetSwitchValue("SystemNetLogging", "System.Net logging module", false)) {
                s_ThreadIdSlot = Thread.AllocateDataSlot();
                s_UseThreadId = GetSwitchValue("SystemNetLog_UseThreadId", "System.Net log display system thread id", false);
                s_UseTimeSpan = GetSwitchValue("SystemNetLog_UseTimeSpan", "System.Net log display ticks as TimeSpan", false);
                s_DumpWebData = GetSwitchValue("SystemNetLog_DumpWebData", "System.Net log display HTTP send/receive data", false);
                s_MaxDumpSize = GetSwitchValue("SystemNetLog_MaxDumpSize", "System.Net log max size of display data", 256);
                s_UsePerfCounter = GetSwitchValue("SystemNetLog_UsePerfCounter", "System.Net log use QueryPerformanceCounter() to get ticks ", false);
                s_DebugCallNesting = GetSwitchValue("SystemNetLog_DebugCallNesting", "System.Net used to debug call nesting", false);
                s_DumpToConsole = GetSwitchValue("SystemNetLog_DumpToConsole", "System.Net log to console", false);
                s_RootDirectory = GetSwitchValue("SystemNetLog_RootDirectory", "System.Net root directory of log file", string.Empty);
                return new LoggingObject();
            }
#endif // TRAVE
            return new BaseLoggingObject();
        }
 
 
#if TRAVE 
        private static string GetSwitchValue(string switchName, string switchDescription, string defaultValue) {
            new EnvironmentPermission(PermissionState.Unrestricted).Assert();
            try {
                defaultValue = Environment.GetEnvironmentVariable(switchName);
            }
            finally {
                EnvironmentPermission.RevertAssert();
            }
            return defaultValue;
        }
 
        private static int GetSwitchValue(string switchName, string switchDescription, int defaultValue) {
            IntegerSwitch theSwitch = new IntegerSwitch(switchName, switchDescription);
            if (theSwitch.Enabled) {
                return theSwitch.Value;
            }
            new EnvironmentPermission(PermissionState.Unrestricted).Assert();
            try {
                string environmentVar = Environment.GetEnvironmentVariable(switchName);
                if (environmentVar!=null) {
                    defaultValue = Int32.Parse(environmentVar.Trim(), CultureInfo.InvariantCulture);
                }
            }
            finally {
                EnvironmentPermission.RevertAssert();
            }
            return defaultValue;
        }
 
#endif
 
#if TRAVE || DEBUG
        private static bool GetSwitchValue(string switchName, string switchDescription, bool defaultValue) {
            BooleanSwitch theSwitch = new BooleanSwitch(switchName, switchDescription);
            new EnvironmentPermission(PermissionState.Unrestricted).Assert();
            try {
                if (theSwitch.Enabled) {
                    return true;
                }
                string environmentVar = Environment.GetEnvironmentVariable(switchName);
                defaultValue = environmentVar!=null && environmentVar.Trim()=="1";
            }
            catch (ConfigurationException) { }
            finally {
                EnvironmentPermission.RevertAssert();
            }
            return defaultValue;
        }
#endif // TRAVE || DEBUG
 
 
        // Enables thread tracing, detects mis-use of threads.
#if DEBUG
        [ThreadStatic]
        private static Stack<ThreadKinds> t_ThreadKindStack;
 
        private static Stack<ThreadKinds> ThreadKindStack
        {
            get
            {
                if (t_ThreadKindStack == null)
                {
                    t_ThreadKindStack = new Stack<ThreadKinds>();
                }
                return t_ThreadKindStack;
            }
        }
#endif
                
        internal static ThreadKinds CurrentThreadKind
        {
            get
            {
#if DEBUG
                return ThreadKindStack.Count > 0 ? ThreadKindStack.Peek() : ThreadKinds.Other;
#else
                return ThreadKinds.Unknown;
#endif
            }
        }
 
#if DEBUG
        // ifdef'd instead of conditional since people are forced to handle the return value.
        // [Conditional("DEBUG")]
        [ReliabilityContract(Consistency.MayCorruptAppDomain, Cer.None)]
        internal static IDisposable SetThreadKind(ThreadKinds kind)
        {
            if ((kind & ThreadKinds.SourceMask) != ThreadKinds.Unknown)
            {
                throw new InternalException();
            }
 
            // Ignore during shutdown.
            if (NclUtilities.HasShutdownStarted)
            {
                return null;
            }
 
            ThreadKinds threadKind = CurrentThreadKind;
            ThreadKinds source = threadKind & ThreadKinds.SourceMask;
 
#if TRAVE
            // Special warnings when doing dangerous things on a thread.
            if ((threadKind & ThreadKinds.User) != 0 && (kind & ThreadKinds.System) != 0)
            {
                Print("WARNING: Thread changed from User to System; user's thread shouldn't be hijacked.");
            }
 
            if ((threadKind & ThreadKinds.Async) != 0 && (kind & ThreadKinds.Sync) != 0)
            {
                Print("WARNING: Thread changed from Async to Sync, may block an Async thread.");
            }
            else if ((threadKind & (ThreadKinds.Other | ThreadKinds.CompletionPort)) == 0 && (kind & ThreadKinds.Sync) != 0)
            {
                Print("WARNING: Thread from a limited resource changed to Sync, may deadlock or bottleneck.");
            }
#endif

            ThreadKindStack.Push(
                (((kind & ThreadKinds.OwnerMask) == 0 ? threadKind : kind) & ThreadKinds.OwnerMask) |
                (((kind & ThreadKinds.SyncMask) == 0 ? threadKind : kind) & ThreadKinds.SyncMask) |
                (kind & ~(ThreadKinds.OwnerMask | ThreadKinds.SyncMask)) |
                source);
 
#if TRAVE
            if (CurrentThreadKind != threadKind)
            {
                Print("Thread becomes:(" + CurrentThreadKind.ToString() + ")");
            }
#endif

            return new ThreadKindFrame();
        }
 
        private class ThreadKindFrame : IDisposable
        {
            private int m_FrameNumber;
 
            internal ThreadKindFrame()
            {
                m_FrameNumber = ThreadKindStack.Count;
            }
 
            void IDisposable.Dispose()
            {
                // Ignore during shutdown.
                if (NclUtilities.HasShutdownStarted)
                {
                    return;
                }
 
                if (m_FrameNumber != ThreadKindStack.Count)
                {
                    throw new InternalException();
                }
 
                ThreadKinds previous = ThreadKindStack.Pop();
 
#if TRAVE
                if (CurrentThreadKind != previous)
                {
                    Print("Thread reverts:(" + CurrentThreadKind.ToString() + ")");
                }
#endif
            }
        }
#endif
 
        [Conditional("DEBUG")]
        [ReliabilityContract(Consistency.MayCorruptAppDomain, Cer.None)]
        internal static void SetThreadSource(ThreadKinds source)
        {
#if DEBUG
            if ((source & ThreadKinds.SourceMask) != source || source == ThreadKinds.Unknown)
            {
                throw new ArgumentException("Must specify the thread source.", "source");
            }
 
            if (ThreadKindStack.Count == 0)
            {
                ThreadKindStack.Push(source);
                return;
            }
 
            if (ThreadKindStack.Count > 1)
            {
                Print("WARNING: SetThreadSource must be called at the base of the stack, or the stack has been corrupted.");
                while (ThreadKindStack.Count > 1)
                {
                    ThreadKindStack.Pop();
                }
            }
 
            if (ThreadKindStack.Peek() != source)
            {
                // SQL can fail to clean up the stack, leaving the default Other at the bottom.  Replace it.
                Print("WARNING: The stack has been corrupted.");
                ThreadKinds last = ThreadKindStack.Pop() & ThreadKinds.SourceMask;
                Assert(last == source || last == ThreadKinds.Other, "Thread source changed.|Was:({0}) Now:({1})", last, source);
                ThreadKindStack.Push(source);
            }
#endif
        }
 
        [Conditional("DEBUG")]
        [ReliabilityContract(Consistency.MayCorruptAppDomain, Cer.None)]
        internal static void ThreadContract(ThreadKinds kind, string errorMsg)
        {
            ThreadContract(kind, ThreadKinds.SafeSources, errorMsg);
        }
 
        [Conditional("DEBUG")]
        [ReliabilityContract(Consistency.MayCorruptAppDomain, Cer.None)]
        internal static void ThreadContract(ThreadKinds kind, ThreadKinds allowedSources, string errorMsg)
        {
            if ((kind & ThreadKinds.SourceMask) != ThreadKinds.Unknown || (allowedSources & ThreadKinds.SourceMask) != allowedSources)
            {
                throw new InternalException();
            }
 
            ThreadKinds threadKind = CurrentThreadKind;
            Assert((threadKind & allowedSources) != 0, errorMsg, "Thread Contract Violation.|Expected source:({0}) Actual source:({1})", allowedSources , threadKind & ThreadKinds.SourceMask);
            Assert((threadKind & kind) == kind, errorMsg, "Thread Contract Violation.|Expected kind:({0}) Actual kind:({1})", kind, threadKind & ~ThreadKinds.SourceMask);
        }
 
#if DEBUG
        // Enables auto-hang detection, which will "snap" a log on hang
        internal static bool EnableMonitorThread = false;
 
        // Default value for hang timer
#if FEATURE_PAL // ROTORTODO - after speedups (like real JIT and GC) remove this
        public const int DefaultTickValue = 1000*60*5; // 5 minutes
#else
        public const int DefaultTickValue = 1000*60; // 60 secs
#endif // FEATURE_PAL
#endif // DEBUG
 
        [System.Diagnostics.Conditional("TRAVE")]
        public static void AddToArray(string msg) {
#if TRAVE
            GlobalLog.Logobject.PrintLine(msg);
#endif
        }
 
        [System.Diagnostics.Conditional("TRAVE")]
        public static void Ignore(object msg) {
        }
 
        [System.Diagnostics.Conditional("TRAVE")]
        [ReliabilityContract(Consistency.MayCorruptAppDomain, Cer.None)]
        public static void Print(string msg) {
#if TRAVE
            GlobalLog.Logobject.PrintLine(msg);
#endif
        }
 
        [System.Diagnostics.Conditional("TRAVE")]
        public static void PrintHex(string msg, object value) {
#if TRAVE
            GlobalLog.Logobject.PrintLine(msg+TraveHelper.ToHex(value));
#endif
        }
 
        [System.Diagnostics.Conditional("TRAVE")]
        public static void Enter(string func) {
#if TRAVE
            GlobalLog.Logobject.EnterFunc(func + "(*none*)");
#endif
        }
 
        [System.Diagnostics.Conditional("TRAVE")]
        public static void Enter(string func, string parms) {
#if TRAVE
            GlobalLog.Logobject.EnterFunc(func + "(" + parms + ")");
#endif
        }
 
        [Conditional("DEBUG")]
        [Conditional("_FORCE_ASSERTS")]
        [ReliabilityContract(Consistency.MayCorruptAppDomain, Cer.None)]
        public static void Assert(bool condition, string messageFormat, params object[] data)
        {
            if (!condition)
            {
                string fullMessage = string.Format(CultureInfo.InvariantCulture, messageFormat, data);
                int pipeIndex = fullMessage.IndexOf('|');
                if (pipeIndex == -1)
                {
                    Assert(fullMessage);
                }
                else
                {
                    int detailLength = fullMessage.Length - pipeIndex - 1;
                    Assert(fullMessage.Substring(0, pipeIndex), detailLength > 0 ? fullMessage.Substring(pipeIndex + 1, detailLength) : null);
                }
            }
        }
 
        [Conditional("DEBUG")]
        [Conditional("_FORCE_ASSERTS")]
        [ReliabilityContract(Consistency.MayCorruptAppDomain, Cer.None)]
        public static void Assert(string message)
        {
            Assert(message, null);
        }
 
        [Conditional("DEBUG")]
        [Conditional("_FORCE_ASSERTS")]
        [ReliabilityContract(Consistency.MayCorruptAppDomain, Cer.None)]
        public static void Assert(string message, string detailMessage)
        {
            try
            {
                Print("Assert: " + message + (!string.IsNullOrEmpty(detailMessage) ? ": " + detailMessage : ""));
                Print("*******");
                Logobject.DumpArray(false);
            }
            finally
            {
#if DEBUG && !STRESS
                Debug.Assert(false, message, detailMessage);
#else
                UnsafeNclNativeMethods.DebugBreak();
                Debugger.Break();
#endif
            }
        }
 
 
        [System.Diagnostics.Conditional("TRAVE")]
        public static void LeaveException(string func, Exception exception) {
#if TRAVE
            GlobalLog.Logobject.LeaveFunc(func + " exception " + ((exception!=null) ? exception.Message : String.Empty));
#endif
        }
 
        [System.Diagnostics.Conditional("TRAVE")]
        public static void Leave(string func) {
#if TRAVE
            GlobalLog.Logobject.LeaveFunc(func + " returns ");
#endif
        }
 
        [System.Diagnostics.Conditional("TRAVE")]
        public static void Leave(string func, string result) {
#if TRAVE
            GlobalLog.Logobject.LeaveFunc(func + " returns " + result);
#endif
        }
 
        [System.Diagnostics.Conditional("TRAVE")]
        public static void Leave(string func, int returnval) {
#if TRAVE
            GlobalLog.Logobject.LeaveFunc(func + " returns " + returnval.ToString());
#endif
        }
 
        [System.Diagnostics.Conditional("TRAVE")]
        public static void Leave(string func, bool returnval) {
#if TRAVE
            GlobalLog.Logobject.LeaveFunc(func + " returns " + returnval.ToString());
#endif
        }
 
        [System.Diagnostics.Conditional("TRAVE")]
        public static void DumpArray() {
#if TRAVE
            GlobalLog.Logobject.DumpArray(true);
#endif
        }
 
        [System.Diagnostics.Conditional("TRAVE")]
        public static void Dump(byte[] buffer) {
#if TRAVE
            Logobject.Dump(buffer, 0, buffer!=null ? buffer.Length : -1);
#endif
        }
 
        [System.Diagnostics.Conditional("TRAVE")]
        public static void Dump(byte[] buffer, int length) {
#if TRAVE
            Logobject.Dump(buffer, 0, length);
#endif
        }
 
        [System.Diagnostics.Conditional("TRAVE")]
        public static void Dump(byte[] buffer, int offset, int length) {
#if TRAVE
            Logobject.Dump(buffer, offset, length);
#endif
        }
 
        [System.Diagnostics.Conditional("TRAVE")]
        public static void Dump(IntPtr buffer, int offset, int length) {
#if TRAVE
            Logobject.Dump(buffer, offset, length);
#endif
        }
 
#if DEBUG
        private class HttpWebRequestComparer : IComparer {
            public int Compare(
                   object x1,
                   object y1
                   ) {
 
                HttpWebRequest x = (HttpWebRequest) x1;
                HttpWebRequest y = (HttpWebRequest) y1;
 
                if (x.GetHashCode() == y.GetHashCode()) {
                    return 0;
                } else if (x.GetHashCode() < y.GetHashCode()) {
                    return -1;
                } else if (x.GetHashCode() > y.GetHashCode()) {
                    return 1;
                }
 
                return 0;
            }
        }
 
        private class ConnectionMonitorEntry {
            public HttpWebRequest m_Request;
            public int m_Flags;
            public DateTime m_TimeAdded;
            public Connection m_Connection;
 
            public ConnectionMonitorEntry(HttpWebRequest request, Connection connection, int flags) {
                m_Request = request;
                m_Connection = connection;
                m_Flags = flags;
                m_TimeAdded = DateTime.Now;
            }
        }
 
        private static volatile ManualResetEvent s_ShutdownEvent;
        private static volatile SortedList s_RequestList;
 
        internal const int WaitingForReadDoneFlag = 0x1;
#endif
 
#if DEBUG
        private static void ConnectionMonitor() {
            while(! s_ShutdownEvent.WaitOne(DefaultTickValue, false)) {
                if (GlobalLog.EnableMonitorThread) {
#if TRAVE
                    GlobalLog.Logobject.LoggingMonitorTick();
#endif
                }
 
                int hungCount = 0;
                lock (s_RequestList) {
                    DateTime dateNow = DateTime.Now;
                    DateTime dateExpired = dateNow.AddSeconds(-DefaultTickValue);
                    foreach (ConnectionMonitorEntry monitorEntry in s_RequestList.GetValueList() ) {
                        if (monitorEntry != null &&
                            (dateExpired > monitorEntry.m_TimeAdded))
                        {
                            hungCount++;
#if TRAVE
                            GlobalLog.Print("delay:" + (dateNow - monitorEntry.m_TimeAdded).TotalSeconds +
                                " req#" + monitorEntry.m_Request.GetHashCode() +
                                " cnt#" + monitorEntry.m_Connection.GetHashCode() +
                                " flags:" + monitorEntry.m_Flags);
 
#endif
                            monitorEntry.m_Connection.DebugMembers(monitorEntry.m_Request.GetHashCode());
                        }
                    }
                }
                Assert(hungCount == 0, "Warning: Hang Detected on Connection(s) of greater than {0} ms.  {1} request(s) hung.|Please Dump System.Net.GlobalLog.s_RequestList for pending requests, make sure your streams are calling Close(), and that your destination server is up.", DefaultTickValue, hungCount);
            }
        }
#endif // DEBUG
 
#if DEBUG
        [ReliabilityContract(Consistency.MayCorruptAppDomain, Cer.None)]
        internal static void AppDomainUnloadEvent(object sender, EventArgs e) {
            s_ShutdownEvent.Set();
        }
#endif
 
#if DEBUG
        [System.Diagnostics.Conditional("DEBUG")]
        private static void InitConnectionMonitor() {
            s_RequestList = new SortedList(new HttpWebRequestComparer(), 10);
            s_ShutdownEvent = new ManualResetEvent(false);
            AppDomain.CurrentDomain.DomainUnload += new EventHandler(AppDomainUnloadEvent);
            AppDomain.CurrentDomain.ProcessExit += new EventHandler(AppDomainUnloadEvent);
            Thread threadMonitor = new Thread(new ThreadStart(ConnectionMonitor));
            threadMonitor.IsBackground = true;
            threadMonitor.Start();
        }
#endif
 
        [System.Diagnostics.Conditional("DEBUG")]
        internal static void DebugAddRequest(HttpWebRequest request, Connection connection, int flags) {
#if DEBUG
            // null if the connection monitor is off
            if(s_RequestList == null)
                return;
 
            lock(s_RequestList) {
                Assert(!s_RequestList.ContainsKey(request), "s_RequestList.ContainsKey(request)|A HttpWebRequest should not be submitted twice.");
 
                ConnectionMonitorEntry requestEntry =
                    new ConnectionMonitorEntry(request, connection, flags);
 
                try {
                    s_RequestList.Add(request, requestEntry);
                } catch {
                }
            }
#endif
        }
 
        [System.Diagnostics.Conditional("DEBUG")]
        internal static void DebugRemoveRequest(HttpWebRequest request) {
#if DEBUG
            // null if the connection monitor is off
            if(s_RequestList == null)
                return;
 
            lock(s_RequestList) {
                Assert(s_RequestList.ContainsKey(request), "!s_RequestList.ContainsKey(request)|A HttpWebRequest should not be removed twice.");
 
                try {
                    s_RequestList.Remove(request);
                } catch {
                }
            }
#endif
        }
 
        [System.Diagnostics.Conditional("DEBUG")]
        internal static void DebugUpdateRequest(HttpWebRequest request, Connection connection, int flags) {
#if DEBUG
            // null if the connection monitor is off
            if(s_RequestList == null)
                return;
 
            lock(s_RequestList) {
                if(!s_RequestList.ContainsKey(request)) {
                    return;
                }
 
                ConnectionMonitorEntry requestEntry =
                    new ConnectionMonitorEntry(request, connection, flags);
 
                try {
                    s_RequestList.Remove(request);
                    s_RequestList.Add(request, requestEntry);
                } catch {
                }
            }
#endif
        }
    } // class GlobalLog
} // namespace System.Net