File: System\Xml\Xsl\IlGen\XmlILTrace.cs
Web Access
Project: src\src\libraries\System.Private.Xml\src\System.Private.Xml.csproj (System.Private.Xml)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Globalization;
using System.IO;
using System.Runtime.Versioning;
using System.Security;
using System.Xml;
using System.Xml.Xsl.Qil;
 
// This class is only for debug purposes so there is no need to have it in Retail builds
#if DEBUG
namespace System.Xml.Xsl.IlGen
{
    /// <summary>
    /// Helper class that facilitates tracing of ILGen.
    /// </summary>
    internal static class XmlILTrace
    {
        private const int MAX_REWRITES = 200;
 
        /// <summary>
        /// Check environment variable in order to determine whether to write out trace files.  This really should be a
        /// check of the configuration file, but System.Xml does not yet have a good tracing story.
        /// </summary>
        private static volatile string? s_dirName;
        private static volatile bool s_alreadyCheckedEnabled;
 
        /// <summary>
        /// True if tracing has been enabled (environment variable set).
        /// </summary>
        public static bool IsEnabled
        {
            // SxS: This property poses potential SxS issue. However the class is used only in debug builds (it won't
            // get compiled into ret build) so it's OK to suppress the SxS warning.
            get
            {
                // If environment variable has not yet been checked, do so now
                if (!s_alreadyCheckedEnabled)
                {
                    try
                    {
                        s_dirName = Environment.GetEnvironmentVariable("XmlILTrace");
                    }
                    catch (SecurityException)
                    {
                        // If user does not have access to environment variables, tracing will remain disabled
                    }
 
                    s_alreadyCheckedEnabled = true;
                }
 
                return (s_dirName != null);
            }
        }
 
        /// <summary>
        /// If tracing is enabled, this method will delete the contents of "filename" in preparation for append
        /// operations.
        /// </summary>
        public static void PrepareTraceWriter(string fileName)
        {
            if (!IsEnabled)
                return;
 
            File.Delete(s_dirName + "\\" + fileName);
        }
 
        /// <summary>
        /// If tracing is enabled, this method will open a TextWriter over "fileName" and return it.  Otherwise,
        /// null will be returned.
        /// </summary>
        public static TextWriter? GetTraceWriter(string fileName)
        {
            if (!IsEnabled)
                return null;
 
            return new StreamWriter(s_dirName + "\\" + fileName, true);
        }
 
        /// <summary>
        /// Serialize Qil tree to "fileName", in the directory identified by "dirName".
        /// </summary>
        public static void WriteQil(QilExpression qil, string fileName)
        {
            if (!IsEnabled)
                return;
 
            XmlWriter w = XmlWriter.Create(s_dirName + "\\" + fileName);
            try
            {
                WriteQil(qil, w);
            }
            finally
            {
                w.Close();
            }
        }
 
        /// <summary>
        /// Trace ILGen optimizations and log them to "fileName".
        /// </summary>
        public static void TraceOptimizations(QilExpression qil, string fileName)
        {
            if (!IsEnabled)
                return;
 
            XmlWriter w = XmlWriter.Create(s_dirName + "\\" + fileName);
 
            w.WriteStartDocument();
            w.WriteProcessingInstruction("xml-stylesheet", "href='qilo.xslt' type='text/xsl'");
            w.WriteStartElement("QilOptimizer");
            w.WriteAttributeString("timestamp", DateTime.Now.ToString(CultureInfo.InvariantCulture));
            WriteQilRewrite(qil, w, null);
 
            try
            {
                // Then, rewrite the graph until "done" or some max value is reached.
                for (int i = 1; i < MAX_REWRITES; i++)
                {
                    QilExpression qilTemp = (QilExpression)(new QilCloneVisitor(qil.Factory).Clone(qil));
 
                    XmlILOptimizerVisitor visitor = new XmlILOptimizerVisitor(qilTemp, !qilTemp.IsDebug);
                    visitor.Threshold = i;
                    qilTemp = visitor.Optimize();
 
                    // In debug code, ensure that QIL after N steps is correct
                    QilValidationVisitor.Validate(qilTemp);
 
                    // Trace the rewrite
                    WriteQilRewrite(qilTemp, w, OptimizationToString(visitor.LastReplacement));
 
                    if (visitor.ReplacementCount < i)
                        break;
                }
            }
            catch (Exception e)
            {
                if (!XmlException.IsCatchableException(e))
                {
                    throw;
                }
                w.WriteElementString("Exception", null, e.ToString());
                throw;
            }
            finally
            {
                w.WriteEndElement();
                w.WriteEndDocument();
                w.Flush();
                w.Close();
            }
        }
 
        /// <summary>
        /// Serialize Qil tree to writer "w".
        /// </summary>
        private static void WriteQil(QilExpression qil, XmlWriter w)
        {
            QilXmlWriter qw = new QilXmlWriter(w);
            qw.ToXml(qil);
        }
 
        /// <summary>
        /// Serialize rewritten Qil tree to writer "w".
        /// </summary>
        private static void WriteQilRewrite(QilExpression qil, XmlWriter w, string? rewriteName)
        {
            w.WriteStartElement("Diff");
            if (rewriteName != null)
                w.WriteAttributeString("rewrite", rewriteName);
            WriteQil(qil, w);
            w.WriteEndElement();
        }
 
        /// <summary>
        /// Get friendly string description of an ILGen optimization.
        /// </summary>
        private static string OptimizationToString(int opt)
        {
            string s = Enum.GetName(typeof(XmlILOptimization), opt)!;
            if (s.StartsWith("Introduce", StringComparison.Ordinal))
            {
                return string.Concat(s.AsSpan(9), " introduction");
            }
            else if (s.StartsWith("Eliminate", StringComparison.Ordinal))
            {
                return string.Concat(s.AsSpan(9), " elimination");
            }
            else if (s.StartsWith("Commute", StringComparison.Ordinal))
            {
                return string.Concat(s.AsSpan(7), " commutation");
            }
            else if (s.StartsWith("Fold", StringComparison.Ordinal))
            {
                return string.Concat(s.AsSpan(4), " folding");
            }
            else if (s.StartsWith("Misc", StringComparison.Ordinal))
            {
                return s.Substring(4);
            }
            return s;
        }
    }
}
#endif