File: Util\Debug.cs
Project: ndp\fx\src\xsp\system\Web\System.Web.csproj (System.Web)
//------------------------------------------------------------------------------
// <copyright file="Debug.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
namespace System.Web.Util {
    using Microsoft.Win32;
    using Microsoft.Win32.SafeHandles;
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Globalization;
    using System.Reflection;
    using System.Runtime.ConstrainedExecution;
    using System.Runtime.InteropServices;
    using System.Security;
    using System.Security.Permissions;
    using System.Threading;
    using System.Runtime.Versioning;
 
    internal static class Debug {
 
        internal const string   TAG_INTERNAL = "Internal";
        internal const string   TAG_EXTERNAL = "External";
        internal const string   TAG_ALL      = "*";
 
        internal const string   DATE_FORMAT = @"yyyy/MM/dd HH:mm:ss.ffff";
        internal const string   TIME_FORMAT = @"HH:mm:ss:ffff";
 
        // Some of these APIs must be always available, not #ifdefed away.
        [SuppressUnmanagedCodeSecurity]
        private static partial class NativeMethods {
            [DllImport("kernel32.dll")]
            internal extern static bool IsDebuggerPresent();
        }
 
#if DBG
        private static partial class NativeMethods {
            [DllImport("kernel32.dll")]
            internal extern static void DebugBreak();
 
            [DllImport("kernel32.dll")]
            internal extern static int GetCurrentProcessId();
 
            [DllImport("kernel32.dll")]
            internal extern static int GetCurrentThreadId();
 
            [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
            internal extern static IntPtr GetCurrentProcess();
 
            [DllImport("kernel32.dll", SetLastError=true)]
            internal extern static bool TerminateProcess(HandleRef processHandle, int exitCode);
 
            [DllImport("kernel32.dll", CharSet=CharSet.Auto, BestFitMapping=false)]
            internal extern static void OutputDebugString(string message);
 
            internal const int PM_NOREMOVE = 0x0000;
            internal const int PM_REMOVE = 0x0001;
 
            [StructLayout(LayoutKind.Sequential)]
            internal struct MSG {
                internal IntPtr   hwnd;
                internal int      message;
                internal IntPtr   wParam;
                internal IntPtr   lParam;
                internal int      time;
                internal int      pt_x;
                internal int      pt_y;
            }
 
            [DllImport("user32.dll", CharSet=System.Runtime.InteropServices.CharSet.Auto)]
            internal extern static bool PeekMessage([In, Out] ref MSG msg, HandleRef hwnd, int msgMin, int msgMax, int remove);
 
            internal const int 
                MB_OK = 0x00000000,
                MB_OKCANCEL = 0x00000001,
                MB_ABORTRETRYIGNORE = 0x00000002,
                MB_YESNOCANCEL = 0x00000003,
                MB_YESNO = 0x00000004,
                MB_RETRYCANCEL = 0x00000005,
                MB_ICONHAND = 0x00000010,
                MB_ICONQUESTION = 0x00000020,
                MB_ICONEXCLAMATION = 0x00000030,
                MB_ICONASTERISK = 0x00000040,
                MB_USERICON = 0x00000080,
                MB_ICONWARNING = 0x00000030,
                MB_ICONERROR = 0x00000010,
                MB_ICONINFORMATION = 0x00000040,
                MB_DEFBUTTON1 = 0x00000000,
                MB_DEFBUTTON2 = 0x00000100,
                MB_DEFBUTTON3 = 0x00000200,
                MB_DEFBUTTON4 = 0x00000300,
                MB_APPLMODAL = 0x00000000,
                MB_SYSTEMMODAL = 0x00001000,
                MB_TASKMODAL = 0x00002000,
                MB_HELP = 0x00004000,
                MB_NOFOCUS = 0x00008000,
                MB_SETFOREGROUND = 0x00010000,
                MB_DEFAULT_DESKTOP_ONLY = 0x00020000,
                MB_TOPMOST = 0x00040000,
                MB_RIGHT = 0x00080000,
                MB_RTLREADING = 0x00100000,
                MB_SERVICE_NOTIFICATION = 0x00200000,
                MB_SERVICE_NOTIFICATION_NT3X = 0x00040000,
                MB_TYPEMASK = 0x0000000F,
                MB_ICONMASK = 0x000000F0,
                MB_DEFMASK = 0x00000F00,
                MB_MODEMASK = 0x00003000,
                MB_MISCMASK = 0x0000C000;
 
            internal const int
                IDOK = 1,
                IDCANCEL = 2,
                IDABORT = 3,
                IDRETRY = 4,
                IDIGNORE = 5,
                IDYES = 6,
                IDNO = 7,
                IDCLOSE = 8,
                IDHELP = 9;
 
 
            [DllImport("user32.dll", CharSet=CharSet.Auto, BestFitMapping=false)]
            internal extern static int MessageBox(HandleRef hWnd, string text, string caption, int type);
 
            internal static readonly IntPtr HKEY_LOCAL_MACHINE = unchecked((IntPtr)(int)0x80000002);
 
            internal const int READ_CONTROL           = 0x00020000;
            internal const int STANDARD_RIGHTS_READ   = READ_CONTROL;
 
            internal const int SYNCHRONIZE            = 0x00100000;
 
            internal const int KEY_QUERY_VALUE        = 0x0001;
            internal const int KEY_ENUMERATE_SUB_KEYS = 0x0008;
            internal const int KEY_NOTIFY             = 0x0010;
 
 
            internal const int KEY_READ               = ((STANDARD_RIGHTS_READ |
                                                               KEY_QUERY_VALUE |
                                                               KEY_ENUMERATE_SUB_KEYS |
                                                               KEY_NOTIFY)
                                                              &
                                                              (~SYNCHRONIZE));
 
            internal const int REG_NOTIFY_CHANGE_NAME       = 1;
            internal const int REG_NOTIFY_CHANGE_LAST_SET   = 4;
 
 
            [DllImport("advapi32.dll", CharSet=CharSet.Auto, BestFitMapping=false, SetLastError=true)]
            internal extern static int RegOpenKeyEx(IntPtr hKey, string lpSubKey, int ulOptions, int samDesired, out SafeRegistryHandle hkResult);
 
            [DllImport("advapi32.dll", ExactSpelling=true, SetLastError=true)]
            internal extern static int RegNotifyChangeKeyValue(SafeRegistryHandle hKey, bool watchSubTree, uint notifyFilter, SafeWaitHandle regEvent, bool async);
        }
 
