Copy System.Diagnostics.Debug implementation from corefx
authorJan Kotas <jkotas@microsoft.com>
Fri, 31 Mar 2017 18:18:51 +0000 (11:18 -0700)
committerJan Kotas <jkotas@microsoft.com>
Fri, 31 Mar 2017 18:18:51 +0000 (11:18 -0700)
Commit migrated from https://github.com/dotnet/coreclr/commit/d37fd15d5c6388c770742610f651fe3ae16b7eb1

src/coreclr/src/mscorlib/src/Interop/Windows/kernel32/Interop.OutputDebugString.cs [new file with mode: 0644]
src/coreclr/src/mscorlib/src/System/Diagnostics/Debug.Unix.cs [new file with mode: 0644]
src/coreclr/src/mscorlib/src/System/Diagnostics/Debug.Windows.cs [new file with mode: 0644]
src/coreclr/src/mscorlib/src/System/Diagnostics/Debug.cs [new file with mode: 0644]

diff --git a/src/coreclr/src/mscorlib/src/Interop/Windows/kernel32/Interop.OutputDebugString.cs b/src/coreclr/src/mscorlib/src/Interop/Windows/kernel32/Interop.OutputDebugString.cs
new file mode 100644 (file)
index 0000000..8da50ff
--- /dev/null
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.InteropServices;
+
+internal partial class Interop
+{
+    internal partial class Kernel32
+    {
+        [DllImport(Interop.Libraries.Kernel32, CharSet = CharSet.Unicode, EntryPoint = "OutputDebugStringW", ExactSpelling = true)]
+        internal static extern void OutputDebugString(string message);
+    }
+}
diff --git a/src/coreclr/src/mscorlib/src/System/Diagnostics/Debug.Unix.cs b/src/coreclr/src/mscorlib/src/System/Diagnostics/Debug.Unix.cs
new file mode 100644 (file)
index 0000000..6c9ec04
--- /dev/null
@@ -0,0 +1,120 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Win32.SafeHandles;
+
+namespace System.Diagnostics
+{
+    // Intentionally excluding visibility so it defaults to internal except for
+    // the one public version in System.Diagnostics.Debug which defines
+    // another version of this partial class with the public visibility 
+    static partial class Debug
+    {
+        private static string NewLine => "\n"; 
+
+        // internal and not readonly so that the tests can swap this out.
+        internal static IDebugLogger s_logger = new UnixDebugLogger();
+
+        // --------------
+        // PAL ENDS HERE
+        // --------------
+
+        internal sealed class UnixDebugLogger : IDebugLogger
+        {
+            private const string EnvVar_DebugWriteToStdErr = "COMPlus_DebugWriteToStdErr";
+            private static readonly bool s_shouldWriteToStdErr = 
+                Internal.Runtime.Augments.EnvironmentAugments.GetEnvironmentVariable(EnvVar_DebugWriteToStdErr) == "1";
+
+            public void ShowAssertDialog(string stackTrace, string message, string detailMessage)
+            {
+                if (Debugger.IsAttached)
+                {
+                    Debugger.Break();
+                }
+                else
+                {
+                    // TODO: #3708 Determine if/how to put up a dialog instead.
+                    var exc = new DebugAssertException(message, detailMessage, stackTrace);
+                    if (!s_shouldWriteToStdErr) 
+                    {
+                        // We always want to print out Debug.Assert failures to stderr, even if
+                        // !s_shouldWriteToStdErr, so if it wouldn't have been printed in
+                        // WriteCore (only when s_shouldWriteToStdErr), print it here.
+                        WriteToStderr(exc.Message);
+                    }
+                    throw exc;
+                }
+            }
+
+            public void WriteCore(string message)
+            {
+                WriteToDebugger(message);
+
+                if (s_shouldWriteToStdErr)
+                {
+                    WriteToStderr(message);
+                }
+            }
+
+            private static void WriteToDebugger(string message)
+            {
+                if (Debugger.IsLogging())
+                {
+                    Debugger.Log(0, null, message);
+                }
+                else
+                {
+                    Interop.Sys.SysLog(Interop.Sys.SysLogPriority.LOG_USER | Interop.Sys.SysLogPriority.LOG_DEBUG, "%s", message);
+                }
+            }
+
+            private static void WriteToStderr(string message)
+            {
+                // We don't want to write UTF-16 to a file like standard error.  Ideally we would transcode this
+                // to UTF8, but the downside of that is it pulls in a bunch of stuff into what is ideally
+                // a path with minimal dependencies (as to prevent re-entrency), so we'll take the strategy
+                // of just throwing away any non ASCII characters from the message and writing the rest
+
+                const int BufferLength = 256;
+
+                unsafe
+                {
+                    byte* buf = stackalloc byte[BufferLength];
+                    int bufCount;
+                    int i = 0;
+
+                    using (SafeFileHandle fileHandle = SafeFileHandleHelper.Open(() => Interop.Sys.Dup(Interop.Sys.FileDescriptors.STDERR_FILENO)))
+                    {
+                        while (i < message.Length)
+                        {
+                            for (bufCount = 0; bufCount < BufferLength && i < message.Length; i++)
+                            {
+                                if (message[i] <= 0x7F)
+                                {
+                                    buf[bufCount] = (byte)message[i];
+                                    bufCount++;
+                                }
+                            }
+
+                            int totalBytesWritten = 0;
+                            while (bufCount > 0)
+                            {
+                                int bytesWritten = Interop.Sys.Write(fileHandle, buf + totalBytesWritten, bufCount);
+                                if (bytesWritten < 0)
+                                {
+                                    // On error, simply stop writing the debug output.  This could commonly happen if stderr
+                                    // was piped to a program that ended before this program did, resulting in EPIPE errors.
+                                    return;
+                                }
+
+                                bufCount -= bytesWritten;
+                                totalBytesWritten += bytesWritten;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/coreclr/src/mscorlib/src/System/Diagnostics/Debug.Windows.cs b/src/coreclr/src/mscorlib/src/System/Diagnostics/Debug.Windows.cs
new file mode 100644 (file)
index 0000000..f0f589c
--- /dev/null
@@ -0,0 +1,79 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Security;
+
+namespace System.Diagnostics
+{
+    // Intentionally excluding visibility so it defaults to internal except for
+    // the one public version in System.Diagnostics.Debug which defines
+    // another version of this partial class with the public visibility 
+    static partial class Debug
+    {
+        private static string NewLine => "\r\n";
+
+        // internal and not read only so that the tests can swap this out.
+        internal static IDebugLogger s_logger = new WindowsDebugLogger();
+
+        // --------------
+        // PAL ENDS HERE
+        // --------------
+
+        internal sealed class WindowsDebugLogger : IDebugLogger
+        {
+            [SecuritySafeCritical]
+            public void ShowAssertDialog(string stackTrace, string message, string detailMessage)
+            {
+                if (Debugger.IsAttached)
+                {
+                    Debugger.Break();
+                }
+                else
+                {
+                    // TODO: #3708 Determine if/how to put up a dialog instead.
+                    throw new DebugAssertException(message, detailMessage, stackTrace);
+                }
+            }
+
+            public void WriteCore(string message)
+            {
+                // really huge messages mess up both VS and dbmon, so we chop it up into 
+                // reasonable chunks if it's too big. This is the number of characters 
+                // that OutputDebugstring chunks at.
+                const int WriteChunkLength = 4091;
+
+                // We don't want output from multiple threads to be interleaved.
+                lock (s_ForLock)
+                {
+                    if (message == null || message.Length <= WriteChunkLength)
+                    {
+                        WriteToDebugger(message);
+                    }
+                    else
+                    {
+                        int offset;
+                        for (offset = 0; offset < message.Length - WriteChunkLength; offset += WriteChunkLength)
+                        {
+                            WriteToDebugger(message.Substring(offset, WriteChunkLength));
+                        }
+                        WriteToDebugger(message.Substring(offset));
+                    }
+                }
+            }
+
+            [System.Security.SecuritySafeCritical]
+            private static void WriteToDebugger(string message)
+            {
+                if (Debugger.IsLogging())
+                {
+                    Debugger.Log(0, null, message);
+                }
+                else
+                {
+                    Interop.Kernel32.OutputDebugString(message ?? string.Empty);
+                }
+            }
+        }
+    }
+}
diff --git a/src/coreclr/src/mscorlib/src/System/Diagnostics/Debug.cs b/src/coreclr/src/mscorlib/src/System/Diagnostics/Debug.cs
new file mode 100644 (file)
index 0000000..5f893f8
--- /dev/null
@@ -0,0 +1,331 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// Do not remove this, it is needed to retain calls to these conditional methods in release builds
+#define DEBUG
+
+namespace System.Diagnostics
+{
+    /// <summary>
+    /// Provides a set of properties and methods for debugging code.
+    /// </summary>
+    static partial class Debug
+    {
+        private static readonly object s_lock = new object();
+
+        public static bool AutoFlush { get { return true; } set { } }
+
+        [ThreadStatic]
+        private static int s_indentLevel;
+        public static int IndentLevel
+        {
+            get
+            {
+                return s_indentLevel;
+            }
+            set
+            {
+                s_indentLevel = value < 0 ? 0 : value;
+            }
+        }
+
+        private static int s_indentSize = 4;
+        public static int IndentSize
+        {
+            get
+            {
+                return s_indentSize;
+            }
+            set
+            {
+                s_indentSize = value < 0 ? 0 : value;
+            }
+        }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void Close() { }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void Flush() { }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void Indent()
+        {
+            IndentLevel++;
+        }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void Unindent()
+        {
+            IndentLevel--;
+        }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void Print(string message)
+        {
+            Write(message);
+        }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void Print(string format, params object[] args)
+        {
+            Write(string.Format(null, format, args));
+        }
+
+        private static readonly object s_ForLock = new Object();
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void Assert(bool condition)
+        {
+            Assert(condition, string.Empty, string.Empty);
+        }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void Assert(bool condition, string message)
+        {
+            Assert(condition, message, string.Empty);
+        }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        [System.Security.SecuritySafeCritical]
+        public static void Assert(bool condition, string message, string detailMessage)
+        {
+            if (!condition)
+            {
+                string stackTrace;
+
+                try
+                {
+                    stackTrace = Internal.Runtime.Augments.EnvironmentAugments.StackTrace;
+                }
+                catch
+                {
+                    stackTrace = "";
+                }
+
+                WriteLine(FormatAssert(stackTrace, message, detailMessage));
+                s_logger.ShowAssertDialog(stackTrace, message, detailMessage);
+            }
+        }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void Fail(string message)
+        {
+            Assert(false, message, string.Empty);
+        }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void Fail(string message, string detailMessage)
+        {
+            Assert(false, message, detailMessage);
+        }
+
+        private static string FormatAssert(string stackTrace, string message, string detailMessage)
+        {
+            string newLine = GetIndentString() + NewLine;
+            return SR.DebugAssertBanner + newLine
+                   + SR.DebugAssertShortMessage + newLine
+                   + message + newLine
+                   + SR.DebugAssertLongMessage + newLine
+                   + detailMessage + newLine
+                   + stackTrace;
+        }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void Assert(bool condition, string message, string detailMessageFormat, params object[] args)
+        {
+            Assert(condition, message, string.Format(detailMessageFormat, args));
+        }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void WriteLine(string message)
+        {
+            Write(message + NewLine);
+        }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void Write(string message)
+        {
+            lock (s_lock)
+            {
+                if (message == null)
+                {
+                    WriteCore(string.Empty);
+                    return;
+                }
+                if (s_needIndent)
+                {
+                    message = GetIndentString() + message;
+                    s_needIndent = false;
+                }
+                WriteCore(message);
+                if (message.EndsWith(NewLine))
+                {
+                    s_needIndent = true;
+                }
+            }
+        }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void WriteLine(object value)
+        {
+            WriteLine(value?.ToString());
+        }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void WriteLine(object value, string category)
+        {
+            WriteLine(value?.ToString(), category);
+        }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void WriteLine(string format, params object[] args)
+        {
+            WriteLine(string.Format(null, format, args));
+        }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void WriteLine(string message, string category)
+        {
+            if (category == null)
+            {
+                WriteLine(message);
+            }
+            else
+            {
+                WriteLine(category + ":" + message);
+            }
+        }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void Write(object value)
+        {
+            Write(value?.ToString());
+        }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void Write(string message, string category)
+        {
+            if (category == null)
+            {
+                Write(message);
+            }
+            else
+            {
+                Write(category + ":" + message);
+            }
+        }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void Write(object value, string category)
+        {
+            Write(value?.ToString(), category);
+        }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void WriteIf(bool condition, string message)
+        {
+            if (condition)
+            {
+                Write(message);
+            }
+        }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void WriteIf(bool condition, object value)
+        {
+            if (condition)
+            {
+                Write(value);
+            }
+        }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void WriteIf(bool condition, string message, string category)
+        {
+            if (condition)
+            {
+                Write(message, category);
+            }
+        }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void WriteIf(bool condition, object value, string category)
+        {
+            if (condition)
+            {
+                Write(value, category);
+            }
+        }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void WriteLineIf(bool condition, object value)
+        {
+            if (condition)
+            {
+                WriteLine(value);
+            }
+        }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void WriteLineIf(bool condition, object value, string category)
+        {
+            if (condition)
+            {
+                WriteLine(value, category);
+            }
+        }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void WriteLineIf(bool condition, string message)
+        {
+            if (condition)
+            {
+                WriteLine(message);
+            }
+        }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void WriteLineIf(bool condition, string message, string category)
+        {
+            if (condition)
+            {
+                WriteLine(message, category);
+            }
+        }
+
+        private static bool s_needIndent;
+
+        private static string s_indentString;
+
+        private static string GetIndentString()
+        {
+            int indentCount = IndentSize * IndentLevel;
+            if (s_indentString?.Length == indentCount)
+            {
+                return s_indentString;
+            }
+            return s_indentString = new string(' ', indentCount);
+        }
+
+        private static void WriteCore(string message)
+        {
+            s_logger.WriteCore(message);
+        }
+
+        internal interface IDebugLogger
+        {
+            void ShowAssertDialog(string stackTrace, string message, string detailMessage);
+            void WriteCore(string message);
+        }
+
+        private sealed class DebugAssertException : Exception
+        {
+            internal DebugAssertException(string message, string detailMessage, string stackTrace) :
+                base(message + NewLine + detailMessage + NewLine + stackTrace)
+            {
+            }
+        }
+    }
+}