--- /dev/null
+// 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;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
--- /dev/null
+// 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);
+ }
+ }
+ }
+ }
+}
--- /dev/null
+// 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)
+ {
+ }
+ }
+ }
+}