        private class SafeRegistryHandle : SafeHandleZeroOrMinusOneIsInvalid {
 
            // Note: Officially -1 is the recommended invalid handle value for
            // registry keys, but we'll also get back 0 as an invalid handle from
            // RegOpenKeyEx.
 
            [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode=true)]
            [ResourceExposure(ResourceScope.Machine)]
            internal SafeRegistryHandle() : base(true) {}
 
            [DllImport("advapi32.dll"),
             SuppressUnmanagedCodeSecurity,
             ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
            private static extern int RegCloseKey(IntPtr hKey);
 
            override protected bool ReleaseHandle()
            {
                // Returns a Win32 error code, 0 for success
                int r = RegCloseKey(handle);
                return r == 0;
            }
        }
 
        private enum TagValue {
            Disabled = 0,
            Enabled = 1,
            Break = 2,
 
            Min = Disabled,
            Max = Break,
        }
 
        private const string            TAG_ASSERT = "Assert";
        private const string            TAG_ASSERT_BREAK = "AssertBreak";
 
        private const string            TAG_DEBUG_VERBOSE = "DebugVerbose";
        private const string            TAG_DEBUG_MONITOR = "DebugMonitor";
        private const string            TAG_DEBUG_PREFIX = "DebugPrefix";
        private const string            TAG_DEBUG_THREAD_PREFIX = "DebugThreadPrefix";
 
        private const string            PRODUCT = "Microsoft .NET Framework";
        private const string            COMPONENT = "System.Web";
 
        private static string           s_regKeyName = @"Software\Microsoft\ASP.NET\Debug";
        private static string           s_listenKeyName = @"Software\Microsoft";
 
        private static bool             s_assert;
        private static bool             s_assertBreak;
 
        private static bool             s_includePrefix;
        private static bool             s_includeThreadPrefix;
        private static bool             s_monitor;
 
        private static object           s_lock;
        private static volatile bool    s_inited;
        private static ReadOnlyCollection<Tag>  s_tagDefaults;
        private static List<Tag>        s_tags;
 
        private static AutoResetEvent       s_notifyEvent;
        private static RegisteredWaitHandle s_waitHandle;
        private static SafeRegistryHandle   s_regHandle;
        private static bool                 s_stopMonitoring;
 
        private static Hashtable        s_tableAlwaysValidate;
        private static Type[]           s_DumpArgs;
        private static Type[]           s_ValidateArgs;
 
        private class Tag {
            string      _name;
            TagValue    _value;
            int         _prefixLength;
 
            internal Tag(string name, TagValue value) {
                _name = name;
                _value = value;
 
                if (_name[_name.Length - 1] == '*') {
                    _prefixLength = _name.Length - 1;
                }
                else {
                    _prefixLength = -1;
                }
            }
 
            internal string Name {
                get {return _name;}
            }
 
            internal TagValue Value {
                get {return _value;}
            }
 
            internal int PrefixLength {
                get {return _prefixLength;}
            }
        }
 
        static Debug() {
            s_lock = new object();
        }
 
        private static void EnsureInit() {
            bool continueInit = false;
 
            if (!s_inited) {
                lock (s_lock) {
                    if (!s_inited) {
                        s_tableAlwaysValidate = new Hashtable();
                        s_DumpArgs = new Type[1] {typeof(string)}; 
                        s_ValidateArgs = new Type[0];              
 
                        List<Tag> tagDefaults = new List<Tag>();
                        tagDefaults.Add(new Tag(TAG_ALL, TagValue.Disabled));
                        tagDefaults.Add(new Tag(TAG_INTERNAL, TagValue.Enabled));
                        tagDefaults.Add(new Tag(TAG_EXTERNAL, TagValue.Enabled));
                        tagDefaults.Add(new Tag(TAG_ASSERT, TagValue.Break));
                        tagDefaults.Add(new Tag(TAG_ASSERT_BREAK, TagValue.Disabled));
                        tagDefaults.Add(new Tag(TAG_DEBUG_VERBOSE, TagValue.Enabled));
                        tagDefaults.Add(new Tag(TAG_DEBUG_MONITOR, TagValue.Enabled));
                        tagDefaults.Add(new Tag(TAG_DEBUG_PREFIX, TagValue.Enabled));
                        tagDefaults.Add(new Tag(TAG_DEBUG_THREAD_PREFIX, TagValue.Enabled));
 
                        s_tagDefaults = tagDefaults.AsReadOnly();
                        s_tags = new List<Tag>(s_tagDefaults);
                        GetBuiltinTagValues();
 
                        continueInit = true;
                        s_inited = true;
                    }
                }
            }
 
            // Work to do outside the init lock.
            if (continueInit) {
                ReadTagsFromRegistry();
                Trace(TAG_DEBUG_VERBOSE, "Debugging package initialized");
 
                // Need to read tags before starting to monitor in order to get TAG_DEBUG_MONITOR
                StartRegistryMonitor();
            }
        }
 
        private static bool StringEqualsIgnoreCase(string s1, string s2) {
            return StringComparer.OrdinalIgnoreCase.Equals(s1, s2);
        }
 
        [RegistryPermission(SecurityAction.Assert, Unrestricted=true)]
        private static void WriteTagsToRegistry() {
            try {
                using (RegistryKey key = Registry.LocalMachine.CreateSubKey(s_regKeyName)) {
                    List<Tag> tags = s_tags;
                    foreach (Tag tag in tags) {
                        key.SetValue(tag.Name, tag.Value, RegistryValueKind.DWord);
                    }
                }
            }
            catch {
            }
        }
 
        private static void GetBuiltinTagValues() {
            // Use GetTagValue instead of IsTagEnabled because it does not call EnsureInit
            // and potentially recurse.
            s_assert              = (GetTagValue(TAG_ASSERT) != TagValue.Disabled);
            s_assertBreak         = (GetTagValue(TAG_ASSERT_BREAK) != TagValue.Disabled);
            s_includePrefix       = (GetTagValue(TAG_DEBUG_PREFIX) != TagValue.Disabled);
            s_includeThreadPrefix = (GetTagValue(TAG_DEBUG_THREAD_PREFIX) != TagValue.Disabled);
            s_monitor             = (GetTagValue(TAG_DEBUG_MONITOR) != TagValue.Disabled);
        }
 
        [RegistryPermission(SecurityAction.Assert, Unrestricted=true)]
        private static void ReadTagsFromRegistry() {
            lock (s_lock) {
                try {
                    List<Tag> tags = new List<Tag>(s_tagDefaults);
                    string[] names = null;
 
                    bool writeTags = false;
                    using (RegistryKey key = Registry.LocalMachine.OpenSubKey(s_regKeyName, false)) {
                        if (key != null) {
                            names = key.GetValueNames();
                            foreach (string name in names) {
                                TagValue value = TagValue.Disabled;
                                try {
                                    TagValue keyvalue = (TagValue) key.GetValue(name);
                                    if (TagValue.Min <= keyvalue && keyvalue <= TagValue.Max) {
                                        value = keyvalue;
                                    }
                                    else {
                                        writeTags = true;
                                    }
                                }
                                catch {
                                    writeTags = true;
                                }
 
                                // Add tag to list, making sure it is unique.
                                Tag tag = new Tag(name, (TagValue) value);
                                bool found = false;
                                for (int i = 0; i < s_tagDefaults.Count; i++) {
                                    if (StringEqualsIgnoreCase(name, tags[i].Name)) {
                                        found = true;
                                        tags[i] = tag;
                                        break;
                                    }
                                }
 
                                if (!found) {
                                    tags.Add(tag);
                                }
                            }
                        }
                    }
 
                    s_tags = tags;
                    GetBuiltinTagValues();
 
                    // Write tags out if there was an invalid value or 
                    // not all default tags are present.
                    if (writeTags || (names != null && names.Length < tags.Count)) {
                        WriteTagsToRegistry();
                    }
                }
                catch {
                    s_tags = new List<Tag>(s_tagDefaults);
                }
            }
        }
 
        private static void StartRegistryMonitor() {
            if (!s_monitor) {
                Trace(TAG_DEBUG_VERBOSE, "WARNING: Registry monitoring disabled, changes during process execution will not be recognized."); 
                return;
            }
 
            Trace(TAG_DEBUG_VERBOSE, "Monitoring registry key " + s_listenKeyName + " for changes.");
 
            // Event used to notify of changes.
            s_notifyEvent = new AutoResetEvent(false);
 
            // Register a wait on the event.
            s_waitHandle = ThreadPool.RegisterWaitForSingleObject(s_notifyEvent, OnRegChangeKeyValue, null, -1, false);
 
            // Monitor the registry.
            MonitorRegistryForOneChange();
        }
 
        private static void StopRegistryMonitor() {
            // Cleanup allocated handles
            s_stopMonitoring = true;
 
            if (s_regHandle != null) {
                s_regHandle.Close();
                s_regHandle = null;
            }
 
            if (s_waitHandle != null) {
                s_waitHandle.Unregister(s_notifyEvent);
                s_waitHandle = null;
            }
 
            if (s_notifyEvent != null) {
                s_notifyEvent.Close();
                s_notifyEvent = null;
            }
 
            Trace(TAG_DEBUG_VERBOSE, "Registry monitoring stopped."); 
        }
 
        public static void OnRegChangeKeyValue(object state, bool timedOut) {
            if (!s_stopMonitoring) {
                if (timedOut) {
                    StopRegistryMonitor();
                }
                else {
                    // Monitor again
                    MonitorRegistryForOneChange();
 
                    // Once we're monitoring, read the changes to the registry.
                    // We have to do this after we start monitoring in order
                    // to catch all changes to the registry.
                    ReadTagsFromRegistry();
                }
            }
        }
 
        private static void MonitorRegistryForOneChange() {
            // Close the open reg handle
            if (s_regHandle != null) {
                s_regHandle.Close();
                s_regHandle = null;
            }
 
            // Open the reg key
            int result = NativeMethods.RegOpenKeyEx(NativeMethods.HKEY_LOCAL_MACHINE, s_listenKeyName, 0, NativeMethods.KEY_READ, out s_regHandle);
            if (result != 0) {
                StopRegistryMonitor();
                return;
            }
 
            // Listen for changes.
            result = NativeMethods.RegNotifyChangeKeyValue(
                    s_regHandle, 
                    true, 
                    NativeMethods.REG_NOTIFY_CHANGE_NAME | NativeMethods.REG_NOTIFY_CHANGE_LAST_SET,
                    s_notifyEvent.SafeWaitHandle,
                    true);
 
            if (result != 0) {
                StopRegistryMonitor();
            }
        }
 
        private static Tag FindMatchingTag(string name, bool exact) {
            List<Tag> tags = s_tags;
 
            // Look for exact match first
            foreach (Tag tag in tags) {
                if (StringEqualsIgnoreCase(name, tag.Name)) {
                    return tag;
                }
            }
 
            if (exact) {
                return null;
            }
 
            Tag longestTag = null;
            int longestPrefix = -1;
            foreach (Tag tag in tags) {
                if (    tag.PrefixLength > longestPrefix && 
                        0 == string.Compare(name, 0, tag.Name, 0, tag.PrefixLength, StringComparison.OrdinalIgnoreCase)) {
 
                    longestTag = tag;
                    longestPrefix = tag.PrefixLength;
                }
            }
 
            return longestTag;
        }
 
        private static TagValue GetTagValue(string name) {
            Tag tag = FindMatchingTag(name, false);
            if (tag != null) {
                return tag.Value;
            }
            else {
                return TagValue.Disabled;
            }
        }
 
        [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
        private static bool TraceBreak(string tagName, string message, Exception e, bool includePrefix) {
            EnsureInit();
 
            TagValue tagValue = GetTagValue(tagName);
            if (tagValue == TagValue.Disabled) {
                return false;
            }
 
            bool isAssert = object.ReferenceEquals(tagName, TAG_ASSERT);
            if (isAssert) {
                tagName = "";
            }
 
            string exceptionMessage = null;
            if (e != null) {
                string httpCode = null;
                string errorCode = null;
 
                if (e is HttpException) {
                    httpCode = " _httpCode=" + ((HttpException)e).GetHttpCode().ToString(CultureInfo.InvariantCulture) + " ";
                }
 
                if (e is ExternalException) {
                    // note that HttpExceptions are ExternalExceptions
                    errorCode = "_hr=0x" + ((ExternalException)e).ErrorCode.ToString("x", CultureInfo.InvariantCulture);
                }
 
                // Use e.ToString() in order to get inner exception
                if (errorCode != null) {
                    exceptionMessage = "Exception " + e.ToString() + "\n" + httpCode + errorCode;
                }
                else {
                    exceptionMessage = "Exception " + e.ToString();
                }
            }
 
            if (string.IsNullOrEmpty(message) & exceptionMessage != null) {
                message = exceptionMessage;
                exceptionMessage = null;
            }
 
            string traceFormat;
            int idThread = 0;
            int idProcess = 0;
 
            if (!includePrefix || !s_includePrefix) {
                traceFormat = "{4}\n{5}";
            }
            else {
                if (s_includeThreadPrefix) {
                    idThread = NativeMethods.GetCurrentThreadId();
                    idProcess = NativeMethods.GetCurrentProcessId();
                    traceFormat = "[0x{0:x}.{1:x} {2} {3}] {4}\n{5}";
                }
                else {
                    traceFormat = "[{2} {3}] {4}\n{5}";
                }
            }
 
            string suffix = "";
            if (exceptionMessage != null) {
                suffix += exceptionMessage + "\n";
            }
 
            bool doBreak = (tagValue == TagValue.Break);
            if (doBreak && !isAssert) {
                suffix += "Breaking into debugger...\n";
            }
 
            string traceMessage = string.Format(CultureInfo.InvariantCulture, traceFormat, idProcess, idThread, COMPONENT, tagName, message, suffix);
 
            NativeMethods.OutputDebugString(traceMessage);
 
            return doBreak;
        }
 
        private class MBResult {
            internal int Result;
        }
 
        [ResourceExposure(ResourceScope.None)]
        static bool DoAssert(string message) {
            if (!s_assert) {
                return false;
            }
 
            // Skip 2 frames - one for this function, one for
            // the public Assert function that called this function.
            System.Diagnostics.StackFrame frame = new System.Diagnostics.StackFrame(2, true);
            System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace(2, true);
 
            string fileName = frame.GetFileName();
            int lineNumber = frame.GetFileLineNumber();
 
            string traceFormat;
            if (!string.IsNullOrEmpty(fileName)) {
                traceFormat = "ASSERTION FAILED: {0}\nFile: {1}:{2}\nStack trace:\n{3}";
            }
            else {
                traceFormat = "ASSERTION FAILED: {0}\nStack trace:\n{3}";
            }
 
            string traceMessage = string.Format(CultureInfo.InvariantCulture, traceFormat, message, fileName, lineNumber, trace.ToString());
 
            if (!TraceBreak(TAG_ASSERT, traceMessage, null, true)) {
                // If the value of "Assert" is not TagValue.Break, then don't even ask user.
                return false;
            }
 
            if (s_assertBreak) {
                // If "AssertBreak" is enabled, then always break.
                return true;
            }
 
            string dialogFormat;
            if (!string.IsNullOrEmpty(fileName)) {
                dialogFormat = 
@"Failed expression: {0}
File: {1}:{2}
Component: {3}
PID={4} TID={5}
Stack trace:
{6}
 
A=Exit process R=Debug I=Continue";
            }
            else {
                dialogFormat = 
@"Failed expression: {0}
(no file information available)
Component: {3}
PID={4} TID={5}
Stack trace:
{6}
 
A=Exit process R=Debug I=Continue";
            }
 
            string dialogMessage = string.Format(
                CultureInfo.InvariantCulture,
                dialogFormat,
                message,
                fileName, lineNumber,
                COMPONENT,
                NativeMethods.GetCurrentProcessId(), NativeMethods.GetCurrentThreadId(),
                trace.ToString());
 
            MBResult mbResult = new MBResult();
 
            Thread thread = new Thread(
                delegate() {
                    for (int i = 0; i < 100; i++) {
                        NativeMethods.MSG msg = new NativeMethods.MSG();
                        NativeMethods.PeekMessage(ref msg, new HandleRef(mbResult, IntPtr.Zero), 0, 0, NativeMethods.PM_REMOVE);
                    }
 
                    mbResult.Result = NativeMethods.MessageBox(new HandleRef(mbResult, IntPtr.Zero), dialogMessage, PRODUCT + " Assertion",                
                        NativeMethods.MB_SERVICE_NOTIFICATION | 
                        NativeMethods.MB_TOPMOST |
                        NativeMethods.MB_ABORTRETRYIGNORE | 
                        NativeMethods.MB_ICONEXCLAMATION);
                }
            );
 
            thread.Start();
            thread.Join();
 
            if (mbResult.Result == NativeMethods.IDABORT) {
                IntPtr currentProcess = NativeMethods.GetCurrentProcess();
                NativeMethods.TerminateProcess(new HandleRef(mbResult, currentProcess), 1);
            }
 
            return mbResult.Result == NativeMethods.IDRETRY;
        }
#endif
 
        //
        // Sends the message to the debugger if the tag is enabled.
        // Also breaks into the debugger the value of the tag is 2 (TagValue.Break).
        //
        [System.Diagnostics.Conditional("DBG")]
        internal static void Trace(string tagName, string message) {
#if DBG
            if (TraceBreak(tagName, message, null, true)) {
                Break();
            }
#endif
        }
 
        //
        // Sends the message to the debugger if the tag is enabled.
        // Also breaks into the debugger the value of the tag is 2 (TagValue.Break).
        //
        [System.Diagnostics.Conditional("DBG")]
        internal static void Trace(string tagName, string message, bool includePrefix) {
#if DBG
            if (TraceBreak(tagName, message, null, includePrefix)) {
                Break();
            }
#endif
        }
 
        //
        // Sends the message to the debugger if the tag is enabled.
        // Also breaks into the debugger the value of the tag is 2 (TagValue.Break).
        //
        [System.Diagnostics.Conditional("DBG")]
        internal static void Trace(string tagName, string message, Exception e) {
#if DBG
            if (TraceBreak(tagName, message, e, true)) {
                Break();
            }
#endif
        }
 
        //
        // Sends the message to the debugger if the tag is enabled.
        // Also breaks into the debugger the value of the tag is 2 (TagValue.Break).
        //
        [System.Diagnostics.Conditional("DBG")]
        internal static void Trace(string tagName, Exception e) {
#if DBG
            if (TraceBreak(tagName, null, e, true)) {
                Break();
            }
#endif
        }
 
        //
        // Sends the message to the debugger if the tag is enabled.
        // Also breaks into the debugger the value of the tag is 2 (TagValue.Break).
        //
        [System.Diagnostics.Conditional("DBG")]
        internal static void Trace(string tagName, string message, Exception e, bool includePrefix) {
#if DBG
            if (TraceBreak(tagName, message, e, includePrefix)) {
                Break();
            }
#endif
        }
 
#if DBG
#endif
 
        [System.Diagnostics.Conditional("DBG")]
        public static void TraceException(String tagName, Exception e) {
#if DBG
            if (TraceBreak(tagName, null, e, true)) {
                Break();
            }
#endif
        }
 
 
        //
        // If the assertion is false and the 'Assert' tag is enabled:
        //      * Send a message to the debugger.
        //      * If the 'AssertBreak' tag is enabled, immediately break into the debugger
        //      * Else display a dialog box asking the user to Abort, Retry (break), or Ignore
        //
        [System.Diagnostics.Conditional("DBG")]
        internal static void Assert(bool assertion, string message) {
#if DBG
            EnsureInit();
            if (assertion == false) {
                if (DoAssert(message)) {
                    Break();
                }
            }
#endif
        }
 
 
        //
        // If the assertion is false and the 'Assert' tag is enabled:
        //      * Send a message to the debugger.
        //      * If the 'AssertBreak' tag is enabled, immediately break into the debugger
        //      * Else display a dialog box asking the user to Abort, Retry (break), or Ignore
        //
        [System.Diagnostics.Conditional("DBG")]
        [ResourceExposure(ResourceScope.None)]
        internal static void Assert(bool assertion) {
#if DBG
            EnsureInit();
            if (assertion == false) {
                if (DoAssert(null)) {
                    Break();
                }
            }
#endif
        }
 
        //
        // Like Assert, but the assertion is always considered to be false.
        //
        [System.Diagnostics.Conditional("DBG")]
        [ResourceExposure(ResourceScope.None)]
        internal static void Fail(string message) {
#if DBG
            Assert(false, message);
#endif
        }
 
        //
        // Returns true if the tag is enabled, false otherwise.
        // Note that the tag needn't be an exact match.
        //
        [ResourceExposure(ResourceScope.None)]
        internal static bool IsTagEnabled(string tagName) {
#if DBG
            EnsureInit();
            return GetTagValue(tagName) != TagValue.Disabled;
#else
            return false;
#endif
        }
 
        //
        // Returns true if the tag present. 
        // This function chekcs for an exact match.
        //
        [ResourceExposure(ResourceScope.None)]
        internal static bool IsTagPresent(string tagName) {
#if DBG
            EnsureInit();
            return FindMatchingTag(tagName, true) != null;
#else
            return false;
#endif
        }
 
        // Returns a value indicating whether a debugger is attached to the process.
        // We don't #ifdef this away since we might need to change behavior based
        // on whether one is attached.
        internal static bool IsDebuggerPresent() {
            return (NativeMethods.IsDebuggerPresent() || System.Diagnostics.Debugger.IsAttached);
        }
 
        //
        // Breaks into the debugger, or launches one if not yet attached.
        //
        [System.Diagnostics.Conditional("DBG")]
        [ResourceExposure(ResourceScope.None)]
        internal static void Break() {
#if DBG
            if (NativeMethods.IsDebuggerPresent()) {
                NativeMethods.DebugBreak();
            }
            else if (!System.Diagnostics.Debugger.IsAttached) {
                System.Diagnostics.Debugger.Launch();
            }
            else {
                System.Diagnostics.Debugger.Break();            
            }
#endif
        }
 
 
        //
        // Tells the debug system to always validate calls for a
        // particular tag. This is useful for temporarily enabling
        // validation in stress tests or other situations where you
        // may not have control over the debug tags that are enabled
        // on a particular machine.
        // 
        [System.Diagnostics.Conditional("DBG")]
        internal static void AlwaysValidate(string tagName) {
#if DBG
            EnsureInit();
            s_tableAlwaysValidate[tagName] = tagName;
#endif
        }
 
 
        //
        // Throws an exception if the assertion is not valid.
        // Use this function from a DebugValidate method where
        // you would otherwise use Assert.
        //
        [System.Diagnostics.Conditional("DBG")]
        internal static void CheckValid(bool assertion, string message) {
#if DBG
            if (!assertion) {
                throw new Exception(message);
            }
#endif
        }
 
        //
        // Calls DebugValidate on an object if such a method exists.
        //
        // This method should be used from implementations of DebugValidate
        // where it is unknown whether an object has a DebugValidate method.
        // For example, the DoubleLink class uses it to validate the
        // item of type Object which it points to.
        //
        // This method should NOT be used when code wants to conditionally
        // validate an object and have a failed validation caught in an assert.
        // Use Debug.Validate(tagName, obj) for that purpose.
        //
        [System.Diagnostics.Conditional("DBG")]
        [ResourceExposure(ResourceScope.None)]
        internal static void Validate(Object obj) {
#if DBG
            Type        type;
            MethodInfo  mi;
 
            if (obj != null) {
                type = obj.GetType();
 
                mi = type.GetMethod(
                        "DebugValidate", 
                        BindingFlags.NonPublic | BindingFlags.Instance,
                        null,
                        s_ValidateArgs,
                        null);
 
                if (mi != null) {
                    object[] tempIndex = null;
                    mi.Invoke(obj, tempIndex);
                }
            }
#endif
        }
 
        // Ensures that the bounds provided to an array-consuming method are correct.
        // Because this method is on the Debug type, it should only be used in debugging
        // code and should not be depended on for user input sanitization (since the
        // compiler won't emit calls to it.)
        [System.Diagnostics.Conditional("DBG")]
        internal static void ValidateArrayBounds<T>(T[] array, int offset, int count) {
#if DBG
            // these are the same checks as in the ArraySegment<T> ctor
            Assert(array != null, "Array cannot be null.");
            Assert(offset >= 0, "Offset must be non-negative.");
            Assert(count >= 0, "Count must be non-negative.");
            Assert(array.Length - offset >= count, "Invalid offset.");
#endif
        }
 
        //
        // Validates an object is the "Validate" tag is enabled, or when
        // the "Validate" tag is not disabled and the given 'tag' is enabled.
        // An Assertion is made if the validation fails.
        //
        [System.Diagnostics.Conditional("DBG")]
        [ResourceExposure(ResourceScope.None)]
        internal static void Validate(string tagName, Object obj) {
#if DBG
            EnsureInit();
 
            if (    obj != null 
                    && (    IsTagEnabled("Validate")
                            ||  (   !IsTagPresent("Validate") 
                                    && (   s_tableAlwaysValidate[tagName] != null 
                                           ||  IsTagEnabled(tagName))))) {
                try {
                    Debug.Validate(obj);
                }
                catch (Exception e) {
                    Debug.Assert(false, "Validate failed: " + e.InnerException.Message);
                }
#pragma warning disable 1058
                catch {
                    Debug.Assert(false, "Validate failed.  Non-CLS compliant exception caught.");
                }
#pragma warning restore 1058
            }
#endif
        }
 
#if DBG

        //
        // Calls DebugDescription on an object to get its description.
        //
        // This method should only be used in implementations of DebugDescription
        // where it is not known whether a nested objects has an implementation
        // of DebugDescription. For example, the double linked list class uses
        // GetDescription to get the description of the item it points to.
        //
        // This method should NOT be used when you want to conditionally
        // dump an object. Use Debug.Dump instead.
        //
        // @param obj      The object to call DebugDescription on. May be null.
        // @param indent   A prefix for each line in the description. This is used
        //                 to allow the nested display of objects within other objects.
        //                 The indent is usually a multiple of four spaces.
        //
        // @return         The description.
        //
        [ReflectionPermission(SecurityAction.Assert, Unrestricted=true)]
        internal static string GetDescription(Object obj, string indent) {
            string      description;
            Type        type;
            MethodInfo  mi;
            Object[]   parameters;
 
            if (obj == null) {
                description = "\n";
            }
            else {
                type = obj.GetType();
                mi = type.GetMethod(
                        "DebugDescription", 
                        BindingFlags.NonPublic | BindingFlags.Instance,
                        null,
                        s_DumpArgs,
                        null);
                        
                if (mi == null || mi.ReturnType != typeof(string)) {
                    description = indent + obj.ToString();
                }
                else {
                    parameters = new Object[1] {(Object) indent};
                    description = (string) mi.Invoke(obj, parameters);
                }
            }
 
            return description;
        }
#endif
 
 
        // 
        // Dumps an object to the debugger if the "Dump" tag is enabled,
        // or if the "Dump" tag is not present and the 'tag' is enabled.
        // 
        // @param tagName  The tag to Dump with.
        // @param obj  The object to dump.
        // 
        [System.Diagnostics.Conditional("DBG")]
        internal static void Dump(string tagName, Object obj) {
#if DBG
            EnsureInit();
 
            string  description;
            string  traceTag = null;
            bool    dumpEnabled, dumpPresent;
 
            if (obj != null) {
                dumpEnabled = IsTagEnabled("Dump");
                dumpPresent = IsTagPresent("Dump");
                if (dumpEnabled || !dumpPresent) {
                    if (IsTagEnabled(tagName)) {
                        traceTag = tagName;
                    }
                    else if (dumpEnabled) {
                        traceTag = "Dump";
                    }
 
                    if (traceTag != null) {
                        description = GetDescription(obj, string.Empty);
                        Debug.Trace(traceTag, "Dump\n" + description);
                    }
                }
            }
#endif
        }
 
#if DBG
        static internal string ToStringMaybeNull(object o) {
            if (o != null) {
                return o.ToString();
            }
 
            return "<null>";
        }
#endif
 
        static internal string FormatUtcDate(DateTime utcTime) {
#if DBG
            DateTime localTime = DateTimeUtil.ConvertToLocalTime(utcTime);
            return localTime.ToString(DATE_FORMAT, CultureInfo.InvariantCulture);
#else
            return string.Empty;
#endif
        }
 
        static internal string FormatLocalDate(DateTime localTime) {
#if DBG
            return localTime.ToString(DATE_FORMAT, CultureInfo.InvariantCulture);
#else
            return string.Empty;
#endif
        }
    }
}