From 8e690a261f4f167a9829a60c0e4989a64498fbf7 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Sat, 27 Mar 2021 00:11:34 -0700 Subject: [PATCH 01/98] Fix PKCS Crypto tests on Android (#50252) * Handle the fact that on Android, more validation failures are treated as PartialChain when doing CMS signing. * Fix copy-paste error for RSA SHA-512 PKCS1 padding * Change condition so we have it in netstandard builds. --- .../Security/Cryptography/RsaPaddingProcessor.cs | 2 +- .../System/Security/Cryptography/Pkcs/CmsSigner.cs | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/libraries/Common/src/System/Security/Cryptography/RsaPaddingProcessor.cs b/src/libraries/Common/src/System/Security/Cryptography/RsaPaddingProcessor.cs index f7aa5b4204b3..8ee516f1cf5a 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/RsaPaddingProcessor.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/RsaPaddingProcessor.cs @@ -39,7 +39,7 @@ internal sealed class RsaPaddingProcessor private static readonly byte[] s_digestInfoSha512 = { - 0x30, 0x41, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, + 0x30, 0x51, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40, }; diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSigner.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSigner.cs index d3d1cc650e85..fbe270792bc8 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSigner.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSigner.cs @@ -258,6 +258,7 @@ internal SignerInfoAsn Sign( X509Chain chain = new X509Chain(); chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags; + chain.ChainPolicy.VerificationTime = Certificate!.NotBefore; if (!chain.Build(Certificate!)) { @@ -265,7 +266,18 @@ internal SignerInfoAsn Sign( { if (status.Status == X509ChainStatusFlags.PartialChain) { - throw new CryptographicException(SR.Cryptography_Cms_IncompleteCertChain); + if (chain.ChainElements.Count == 0) + { + // On Android, we will fail with PartialChain to build a cert chain + // even if the failure is an untrusted root cert since the underlying platform + // does not provide a way to distinguish the failure. + // In that case, just use the provided cert. + certs.Add(Certificate!); + } + else + { + throw new CryptographicException(SR.Cryptography_Cms_IncompleteCertChain); + } } } } From 738cd9ad8fe0a6b800d16f8a66a446372b15f1b0 Mon Sep 17 00:00:00 2001 From: Jose Perez Rodriguez Date: Sat, 27 Mar 2021 08:59:20 -0700 Subject: [PATCH 02/98] Resolve remaining Xslt-related warnings (round 2) (#50211) * Resolve remaining Xslt-related warnings * Addressing final linker warning and adding annotations to ref assembly * Update src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/GenerateHelper.cs Co-authored-by: Eric Erhardt * Update src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlExtensionFunction.cs Co-authored-by: Eric Erhardt * Addressing PR feedback * kept -> preserved * Address remaining feedback Co-authored-by: Eric Erhardt --- .../src/ILLink/ILLink.Suppressions.xml | 18 ------------------ .../src/System/Xml/Xsl/IlGen/GenerateHelper.cs | 6 +++++- .../Xml/Xsl/Runtime/XmlExtensionFunction.cs | 9 +++++++-- .../System/Xml/Xsl/Runtime/XmlQueryContext.cs | 6 ++++++ .../System/Xml/Xsl/Runtime/XmlQueryRuntime.cs | 3 +++ .../src/System/Xml/Xsl/Runtime/XsltLibrary.cs | 9 ++++++++- .../src/System/Xml/Xsl/Xslt/QilGeneratorEnv.cs | 4 ++++ .../src/System/Xml/Xsl/Xslt/Scripts.cs | 2 ++ .../src/System/Xml/Xsl/Xslt/XslAstAnalyzer.cs | 3 +++ .../src/System/Xml/Xsl/XsltOld/Processor.cs | 3 +++ .../Xml/Xsl/XsltOld/XsltCompileContext.cs | 2 ++ .../src/System/Xml/Xslt/XsltArgumentList.cs | 7 +++++++ .../ref/System.Xml.ReaderWriter.cs | 2 ++ 13 files changed, 52 insertions(+), 22 deletions(-) diff --git a/src/libraries/System.Private.Xml/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Private.Xml/src/ILLink/ILLink.Suppressions.xml index 991377561048..40dbf4ac889e 100644 --- a/src/libraries/System.Private.Xml/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Private.Xml/src/ILLink/ILLink.Suppressions.xml @@ -313,23 +313,5 @@ member M:System.Xml.Serialization.XmlSerializationWriterILGen.WriteText(System.Xml.Serialization.SourceInfo,System.Xml.Serialization.TextAccessor) - - ILLink - IL2067 - member - M:System.Xml.Xsl.Runtime.XmlExtensionFunction.#ctor(System.String,System.String,System.Int32,System.Type,System.Reflection.BindingFlags) - - - ILLink - IL2067 - member - M:System.Xml.Xsl.Runtime.XmlExtensionFunctionTable.Bind(System.String,System.String,System.Int32,System.Type,System.Reflection.BindingFlags) - - - ILLink - IL2075 - member - M:System.Xml.Xsl.XsltOld.XsltCompileContext.GetExtentionMethod(System.String,System.String,System.Xml.XPath.XPathResultType[],System.Object@) - diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/GenerateHelper.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/GenerateHelper.cs index a3fb2822f6d1..e02699f4abe8 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/GenerateHelper.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/GenerateHelper.cs @@ -303,7 +303,11 @@ internal static class XmlILMethods public static readonly MethodInfo GetDataSource = typeof(XmlQueryContext).GetMethod("GetDataSource")!; public static readonly MethodInfo GetDefaultDataSource = typeof(XmlQueryContext).GetMethod("get_DefaultDataSource")!; public static readonly MethodInfo GetParam = typeof(XmlQueryContext).GetMethod("GetParameter")!; - public static readonly MethodInfo InvokeXsltLate = typeof(XmlQueryContext).GetMethod("InvokeXsltLateBoundFunction")!; + public static readonly MethodInfo InvokeXsltLate = GetInvokeXsltLateBoundFunction(); + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "Supressing warning about not having the RequiresUnreferencedCode attribute since this code path " + + "will only be emitting IL that will later be called by Transform() method which is already annotated as RequiresUnreferencedCode")] + private static MethodInfo GetInvokeXsltLateBoundFunction() => typeof(XmlQueryContext).GetMethod("InvokeXsltLateBoundFunction")!; // XmlILIndex public static readonly MethodInfo IndexAdd = typeof(XmlILIndex).GetMethod("Add")!; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlExtensionFunction.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlExtensionFunction.cs index ef7ddb38c996..da2e1b645d43 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlExtensionFunction.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlExtensionFunction.cs @@ -27,7 +27,12 @@ public XmlExtensionFunctionTable() _table = new Dictionary(); } - public XmlExtensionFunction Bind(string name, string namespaceUri, int numArgs, Type objectType, BindingFlags flags) + public XmlExtensionFunction Bind( + string name, + string namespaceUri, + int numArgs, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] Type objectType, + BindingFlags flags) { XmlExtensionFunction func; @@ -89,7 +94,7 @@ public XmlExtensionFunction(string name, string namespaceUri, MethodInfo meth) /// /// Constructor. /// - public XmlExtensionFunction(string name, string namespaceUri, int numArgs, Type objectType, BindingFlags flags) + public XmlExtensionFunction(string name, string namespaceUri, int numArgs, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.PublicMethods)] Type objectType, BindingFlags flags) { Init(name, namespaceUri, numArgs, objectType, flags); } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryContext.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryContext.cs index 752a94d36ec1..c696d59abe04 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryContext.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryContext.cs @@ -12,6 +12,8 @@ using System.Xml.Schema; using System.Xml.XPath; using System.Runtime.Versioning; +using System.Diagnostics.CodeAnalysis; +using System.Xml.Xsl.Xslt; namespace System.Xml.Xsl.Runtime { @@ -222,6 +224,8 @@ public object GetParameter(string localName, string namespaceUri) /// /// Return the extension object that is mapped to the specified namespace, or null if no object is mapped. /// + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = XsltArgumentList.ExtensionObjectSuppresion)] public object GetLateBoundObject(string namespaceUri) { return (_argList != null) ? _argList.GetExtensionObject(namespaceUri) : null; @@ -230,6 +234,7 @@ public object GetLateBoundObject(string namespaceUri) /// /// Return true if the late bound object identified by "namespaceUri" contains a method that matches "name". /// + [RequiresUnreferencedCode(Scripts.ExtensionFunctionCannotBeStaticallyAnalyzed)] public bool LateBoundFunctionExists(string name, string namespaceUri) { object instance; @@ -248,6 +253,7 @@ public bool LateBoundFunctionExists(string name, string namespaceUri) /// Get a late-bound extension object from the external argument list. Bind to a method on the object and invoke it, /// passing "args" as arguments. /// + [RequiresUnreferencedCode(Scripts.ExtensionFunctionCannotBeStaticallyAnalyzed)] public IList InvokeXsltLateBoundFunction(string name, string namespaceUri, IList[] args) { object instance; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryRuntime.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryRuntime.cs index ce2e8f1ccbe9..4a490b3128ff 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryRuntime.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryRuntime.cs @@ -6,11 +6,13 @@ using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Reflection; using System.Xml.Schema; using System.Xml.XPath; using System.Xml.Xsl.IlGen; +using System.Xml.Xsl.Xslt; using MS.Internal.Xml.XPath; namespace System.Xml.Xsl.Runtime @@ -273,6 +275,7 @@ public object GetEarlyBoundObject(int index) /// /// Return true if the early bound object identified by "namespaceUri" contains a method that matches "name". /// + [RequiresUnreferencedCode(Scripts.ExtensionFunctionCannotBeStaticallyAnalyzed)] public bool EarlyBoundFunctionExists(string name, string namespaceUri) { if (_earlyInfo == null) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltLibrary.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltLibrary.cs index 0c8018bbb1ba..31c779d6abd6 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltLibrary.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltLibrary.cs @@ -10,6 +10,7 @@ using System.Xml.XPath; using System.Xml.Xsl.Xslt; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; namespace System.Xml.Xsl.Runtime { @@ -59,7 +60,11 @@ internal static class XsltMethods // XSLT functions and helper methods (non-static) public static readonly MethodInfo CheckScriptNamespace = typeof(XsltLibrary).GetMethod("CheckScriptNamespace"); - public static readonly MethodInfo FunctionAvailable = typeof(XsltLibrary).GetMethod("FunctionAvailable"); + public static readonly MethodInfo FunctionAvailable = GetFunctionAvailableMethod(); + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "Supressing warning about not having the RequiresUnreferencedCode attribute since this code path " + + "will only be emitting IL that will later be called by Transform() method which is already annotated as RequiresUnreferencedCode")] + private static MethodInfo GetFunctionAvailableMethod() => typeof(XsltLibrary).GetMethod("FunctionAvailable"); public static readonly MethodInfo ElementAvailable = typeof(XsltLibrary).GetMethod("ElementAvailable"); public static readonly MethodInfo RegisterDecimalFormat = typeof(XsltLibrary).GetMethod("RegisterDecimalFormat"); public static readonly MethodInfo RegisterDecimalFormatter = typeof(XsltLibrary).GetMethod("RegisterDecimalFormatter"); @@ -110,6 +115,7 @@ public bool ElementAvailable(XmlQualifiedName name) } // Spec: http://www.w3.org/TR/xslt#function-function-available + [RequiresUnreferencedCode(Scripts.ExtensionFunctionCannotBeStaticallyAnalyzed)] public bool FunctionAvailable(XmlQualifiedName name) { if (_functionsAvail == null) @@ -130,6 +136,7 @@ public bool FunctionAvailable(XmlQualifiedName name) return result; } + [RequiresUnreferencedCode(Scripts.ExtensionFunctionCannotBeStaticallyAnalyzed)] private bool FunctionAvailableHelper(XmlQualifiedName name) { // Is this an XPath or an XSLT function? diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/QilGeneratorEnv.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/QilGeneratorEnv.cs index 7918c2995e69..a9a2e410c1aa 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/QilGeneratorEnv.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/QilGeneratorEnv.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Xml.Schema; using System.Xml.XPath; using System.Xml.Xsl.Qil; @@ -95,6 +96,9 @@ QilNode IXPathEnvironment.ResolveVariable(string prefix, string name) } // NOTE: DO NOT call QilNode.Clone() while executing this method since fixup nodes cannot be cloned + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "Suppressing the warning for the ResolveFunction call on the Scripts since " + + "Scripts functionality is not supported by .NET Core")] QilNode IXPathEnvironment.ResolveFunction(string prefix, string name, IList args, IFocus env) { Debug.Assert(!args.IsReadOnly, "Writable collection expected"); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/Scripts.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/Scripts.cs index 0f74a98c23da..72ec06504aa6 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/Scripts.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/Scripts.cs @@ -16,6 +16,7 @@ internal sealed class Scripts private readonly Compiler _compiler; private readonly TrimSafeDictionary _nsToType = new TrimSafeDictionary(); private readonly XmlExtensionFunctionTable _extFuncs = new XmlExtensionFunctionTable(); + internal const string ExtensionFunctionCannotBeStaticallyAnalyzed = "The extension function referenced will be called from the stylesheet which cannot be statically analyzed."; public Scripts(Compiler compiler) { @@ -27,6 +28,7 @@ public TrimSafeDictionary ScriptClasses get { return _nsToType; } } + [RequiresUnreferencedCode(ExtensionFunctionCannotBeStaticallyAnalyzed)] public XmlExtensionFunction? ResolveFunction(string name, string ns, int numArgs, IErrorHelper errorHelper) { Type? type; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XslAstAnalyzer.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XslAstAnalyzer.cs index 963f3411cea7..482b95131294 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XslAstAnalyzer.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XslAstAnalyzer.cs @@ -1054,6 +1054,9 @@ public XslFlags Variable(string prefix, string name) return XslFlags.None; } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "Supressing warning about not having the RequiresUnreferencedCode attribute since xsl Scripts are " + + "not supported in .NET Core")] public XslFlags Function(string prefix, string name, IList args) { _typeDonor = null; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/Processor.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/Processor.cs index 4a8075b0dcf6..7eec61431623 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/Processor.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/Processor.cs @@ -6,6 +6,7 @@ namespace System.Xml.Xsl.XsltOld using System.Collections; using System.Collections.Generic; using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Reflection; @@ -247,6 +248,8 @@ parameter is float || parameter is decimal return parameter; } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = XsltArgumentList.ExtensionObjectSuppresion)] internal object? GetExtensionObject(string nsUri) { return _args.GetExtensionObject(nsUri); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/XsltCompileContext.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/XsltCompileContext.cs index ef58b3d968f2..af038b5711cf 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/XsltCompileContext.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/XsltCompileContext.cs @@ -183,6 +183,8 @@ public override bool PreserveWhitespace(XPathNavigator node) } private const BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static; + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:RequiresUnreferencedCode", + Justification = XsltArgumentList.ExtensionObjectSuppresion)] private IXsltContextFunction? GetExtentionMethod(string ns, string name, XPathResultType[]? argTypes, out object? extension) { FuncExtension? result = null; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xslt/XsltArgumentList.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xslt/XsltArgumentList.cs index 34ed4a5367fd..76045522c308 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xslt/XsltArgumentList.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xslt/XsltArgumentList.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections; +using System.Diagnostics.CodeAnalysis; namespace System.Xml.Xsl { @@ -16,6 +17,10 @@ public class XsltArgumentList { private readonly Hashtable _parameters = new Hashtable(); private readonly Hashtable _extensions = new Hashtable(); + private const string ExtensionObjectWarning = @"The stylesheet may have calls to methods of the extension object passed in which cannot be statically analyzed " + + "by the trimmer. Ensure all methods that may be called are preserved."; + internal const string ExtensionObjectSuppresion = @"In order for this code path to be hit, a previous call to XsltArgumentList.AddExtensionObject is " + + "required. That method is already annotated as unsafe and throwing a warning, so we can suppress here."; // Used for reporting xsl:message's during execution internal XsltMessageEncounteredEventHandler? xsltMessageEncountered; @@ -27,6 +32,7 @@ public XsltArgumentList() { } return _parameters[new XmlQualifiedName(name, namespaceUri)]; } + [RequiresUnreferencedCode(ExtensionObjectWarning)] public object? GetExtensionObject(string namespaceUri) { return _extensions[namespaceUri]; @@ -43,6 +49,7 @@ public void AddParam(string name, string namespaceUri, object parameter) _parameters.Add(qname, parameter); } + [RequiresUnreferencedCode(ExtensionObjectWarning)] public void AddExtensionObject(string namespaceUri, object extension) { CheckArgumentNull(namespaceUri, nameof(namespaceUri)); diff --git a/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs b/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs index b5d4ab5dc931..479251eab451 100644 --- a/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs +++ b/src/libraries/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.cs @@ -2817,9 +2817,11 @@ public partial class XsltArgumentList { public XsltArgumentList() { } public event System.Xml.Xsl.XsltMessageEncounteredEventHandler XsltMessageEncountered { add { } remove { } } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The stylesheet may have calls to methods of the extension object passed in which cannot be statically analyzed by the trimmer. Ensure all methods that may be called are preserved.")] public void AddExtensionObject(string namespaceUri, object extension) { } public void AddParam(string name, string namespaceUri, object parameter) { } public void Clear() { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The stylesheet may have calls to methods of the extension object passed in which cannot be statically analyzed by the trimmer. Ensure all methods that may be called are preserved.")] public object? GetExtensionObject(string namespaceUri) { throw null; } public object? GetParam(string name, string namespaceUri) { throw null; } public object? RemoveExtensionObject(string namespaceUri) { throw null; } From 3fc609c55c592f189b137b7ad9361dca9e71f741 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Sat, 27 Mar 2021 09:30:11 -0700 Subject: [PATCH 03/98] Harden SuperPMI replay against missing data (#50307) When generating disasm for an SPMI replay, we can call `eeGetMethodFullName` for callees for which we are missing data. Fix `MethodContext::repGetArgNext` to raise an appropriate exception, and not crash, when it encounters missing data. --- .../ToolBox/superpmi/superpmi-shared/methodcontext.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp index 51faa23a6070..7c7296b80363 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp @@ -2685,8 +2685,11 @@ void MethodContext::dmpGetArgNext(DWORDLONG key, DWORDLONG value) } CORINFO_ARG_LIST_HANDLE MethodContext::repGetArgNext(CORINFO_ARG_LIST_HANDLE args) { - CORINFO_ARG_LIST_HANDLE temp = (CORINFO_ARG_LIST_HANDLE)GetArgNext->Get(CastHandle(args)); - DEBUG_REP(dmpGetArgNext(CastHandle(args), CastHandle(temp))); + DWORDLONG key = CastHandle(args); + AssertCodeMsg(GetArgNext != nullptr, EXCEPTIONCODE_MC, "Didn't find %016llx", key); + AssertCodeMsg(GetArgNext->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llx", key); + CORINFO_ARG_LIST_HANDLE temp = (CORINFO_ARG_LIST_HANDLE)GetArgNext->Get(key); + DEBUG_REP(dmpGetArgNext(key, CastHandle(temp))); return temp; } void MethodContext::recGetMethodSig(CORINFO_METHOD_HANDLE ftn, CORINFO_SIG_INFO* sig, CORINFO_CLASS_HANDLE memberParent) From 7795971839be34099b07595fdcf47b95f048a730 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Sat, 27 Mar 2021 14:06:39 -0400 Subject: [PATCH 04/98] [mono] Reduce usage of the domain lock (#50313) * [mono] Add a separate lock for the method_to_dyn_method hashtable. * Use the jit memory manager lock instead of the domain lock in the arm64 patch code. * Remove usage of the domain lock from the interpreter code. * Use a separate lock to protect the jit info table. * Fix the build. --- src/mono/mono/metadata/jit-info.c | 37 +++++++++++++++++---------- src/mono/mono/metadata/sre.c | 32 +++++++++++++++-------- src/mono/mono/mini/interp/interp.c | 4 +-- src/mono/mono/mini/interp/transform.c | 3 --- src/mono/mono/mini/mini-arm64.c | 28 ++++++++++---------- 5 files changed, 60 insertions(+), 44 deletions(-) diff --git a/src/mono/mono/metadata/jit-info.c b/src/mono/mono/metadata/jit-info.c index 9ce62822ffd6..a2f37556d95c 100644 --- a/src/mono/mono/metadata/jit-info.c +++ b/src/mono/mono/metadata/jit-info.c @@ -50,6 +50,7 @@ static MonoJitInfoTable * volatile jit_info_table; static MonoJitInfoTable * volatile aot_modules; static GSList *jit_info_free_queue; static int num_jit_info_table_duplicates; +static mono_mutex_t jit_info_mutex; #define JIT_INFO_TABLE_FILL_RATIO_NOM 3 #define JIT_INFO_TABLE_FILL_RATIO_DENOM 4 @@ -64,6 +65,18 @@ static int num_jit_info_table_duplicates; #define JIT_INFO_TABLE_HAZARD_INDEX 0 #define JIT_INFO_HAZARD_INDEX 1 +static inline void +jit_info_lock (void) +{ + mono_os_mutex_lock (&jit_info_mutex); +} + +static inline void +jit_info_unlock (void) +{ + mono_os_mutex_unlock (&jit_info_mutex); +} + static int jit_info_table_num_elements (MonoJitInfoTable *table) { @@ -83,6 +96,7 @@ void mono_jit_info_tables_init (void) { jit_info_table = mono_jit_info_table_new (); + mono_os_mutex_init_recursive (&jit_info_mutex); } MonoJitInfoTable * @@ -104,9 +118,8 @@ jit_info_table_free (MonoJitInfoTable *table, gboolean duplicate) int i; int num_chunks = table->num_chunks; - MonoDomain *domain = mono_get_root_domain (); - mono_domain_lock (domain); + jit_info_lock (); if (duplicate) { num_jit_info_table_duplicates--; @@ -141,7 +154,7 @@ jit_info_table_free (MonoJitInfoTable *table, gboolean duplicate) g_free (chunk); } - mono_domain_unlock (domain); + jit_info_unlock (); g_free (table); } @@ -700,17 +713,15 @@ jit_info_table_add (MonoJitInfoTable *volatile *table_ptr, MonoJitInfo *ji) void mono_jit_info_table_add (MonoJitInfo *ji) { - MonoDomain *domain = mono_get_root_domain (); - g_assert (ji->d.method != NULL); - mono_domain_lock (domain); + jit_info_lock (); UnlockedIncrement (&mono_stats.jit_info_table_insert_count); jit_info_table_add (&jit_info_table, ji); - mono_domain_unlock (domain); + jit_info_unlock (); } static MonoJitInfo* @@ -792,10 +803,10 @@ jit_info_table_remove (MonoJitInfoTable *table, MonoJitInfo *ji) void mono_jit_info_table_remove (MonoJitInfo *ji) { - MonoDomain *domain = mono_get_root_domain (); MonoJitInfoTable *table; - mono_domain_lock (domain); + jit_info_lock (); + table = jit_info_table; UnlockedIncrement (&mono_stats.jit_info_table_remove_count); @@ -804,7 +815,7 @@ mono_jit_info_table_remove (MonoJitInfo *ji) mono_jit_info_free_or_queue (ji); - mono_domain_unlock (domain); + jit_info_unlock (); } /* @@ -815,11 +826,9 @@ mono_jit_info_table_remove (MonoJitInfo *ji) void mono_jit_info_add_aot_module (MonoImage *image, gpointer start, gpointer end) { - MonoDomain *domain = mono_get_root_domain (); MonoJitInfo *ji; - g_assert (domain); - mono_domain_lock (domain); + jit_info_lock (); /* * We reuse MonoJitInfoTable to store AOT module info, @@ -834,7 +843,7 @@ mono_jit_info_add_aot_module (MonoImage *image, gpointer start, gpointer end) ji->code_size = (guint8*)end - (guint8*)start; jit_info_table_add (&aot_modules, ji); - mono_domain_unlock (domain); + jit_info_unlock (); } void diff --git a/src/mono/mono/metadata/sre.c b/src/mono/mono/metadata/sre.c index 2ae38c56bc84..c70b302f0c2a 100644 --- a/src/mono/mono/metadata/sre.c +++ b/src/mono/mono/metadata/sre.c @@ -50,8 +50,20 @@ #include "icall-decl.h" /* Maps MonoMethod* to weak links to DynamicMethod objects */ -/* Protected by the domain lock */ static GHashTable *method_to_dyn_method; +static mono_mutex_t method_to_dyn_method_lock; + +static inline void +dyn_methods_lock (void) +{ + mono_os_mutex_lock (&method_to_dyn_method_lock); +} + +static inline void +dyn_methods_unlock (void) +{ + mono_os_mutex_unlock (&method_to_dyn_method_lock); +} static GENERATE_GET_CLASS_WITH_CACHE (marshal_as_attribute, "System.Runtime.InteropServices", "MarshalAsAttribute"); #ifndef DISABLE_REFLECTION_EMIT @@ -100,6 +112,8 @@ void mono_reflection_emit_init (void) { mono_dynamic_images_init (); + + mono_os_mutex_init_recursive (&method_to_dyn_method_lock); } char* @@ -3980,14 +3994,13 @@ static void free_dynamic_method (void *dynamic_method) { DynamicMethodReleaseData *data = (DynamicMethodReleaseData *)dynamic_method; - MonoDomain *domain = mono_get_root_domain (); MonoMethod *method = data->handle; MonoGCHandle dis_link; - mono_domain_lock (domain); + dyn_methods_lock (); dis_link = g_hash_table_lookup (method_to_dyn_method, method); g_hash_table_remove (method_to_dyn_method, method); - mono_domain_unlock (domain); + dyn_methods_unlock (); g_assert (dis_link); mono_gchandle_free_internal (dis_link); @@ -4006,7 +4019,6 @@ reflection_create_dynamic_method (MonoReflectionDynamicMethodHandle ref_mb, Mono ReflectionMethodBuilder rmb; MonoMethodSignature *sig; MonoClass *klass; - MonoDomain *domain; GSList *l; int i; gboolean ret = TRUE; @@ -4119,12 +4131,11 @@ reflection_create_dynamic_method (MonoReflectionDynamicMethodHandle ref_mb, Mono } g_slist_free (mb->referenced_by); - domain = mono_get_root_domain (); - mono_domain_lock (domain); + dyn_methods_lock (); if (!method_to_dyn_method) method_to_dyn_method = g_hash_table_new (NULL, NULL); g_hash_table_insert (method_to_dyn_method, handle, mono_gchandle_new_weakref_internal ((MonoObject *)mb, TRUE)); - mono_domain_unlock (domain); + dyn_methods_unlock (); goto exit; exit_false: @@ -4590,10 +4601,9 @@ mono_method_to_dyn_method (MonoMethod *method) if (!method_to_dyn_method) return (MonoGCHandle)NULL; - MonoDomain *domain = mono_get_root_domain (); - mono_domain_lock (domain); + dyn_methods_lock (); handle = (MonoGCHandle*)g_hash_table_lookup (method_to_dyn_method, method); - mono_domain_unlock (domain); + dyn_methods_unlock (); return handle; } diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index e1126873c757..9c0dd1d4ecbd 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -693,11 +693,11 @@ get_virtual_method_fast (InterpMethod *imethod, MonoVTable *vtable, int offset) if (!table) { /* Lazily allocate method table */ - mono_domain_lock (vtable->domain); + mono_mem_manager_lock (memory_manager); table = get_method_table (vtable, offset); if (!table) table = alloc_method_table (vtable, offset); - mono_domain_unlock (vtable->domain); + mono_mem_manager_unlock (memory_manager); } if (!table [offset]) { diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 276d65ad889a..31dc24e235c6 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -9048,7 +9048,6 @@ mono_interp_transform_method (InterpMethod *imethod, ThreadContext *context, Mon MonoMethodSignature *signature = mono_method_signature_internal (method); MonoVTable *method_class_vt; MonoGenericContext *generic_context = NULL; - MonoDomain *domain = mono_get_root_domain (); InterpMethod tmp_imethod; InterpMethod *real_imethod; @@ -9167,12 +9166,10 @@ mono_interp_transform_method (InterpMethod *imethod, ThreadContext *context, Mon } mono_os_mutex_unlock (&calc_section); - mono_domain_lock (domain); if (mono_stats_method_desc && mono_method_desc_full_match (mono_stats_method_desc, imethod->method)) { g_printf ("Printing runtime stats at method: %s\n", mono_method_get_full_name (imethod->method)); mono_runtime_print_stats (); } - mono_domain_unlock (domain); MonoJitMemoryManager *jit_mm = jit_mm_for_method (imethod->method); jit_mm_lock (jit_mm); diff --git a/src/mono/mono/mini/mini-arm64.c b/src/mono/mono/mini/mini-arm64.c index 47126892014d..b46541b01dae 100644 --- a/src/mono/mono/mini/mini-arm64.c +++ b/src/mono/mono/mini/mini-arm64.c @@ -858,7 +858,7 @@ emit_thunk (guint8 *code, gconstpointer target) } static gpointer -create_thunk (MonoCompile *cfg, MonoDomain *domain, guchar *code, const guchar *target) +create_thunk (MonoCompile *cfg, guchar *code, const guchar *target) { MonoJitInfo *ji; MonoThunkJitInfo *info; @@ -866,9 +866,7 @@ create_thunk (MonoCompile *cfg, MonoDomain *domain, guchar *code, const guchar * int thunks_size; guint8 *orig_target; guint8 *target_thunk; - - if (!domain) - domain = mono_domain_get (); + MonoJitMemoryManager* jit_mm; if (cfg) { /* @@ -905,7 +903,10 @@ create_thunk (MonoCompile *cfg, MonoDomain *domain, guchar *code, const guchar * orig_target = mono_arch_get_call_target (code + 4); - mono_domain_lock (domain); + /* Arbitrary lock */ + jit_mm = get_default_jit_mm (); + + jit_mm_lock (jit_mm); target_thunk = NULL; if (orig_target >= thunks && orig_target < thunks + thunks_size) { @@ -928,21 +929,21 @@ create_thunk (MonoCompile *cfg, MonoDomain *domain, guchar *code, const guchar * //printf ("THUNK: %p %p %p\n", code, target, target_thunk); if (!target_thunk) { - mono_domain_unlock (domain); + jit_mm_unlock (jit_mm); g_print ("thunk failed %p->%p, thunk space=%d method %s", code, target, thunks_size, cfg ? mono_method_full_name (cfg->method, TRUE) : mono_method_full_name (jinfo_get_method (ji), TRUE)); g_assert_not_reached (); } emit_thunk (target_thunk, target); - mono_domain_unlock (domain); + jit_mm_unlock (jit_mm); return target_thunk; } } static void -arm_patch_full (MonoCompile *cfg, MonoDomain *domain, guint8 *code, guint8 *target, int relocation) +arm_patch_full (MonoCompile *cfg, guint8 *code, guint8 *target, int relocation) { switch (relocation) { case MONO_R_ARM64_B: @@ -952,7 +953,7 @@ arm_patch_full (MonoCompile *cfg, MonoDomain *domain, guint8 *code, guint8 *targ } else { gpointer thunk; - thunk = create_thunk (cfg, domain, code, target); + thunk = create_thunk (cfg, code, target); g_assert (arm_is_bl_disp (code, thunk)); arm_b (code, thunk); } @@ -986,7 +987,7 @@ arm_patch_full (MonoCompile *cfg, MonoDomain *domain, guint8 *code, guint8 *targ } else { gpointer thunk; - thunk = create_thunk (cfg, domain, code, target); + thunk = create_thunk (cfg, code, target); g_assert (arm_is_bl_disp (code, thunk)); arm_bl (code, thunk); } @@ -999,7 +1000,7 @@ arm_patch_full (MonoCompile *cfg, MonoDomain *domain, guint8 *code, guint8 *targ static void arm_patch_rel (guint8 *code, guint8 *target, int relocation) { - arm_patch_full (NULL, NULL, code, target, relocation); + arm_patch_full (NULL, code, target, relocation); } void @@ -1012,18 +1013,17 @@ void mono_arch_patch_code_new (MonoCompile *cfg, guint8 *code, MonoJumpInfo *ji, gpointer target) { guint8 *ip; - MonoDomain *domain = mono_get_root_domain (); ip = ji->ip.i + code; switch (ji->type) { case MONO_PATCH_INFO_METHOD_JUMP: /* ji->relocation is not set by the caller */ - arm_patch_full (cfg, domain, ip, (guint8*)target, MONO_R_ARM64_B); + arm_patch_full (cfg, ip, (guint8*)target, MONO_R_ARM64_B); mono_arch_flush_icache (ip, 8); break; default: - arm_patch_full (cfg, domain, ip, (guint8*)target, ji->relocation); + arm_patch_full (cfg, ip, (guint8*)target, ji->relocation); break; case MONO_PATCH_INFO_NONE: break; From 4f8be1992cc79807d377afce640f219d4caffb5b Mon Sep 17 00:00:00 2001 From: Sychev Vadim Date: Sun, 28 Mar 2021 15:46:02 +0300 Subject: [PATCH 05/98] Add check for JsonConverterFactory (#50219) (#50329) --- .../src/Resources/Strings.resx | 5 ++++- .../Serialization/JsonConverterFactory.cs | 5 +++++ .../Text/Json/ThrowHelper.Serialization.cs | 6 +++++ .../CustomConverterTests.BadConverters.cs | 22 +++++++++++++++++++ 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Text.Json/src/Resources/Strings.resx b/src/libraries/System.Text.Json/src/Resources/Strings.resx index 217b5220ab1f..c6039fdeba14 100644 --- a/src/libraries/System.Text.Json/src/Resources/Strings.resx +++ b/src/libraries/System.Text.Json/src/Resources/Strings.resx @@ -554,4 +554,7 @@ Unable to assign 'null' to the property or field of type '{0}'. - + + The converter '{0}' cannot return an instance of JsonConverterFactory. + + \ No newline at end of file diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterFactory.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterFactory.cs index 404ea47449c6..ed6f50c8591b 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterFactory.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterFactory.cs @@ -65,6 +65,11 @@ internal JsonConverter GetConverterInternal(Type typeToConvert, JsonSerializerOp ThrowHelper.ThrowInvalidOperationException_SerializerConverterFactoryReturnsNull(GetType()); } + if (converter is JsonConverterFactory) + { + ThrowHelper.ThrowInvalidOperationException_SerializerConverterFactoryReturnsJsonConverterFactorty(GetType()); + } + return converter!; } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs index cee333582020..34868bbf4ede 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs @@ -188,6 +188,12 @@ public static void ThrowInvalidOperationException_SerializerConverterFactoryRetu throw new InvalidOperationException(SR.Format(SR.SerializerConverterFactoryReturnsNull, converterType)); } + [DoesNotReturn] + public static void ThrowInvalidOperationException_SerializerConverterFactoryReturnsJsonConverterFactorty(Type converterType) + { + throw new InvalidOperationException(SR.Format(SR.SerializerConverterFactoryReturnsJsonConverterFactory, converterType)); + } + [DoesNotReturn] [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowInvalidOperationException_MultiplePropertiesBindToConstructorParameters( diff --git a/src/libraries/System.Text.Json/tests/Serialization/CustomConverterTests/CustomConverterTests.BadConverters.cs b/src/libraries/System.Text.Json/tests/Serialization/CustomConverterTests/CustomConverterTests.BadConverters.cs index 4a6cf3dd4220..b62d8556a977 100644 --- a/src/libraries/System.Text.Json/tests/Serialization/CustomConverterTests/CustomConverterTests.BadConverters.cs +++ b/src/libraries/System.Text.Json/tests/Serialization/CustomConverterTests/CustomConverterTests.BadConverters.cs @@ -188,6 +188,28 @@ public static void ConverterThatReturnsNullFail() Assert.Contains(typeof(ConverterFactoryThatReturnsNull).ToString(), ex.Message); } + private class ConverterFactoryThatReturnsJsonConverterFactory : JsonConverterFactory + { + public override bool CanConvert(Type type) => true; + + public override JsonConverter CreateConverter(Type type, JsonSerializerOptions options) => new ConverterFactoryThatReturnsJsonConverterFactory(); + } + + [Fact] + public static void CustomJsonConverterFactoryThatReturnsJsonConverterFactoryFail() + { + JsonSerializerOptions options = new() + { + Converters = { new ConverterFactoryThatReturnsJsonConverterFactory() } + }; + + InvalidOperationException ex = Assert.Throws(() => JsonSerializer.Serialize(1, options)); + Assert.Contains(typeof(ConverterFactoryThatReturnsJsonConverterFactory).ToString(), ex.Message); + + ex = Assert.Throws(() => JsonSerializer.Deserialize("1", options)); + Assert.Contains(typeof(ConverterFactoryThatReturnsJsonConverterFactory).ToString(), ex.Message); + } + private class Level1 { public Level1() From 312521fdc16d53b436cdedf00aa9c5798802552b Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Sun, 28 Mar 2021 20:34:59 +0000 Subject: [PATCH 06/98] [main] Update dependencies from dotnet/arcade dotnet/xharness (#50278) [main] Update dependencies from dotnet/arcade dotnet/xharness - reintroduce publishing branch workarounds. Required while https://github.com/dotnet/arcade/issues/6987 is fixed - fix whitespace in publish-using-darc script - re-introduce publishing branch workaround https://github.com/dotnet/arcade/issues/698 --- .config/dotnet-tools.json | 2 +- eng/Version.Details.xml | 76 ++++++++++---------- eng/Versions.props | 30 ++++---- eng/common/generate-locproject.ps1 | 4 +- eng/common/post-build/publish-using-darc.ps1 | 7 +- eng/common/templates/job/onelocbuild.yml | 7 +- global.json | 8 +-- 7 files changed, 67 insertions(+), 67 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index d64902048b60..2baea1807598 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "1.0.0-prerelease.21175.2", + "version": "1.0.0-prerelease.21176.1", "commands": [ "xharness" ] diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 3dc5e6869fbc..b926f2348771 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -6,69 +6,69 @@ - + https://github.com/dotnet/arcade - 12a2bc501ce756c9522588d88583c0256297bd70 + 9a72efb067b74bb9147f9413ade6173b568ea1af - + https://github.com/dotnet/arcade - 12a2bc501ce756c9522588d88583c0256297bd70 + 9a72efb067b74bb9147f9413ade6173b568ea1af - + https://github.com/dotnet/arcade - 12a2bc501ce756c9522588d88583c0256297bd70 + 9a72efb067b74bb9147f9413ade6173b568ea1af - + https://github.com/dotnet/arcade - 12a2bc501ce756c9522588d88583c0256297bd70 + 9a72efb067b74bb9147f9413ade6173b568ea1af - + https://github.com/dotnet/arcade - 12a2bc501ce756c9522588d88583c0256297bd70 + 9a72efb067b74bb9147f9413ade6173b568ea1af - + https://github.com/dotnet/arcade - 12a2bc501ce756c9522588d88583c0256297bd70 + 9a72efb067b74bb9147f9413ade6173b568ea1af - + https://github.com/dotnet/arcade - 12a2bc501ce756c9522588d88583c0256297bd70 + 9a72efb067b74bb9147f9413ade6173b568ea1af - + https://github.com/dotnet/arcade - 12a2bc501ce756c9522588d88583c0256297bd70 + 9a72efb067b74bb9147f9413ade6173b568ea1af - + https://github.com/dotnet/arcade - 12a2bc501ce756c9522588d88583c0256297bd70 + 9a72efb067b74bb9147f9413ade6173b568ea1af - + https://github.com/dotnet/arcade - 12a2bc501ce756c9522588d88583c0256297bd70 + 9a72efb067b74bb9147f9413ade6173b568ea1af - + https://github.com/dotnet/arcade - 12a2bc501ce756c9522588d88583c0256297bd70 + 9a72efb067b74bb9147f9413ade6173b568ea1af - + https://github.com/dotnet/arcade - 12a2bc501ce756c9522588d88583c0256297bd70 + 9a72efb067b74bb9147f9413ade6173b568ea1af - + https://github.com/dotnet/arcade - 12a2bc501ce756c9522588d88583c0256297bd70 + 9a72efb067b74bb9147f9413ade6173b568ea1af - + https://github.com/dotnet/arcade - 12a2bc501ce756c9522588d88583c0256297bd70 + 9a72efb067b74bb9147f9413ade6173b568ea1af - + https://github.com/dotnet/arcade - 12a2bc501ce756c9522588d88583c0256297bd70 + 9a72efb067b74bb9147f9413ade6173b568ea1af - + https://github.com/dotnet/arcade - 12a2bc501ce756c9522588d88583c0256297bd70 + 9a72efb067b74bb9147f9413ade6173b568ea1af https://dev.azure.com/dnceng/internal/_git/dotnet-optimization @@ -198,17 +198,17 @@ https://github.com/mono/linker 2f5594e95e1a5227103b9b7d6b92f4b2250be2d7 - + https://github.com/dotnet/xharness - f4d7df9189d3b8ab04c6ba9a94dc8ccff255224e + 4b4b63fe858a81329d3e6c3f781ce30425ce5dd7 - + https://github.com/dotnet/xharness - f4d7df9189d3b8ab04c6ba9a94dc8ccff255224e + 4b4b63fe858a81329d3e6c3f781ce30425ce5dd7 - + https://github.com/dotnet/arcade - 12a2bc501ce756c9522588d88583c0256297bd70 + 9a72efb067b74bb9147f9413ade6173b568ea1af diff --git a/eng/Versions.props b/eng/Versions.props index 0f463d20019a..d974f9a2ff7c 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -49,19 +49,19 @@ 3.9.0-5.final 3.9.0-5.final - 6.0.0-beta.21175.1 - 6.0.0-beta.21175.1 - 6.0.0-beta.21175.1 - 6.0.0-beta.21175.1 - 6.0.0-beta.21175.1 - 6.0.0-beta.21175.1 - 2.5.1-beta.21175.1 - 6.0.0-beta.21175.1 - 6.0.0-beta.21175.1 - 6.0.0-beta.21175.1 - 6.0.0-beta.21175.1 - 6.0.0-beta.21175.1 - 6.0.0-beta.21175.1 + 6.0.0-beta.21176.2 + 6.0.0-beta.21176.2 + 6.0.0-beta.21176.2 + 6.0.0-beta.21176.2 + 6.0.0-beta.21176.2 + 6.0.0-beta.21176.2 + 2.5.1-beta.21176.2 + 6.0.0-beta.21176.2 + 6.0.0-beta.21176.2 + 6.0.0-beta.21176.2 + 6.0.0-beta.21176.2 + 6.0.0-beta.21176.2 + 6.0.0-beta.21176.2 5.9.0-preview.2 @@ -148,8 +148,8 @@ 1.0.1-prerelease-00006 16.9.0-preview-20201201-01 - 1.0.0-prerelease.21175.2 - 1.0.0-prerelease.21175.2 + 1.0.0-prerelease.21176.1 + 1.0.0-prerelease.21176.1 2.4.1 2.4.2 1.3.0 diff --git a/eng/common/generate-locproject.ps1 b/eng/common/generate-locproject.ps1 index ad3e80651fe3..7225ddc66690 100644 --- a/eng/common/generate-locproject.ps1 +++ b/eng/common/generate-locproject.ps1 @@ -38,7 +38,7 @@ if ($allXlfFiles) { $langXlfFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory\*\*.$firstLangCode.xlf" } $langXlfFiles | ForEach-Object { - $null = $_.Name -Match "([^.]+)\.[\w-]+\.xlf" # matches '[filename].[langcode].xlf' + $null = $_.Name -Match "(.+)\.[\w-]+\.xlf" # matches '[filename].[langcode].xlf $destinationFile = "$($_.Directory.FullName)\$($Matches.1).xlf" $xlfFiles += Copy-Item "$($_.FullName)" -Destination $destinationFile -PassThru @@ -52,7 +52,7 @@ $locJson = @{ LanguageSet = $LanguageSet LocItems = @( $locFiles | ForEach-Object { - $outputPath = "Localize\$(($_.DirectoryName | Resolve-Path -Relative) + "\")" + $outputPath = "$(($_.DirectoryName | Resolve-Path -Relative) + "\")" $continue = $true foreach ($exclusion in $exclusions.Exclusions) { if ($outputPath.Contains($exclusion)) diff --git a/eng/common/post-build/publish-using-darc.ps1 b/eng/common/post-build/publish-using-darc.ps1 index b6e55f5e74a7..2427ca6b6aec 100644 --- a/eng/common/post-build/publish-using-darc.ps1 +++ b/eng/common/post-build/publish-using-darc.ps1 @@ -55,15 +55,12 @@ try { $optionalParams.Add($SigningValidationAdditionalParameters) | Out-Null } } - # note the custom branch to work around https://github.com/dotnet/arcade/issues/6987 . Try to keep this rebased off master if publishing changes - - Write-Host "Using Arcade branch master-workaround-publishing-issue to work around disk space issues with alternate build pool. Will be lost on Arcade updates as this is in eng common. Contact dnceng for questions." - + & $darc add-build-to-channel ` --id $buildId ` --publishing-infra-version $PublishingInfraVersion ` --default-channels ` - --source-branch master-workaround-publishing-issue ` + --source-branch main ` --azdev-pat $AzdoToken ` --bar-uri $MaestroApiEndPoint ` --password $MaestroToken ` diff --git a/eng/common/templates/job/onelocbuild.yml b/eng/common/templates/job/onelocbuild.yml index 6abc041cd1c8..928a70cda2c5 100644 --- a/eng/common/templates/job/onelocbuild.yml +++ b/eng/common/templates/job/onelocbuild.yml @@ -46,16 +46,19 @@ jobs: - task: OneLocBuild@2 displayName: OneLocBuild + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) inputs: locProj: Localize/LocProject.json outDir: $(Build.ArtifactStagingDirectory) lclSource: ${{ parameters.LclSource }} lclPackageId: ${{ parameters.LclPackageId }} isCreatePrSelected: ${{ parameters.CreatePr }} - repoType: ${{ parameters.RepoType }} - gitHubPatVariable: "${{ parameters.GithubPat }}" packageSourceAuth: patAuth patVariable: ${{ parameters.CeapexPat }} + ${{ if eq(parameters.RepoType, 'gitHub') }}: + repoType: ${{ parameters.RepoType }} + gitHubPatVariable: "${{ parameters.GithubPat }}" condition: always() - task: PublishBuildArtifacts@1 diff --git a/global.json b/global.json index d085b22585ab..b3a6c7ce6100 100644 --- a/global.json +++ b/global.json @@ -12,10 +12,10 @@ "python3": "3.7.1" }, "msbuild-sdks": { - "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21175.1", - "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21175.1", - "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21175.1", - "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21175.1", + "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21176.2", + "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21176.2", + "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21176.2", + "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21176.2", "Microsoft.Build.NoTargets": "2.0.17", "Microsoft.Build.Traversal": "2.1.1", "Microsoft.NET.Sdk.IL": "6.0.0-preview.3.21169.6" From 99f4062f8eaad3f8dfa91e08a53e4ec459205bd0 Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Sun, 28 Mar 2021 16:52:37 -0500 Subject: [PATCH 07/98] Add documentation on ILLink files (#50302) * Add documentation on ILLink files * Add link to ILLink-files from project-guidelines --- docs/coding-guidelines/project-guidelines.md | 3 ++ docs/workflow/trimming/ILLink-files.md | 45 ++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 docs/workflow/trimming/ILLink-files.md diff --git a/docs/coding-guidelines/project-guidelines.md b/docs/coding-guidelines/project-guidelines.md index 556c9ac6d9ae..c0a79248d01c 100644 --- a/docs/coding-guidelines/project-guidelines.md +++ b/docs/coding-guidelines/project-guidelines.md @@ -142,6 +142,9 @@ All libraries should use `` for all their references Other target frameworks than .NETCoreApp latest (i.e. `netstandard2.0`, `net461`, `netcoreapp3.0`) should use ProjectReference items to reference dependencies. +### src\ILLink +Contains the files used to direct the trimming tool. See [ILLink files](../workflow/trimming/ILLink-files.md). + ### src output All src outputs are under diff --git a/docs/workflow/trimming/ILLink-files.md b/docs/workflow/trimming/ILLink-files.md new file mode 100644 index 000000000000..bbce56daa43a --- /dev/null +++ b/docs/workflow/trimming/ILLink-files.md @@ -0,0 +1,45 @@ +# ILLink Files + +There are a few `ILLink.*.xml` files under `src` folders in `dotnet/runtime`. These files are used by the trimming tool for various reasons. + +See https://github.com/mono/linker/blob/main/docs/data-formats.md for full documentation on these files. + +## ILLink.Descriptors.xml + +Descriptors are used to direct the trimming tool to always keep some items in the assembly, regardless of if the trimming tool can find any references to them. + +We try to limit the usage of descriptor files as much as possible. Since using a descriptor means the code will always be preserved, even in the final application. Typically the main scenario they are used is when non-IL code (e.g. C/C++, JavaScript, etc.) is calling into IL code. The trimming tool isn't able to see the non-IL code, so it doesn't know which IL methods are necessary. + +In some cases it is only necessary to preserve items only during `dotnet/runtime`'s build, but we don't want to unconditionally preserve them in an the final application. Examples of these cases are non-public methods only used by tests, or non-public methods that are called through Reflection by another assembly. To only preserve items during `dotnet/runtime`'s build, use a `ILLink.Descriptors.LibraryBuild.xml` file. + +In almost all cases, when using a descriptors file, add a comment justifying why it is necessary. + +## ILLink.Substitutions.xml + +Substitutions direct the trimming tool to replace specific method's body with either a throw or return constant statements. + +These files are mainly used to implement [feature switches](feature-switches.md). + +They can also be used to hard-code constants depending on the platform we are building for, typically in `System.Private.CoreLib.dll` since, at this time, that is the only assembly we build specifically for each target architecture. In those cases, there are multiple `ILLink.Substitutions.{arch}.xml` files, which get included depending on the architecture. This is possible through an MSBuild Item `@(ILLinkSubstitutionsXmls)` which can conditionally get added to, and all the .xml files are combined into a final `ILLink.Substitutions.xml`, which is embedded into the assembly. + +## ILLink.Suppressions.xml + +When we build `dotnet/runtime`, we run the trimming tool to analyze our assemblies for code that is using patterns (like Reflection) that may be broken once the application is trimmed. When the trimming tool encounters code that isn't trim compatible, it issues a warning. Because we haven't addressed all these warnings in the code, we suppress the existing warnings in `ILLink.Suppressions.xml` files, and fail the build when an unsuppressed warning is encountered. This ensures that no new code can introduce new warnings while we are addressing the existing warnings. + +If your new feature or bug fix is introducing new ILLink warnings, the warnings need to be addressed before your PR can be merged. No new suppressions should be added to an `ILLink.Suppressions.xml` file. To address the warnings, see [Linking the .NET Libraries](https://github.com/dotnet/designs/blob/main/accepted/2020/linking-libraries.md). Typically, either adding `[DynamicallyAccessedMembers]` or `[RequiresUnreferencedCode]` attributes are acceptable ways of addressing the warnings. If the warning is a false-positive (meaning it is trim compatible, but the trimming tool wasn't able to tell), it can be suppressed in code using an `[UnconditionalSuppressMessage]`. + +ILLink warnings that are suppressed by the `ILLink.Suppressions.xml` file will still be emitted when the final application is published. This allows developers to see where their application might be broken when it is trimmed. Warnings that are suppressed by `[UnconditionalSuppressMessage]` attributes in `dotnet/runtime` code will never be emitted, during the `dotnet/runtime` build nor in the final application. + +Sometimes it is beneficial to leave an ILLink warning as unsuppressed so the final application's developer sees the warning. An examples of this is using the [`Startup Hooks`](../../design/features/host-startup-hook.md) feature in .NET. By default this feature is disabled when trimming a .NET application. However, the application can re-enable the feature. When they do, an ILLInk warning is emitted when the application is trimmed telling them the feature may not work after trimming. + +To suppress a warning only in the `dotnet/runtime` build, but keep emitting it in the final application, add the warning to a `ILLink.Suppressions.LibraryBuild.xml` file, and include a justification why this approach was taken. + +## ILLink.LinkAttributes.xml + +Attribute annotations direct the trimming tool to behave as if the specified item has the specified attribute. + +This is mainly used to tell the trimming tool which attributes to remove from the trimmed application. This is useful because some attributes are only needed at development time. They aren't necessary at runtime. Trimming unnecessary attributes can make the application smaller. + +Under the covers, the way this works is that the `ILLink.LinkAttributes.xml` tells the trimming tool to act like a `[RemoveAttributeInstances]` attribute is applied to the attribute type we want to remove. The trimming tool removes any instantiations of the attribute in all the assemblies of the application. However, if the trimming tool encounters code that is trying to read the attribute at runtime, it doesn't trim the attribute instances. For example, if runtime code is reading the `ObsoleteAttribute`, `ObsoleteAttribute` instances won't be trimmed even if it was asked to be removed through this file. + +This is also how the above `ILLink.Suppressions.xml` file works under the covers. It injects `[UnconditionalSuppressMessage]` attributes to tell the trimming tool to act as if there was a suppress attribute in code. \ No newline at end of file From 6b88f89593d985b625a4fcdadbec6615ad2554d7 Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed Date: Sun, 28 Mar 2021 17:24:24 -0700 Subject: [PATCH 08/98] Use random file names in the TZ tests (#50339) --- src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs b/src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs index 05ae478a9d54..0e8390632acf 100644 --- a/src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs +++ b/src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs @@ -2469,7 +2469,7 @@ public static void GetSystemTimeZones_AllTimeZonesHaveOffsetInValidRange() [InlineData("<+00>0<+01>,A/0,J365/25", 0, 0, false)] public static void NJulianRuleTest(string posixRule, int dayNumber, int monthNumber, bool shouldSucceed) { - string zoneFilePath = Path.GetTempPath() + "dotnet_tz"; + string zoneFilePath = Path.GetTempPath() + Path.GetRandomFileName(); using (FileStream fs = new FileStream(zoneFilePath, FileMode.Create)) { fs.Write(timeZoneFileContents.AsSpan()); From 508df296dc3e64a639bdb0c48329b65306c4938d Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Sun, 28 Mar 2021 18:01:19 -0700 Subject: [PATCH 09/98] catch exceptions in callbacks from native code on OSX in SafeDeleteSslContext (#49945) * catch exceptions in callbacks from native code * style update * add traces for failures * add comment --- .../Security/Pal.OSX/SafeDeleteSslContext.cs | 81 ++++++++++++------- 1 file changed, 52 insertions(+), 29 deletions(-) diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs index 06ecc8d9ed1d..44fc356c3366 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs @@ -13,6 +13,11 @@ namespace System.Net { internal sealed class SafeDeleteSslContext : SafeDeleteContext { + // mapped from OSX error codes + private const int OSStatus_writErr = -20; + private const int OSStatus_readErr = -19; + private const int OSStatus_noErr = 0; + private const int OSStatus_errSSLWouldBlock = -9803; private const int InitialBufferSize = 2048; private SafeSslHandle _sslContext; private Interop.AppleCrypto.SSLReadFunc _readCallback; @@ -155,48 +160,66 @@ protected override void Dispose(bool disposing) private unsafe int WriteToConnection(void* connection, byte* data, void** dataLength) { - ulong length = (ulong)*dataLength; - Debug.Assert(length <= int.MaxValue); + // We don't pool these buffers and we can't because there's a race between their us in the native + // read/write callbacks and being disposed when the SafeHandle is disposed. This race is benign currently, + // but if we were to pool the buffers we would have a potential use-after-free issue. + try + { + ulong length = (ulong)*dataLength; + Debug.Assert(length <= int.MaxValue); - int toWrite = (int)length; - var inputBuffer = new ReadOnlySpan(data, toWrite); + int toWrite = (int)length; + var inputBuffer = new ReadOnlySpan(data, toWrite); - _outputBuffer.EnsureAvailableSpace(toWrite); - inputBuffer.CopyTo(_outputBuffer.AvailableSpan); - _outputBuffer.Commit(toWrite); + _outputBuffer.EnsureAvailableSpace(toWrite); + inputBuffer.CopyTo(_outputBuffer.AvailableSpan); + _outputBuffer.Commit(toWrite); + // Since we can enqueue everything, no need to re-assign *dataLength. - // Since we can enqueue everything, no need to re-assign *dataLength. - const int noErr = 0; - return noErr; + return OSStatus_noErr; + } + catch (Exception e) + { + if (NetEventSource.Log.IsEnabled()) + NetEventSource.Error(this, $"WritingToConnection failed: {e.Message}"); + return OSStatus_writErr; + } } private unsafe int ReadFromConnection(void* connection, byte* data, void** dataLength) { - const int noErr = 0; - const int errSSLWouldBlock = -9803; - ulong toRead = (ulong)*dataLength; - - if (toRead == 0) + try { - return noErr; - } + ulong toRead = (ulong)*dataLength; + + if (toRead == 0) + { + return OSStatus_noErr; + } - uint transferred = 0; + uint transferred = 0; - if (_inputBuffer.ActiveLength == 0) - { - *dataLength = (void*)0; - return errSSLWouldBlock; - } + if (_inputBuffer.ActiveLength == 0) + { + *dataLength = (void*)0; + return OSStatus_errSSLWouldBlock; + } - int limit = Math.Min((int)toRead, _inputBuffer.ActiveLength); + int limit = Math.Min((int)toRead, _inputBuffer.ActiveLength); - _inputBuffer.ActiveSpan.Slice(0, limit).CopyTo(new Span(data, limit)); - _inputBuffer.Discard(limit); - transferred = (uint)limit; + _inputBuffer.ActiveSpan.Slice(0, limit).CopyTo(new Span(data, limit)); + _inputBuffer.Discard(limit); + transferred = (uint)limit; - *dataLength = (void*)transferred; - return noErr; + *dataLength = (void*)transferred; + return OSStatus_noErr; + } + catch (Exception e) + { + if (NetEventSource.Log.IsEnabled()) + NetEventSource.Error(this, $"ReadFromConnectionfailed: {e.Message}"); + return OSStatus_readErr; + } } internal void Write(ReadOnlySpan buf) From 21734a4134dddd977b45e6a39cb139e6b79ea7e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Mon, 29 Mar 2021 05:03:45 +0200 Subject: [PATCH 10/98] Increase test coverage for Exception.TargetSite (#50275) * Increase test coverage for Exception.TargetSite * Apply suggestions from code review Co-authored-by: Stephen Toub Co-authored-by: Stephen Toub --- .../tests/System/ExceptionTests.cs | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime/tests/System/ExceptionTests.cs b/src/libraries/System.Runtime/tests/System/ExceptionTests.cs index 3bec61d7f73a..fb83278ab318 100644 --- a/src/libraries/System.Runtime/tests/System/ExceptionTests.cs +++ b/src/libraries/System.Runtime/tests/System/ExceptionTests.cs @@ -61,7 +61,7 @@ public static void Exception_GetBaseException() } [Fact] - public static void Exception_TargetSite_Jit() + public static void Exception_TargetSite() { bool caught = false; @@ -78,6 +78,32 @@ public static void Exception_TargetSite_Jit() Assert.True(caught); } + + static void RethrowException() + { + try + { + ThrowException(); + } + catch + { + throw; + } + } + + [Fact] + public static void Exception_TargetSite_OtherMethod() + { + Exception ex = Assert.ThrowsAny(() => ThrowException()); + Assert.Equal(nameof(ThrowException), ex.TargetSite.Name); + } + + [Fact] + public static void Exception_TargetSite_Rethrow() + { + Exception ex = Assert.ThrowsAny(() => RethrowException()); + Assert.Equal(nameof(ThrowException), ex.TargetSite.Name); + } [Fact] [ActiveIssue("https://github.com/mono/mono/issues/15140", TestRuntimes.Mono)] From 102d1e856c7e0e553abeec937783da5debed73ad Mon Sep 17 00:00:00 2001 From: David Fowler Date: Sun, 28 Mar 2021 21:15:47 -0700 Subject: [PATCH 11/98] Always clear the local buffer in ArrayBuffer (#49573) - SslStream was holding onto a 4K byte[] after the handshake was complete. This was because the ArrayBuffer struct doesn't clear the local buffer field in dispose. This changes that. --- src/libraries/Common/src/System/Net/ArrayBuffer.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/libraries/Common/src/System/Net/ArrayBuffer.cs b/src/libraries/Common/src/System/Net/ArrayBuffer.cs index 5dbc5a158cb7..56413164232d 100644 --- a/src/libraries/Common/src/System/Net/ArrayBuffer.cs +++ b/src/libraries/Common/src/System/Net/ArrayBuffer.cs @@ -41,15 +41,12 @@ public void Dispose() _activeStart = 0; _availableStart = 0; - if (_usePool) - { - byte[] array = _bytes; - _bytes = null!; + byte[] array = _bytes; + _bytes = null!; - if (array != null) - { - ArrayPool.Shared.Return(array); - } + if (_usePool && array != null) + { + ArrayPool.Shared.Return(array); } } From 3b0c4e426b3ce75f1ef9ed52a8d7726eb7336a57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Mon, 29 Mar 2021 15:37:10 +0200 Subject: [PATCH 12/98] Added LoggerMessage.Define overloads to disable IsEnabled check (#50334) - LoggerMessage.Define with overload to disable IsEnabled check - Hoist skipEnabledCheck --- ...crosoft.Extensions.Logging.Abstractions.cs | 7 + .../src/LoggerMessage.cs | 154 +++++++++++++++++- 2 files changed, 158 insertions(+), 3 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/ref/Microsoft.Extensions.Logging.Abstractions.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/ref/Microsoft.Extensions.Logging.Abstractions.cs index e2d403bc7b38..a1051db9b018 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/ref/Microsoft.Extensions.Logging.Abstractions.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/ref/Microsoft.Extensions.Logging.Abstractions.cs @@ -94,6 +94,7 @@ public static partial class LoggerFactoryExtensions public static partial class LoggerMessage { public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; } + public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; } public static System.Func DefineScope(string formatString) { throw null; } public static System.Func DefineScope(string formatString) { throw null; } public static System.Func DefineScope(string formatString) { throw null; } @@ -102,11 +103,17 @@ public static partial class LoggerMessage public static System.Func DefineScope(string formatString) { throw null; } public static System.Func DefineScope(string formatString) { throw null; } public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; } + public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; } public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; } + public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; } public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; } + public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; } public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; } + public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; } public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; } + public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; } public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; } + public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; } } public partial class Logger : Microsoft.Extensions.Logging.ILogger, Microsoft.Extensions.Logging.ILogger { diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerMessage.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerMessage.cs index 7007782a7a2a..c10693ce3a0f 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerMessage.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerMessage.cs @@ -128,14 +128,35 @@ public static Func DefineScopeThe named format string /// A delegate which when invoked creates a log message. public static Action Define(LogLevel logLevel, EventId eventId, string formatString) + => Define(logLevel, eventId, formatString, skipEnabledCheck: false); + + /// + /// Creates a delegate which can be invoked for logging a message. + /// + /// The + /// The event id + /// The named format string + /// Skips the check if the logging category is enabled. + /// A delegate which when invoked creates a log message. + public static Action Define(LogLevel logLevel, EventId eventId, string formatString, bool skipEnabledCheck) { LogValuesFormatter formatter = CreateLogValuesFormatter(formatString, expectedNamedParameterCount: 0); + void Log(ILogger logger, Exception? exception) + { + logger.Log(logLevel, eventId, new LogValues(formatter), exception, LogValues.Callback); + } + + if (skipEnabledCheck) + { + return Log; + } + return (logger, exception) => { if (logger.IsEnabled(logLevel)) { - logger.Log(logLevel, eventId, new LogValues(formatter), exception, LogValues.Callback); + Log(logger, exception); } }; } @@ -149,6 +170,18 @@ public static Func DefineScopeThe named format string /// A delegate which when invoked creates a log message. public static Action Define(LogLevel logLevel, EventId eventId, string formatString) + => Define(logLevel, eventId, formatString, skipEnabledCheck: false); + + /// + /// Creates a delegate which can be invoked for logging a message. + /// + /// The type of the first parameter passed to the named format string. + /// The + /// The event id + /// The named format string + /// Skips the check if the logging category is enabled. + /// A delegate which when invoked creates a log message. + public static Action Define(LogLevel logLevel, EventId eventId, string formatString, bool skipEnabledCheck) { LogValuesFormatter formatter = CreateLogValuesFormatter(formatString, expectedNamedParameterCount: 1); @@ -157,6 +190,11 @@ void Log(ILogger logger, T1 arg1, Exception? exception) logger.Log(logLevel, eventId, new LogValues(formatter, arg1), exception, LogValues.Callback); } + if (skipEnabledCheck) + { + return Log; + } + return (logger, arg1, exception) => { if (logger.IsEnabled(logLevel)) @@ -176,6 +214,19 @@ void Log(ILogger logger, T1 arg1, Exception? exception) /// The named format string /// A delegate which when invoked creates a log message. public static Action Define(LogLevel logLevel, EventId eventId, string formatString) + => Define(logLevel, eventId, formatString, skipEnabledCheck: false); + + /// + /// Creates a delegate which can be invoked for logging a message. + /// + /// The type of the first parameter passed to the named format string. + /// The type of the second parameter passed to the named format string. + /// The + /// The event id + /// The named format string + /// Skips the check if the logging category is enabled. + /// A delegate which when invoked creates a log message. + public static Action Define(LogLevel logLevel, EventId eventId, string formatString, bool skipEnabledCheck) { LogValuesFormatter formatter = CreateLogValuesFormatter(formatString, expectedNamedParameterCount: 2); @@ -184,6 +235,11 @@ void Log(ILogger logger, T1 arg1, T2 arg2, Exception? exception) logger.Log(logLevel, eventId, new LogValues(formatter, arg1, arg2), exception, LogValues.Callback); } + if (skipEnabledCheck) + { + return Log; + } + return (logger, arg1, arg2, exception) => { if (logger.IsEnabled(logLevel)) @@ -204,6 +260,20 @@ void Log(ILogger logger, T1 arg1, T2 arg2, Exception? exception) /// The named format string /// A delegate which when invoked creates a log message. public static Action Define(LogLevel logLevel, EventId eventId, string formatString) + => Define(logLevel, eventId, formatString, skipEnabledCheck: false); + + /// + /// Creates a delegate which can be invoked for logging a message. + /// + /// The type of the first parameter passed to the named format string. + /// The type of the second parameter passed to the named format string. + /// The type of the third parameter passed to the named format string. + /// The + /// The event id + /// The named format string + /// Skips the check if the logging category is enabled. + /// A delegate which when invoked creates a log message. + public static Action Define(LogLevel logLevel, EventId eventId, string formatString, bool skipEnabledCheck) { LogValuesFormatter formatter = CreateLogValuesFormatter(formatString, expectedNamedParameterCount: 3); @@ -212,6 +282,11 @@ void Log(ILogger logger, T1 arg1, T2 arg2, T3 arg3, Exception? exception) logger.Log(logLevel, eventId, new LogValues(formatter, arg1, arg2, arg3), exception, LogValues.Callback); } + if (skipEnabledCheck) + { + return Log; + } + return (logger, arg1, arg2, arg3, exception) => { if (logger.IsEnabled(logLevel)) @@ -233,6 +308,21 @@ void Log(ILogger logger, T1 arg1, T2 arg2, T3 arg3, Exception? exception) /// The named format string /// A delegate which when invoked creates a log message. public static Action Define(LogLevel logLevel, EventId eventId, string formatString) + => Define(logLevel, eventId, formatString, skipEnabledCheck: false); + + /// + /// Creates a delegate which can be invoked for logging a message. + /// + /// The type of the first parameter passed to the named format string. + /// The type of the second parameter passed to the named format string. + /// The type of the third parameter passed to the named format string. + /// The type of the fourth parameter passed to the named format string. + /// The + /// The event id + /// The named format string + /// Skips the check if the logging category is enabled. + /// A delegate which when invoked creates a log message. + public static Action Define(LogLevel logLevel, EventId eventId, string formatString, bool skipEnabledCheck) { LogValuesFormatter formatter = CreateLogValuesFormatter(formatString, expectedNamedParameterCount: 4); @@ -241,6 +331,11 @@ void Log(ILogger logger, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Exception? exceptio logger.Log(logLevel, eventId, new LogValues(formatter, arg1, arg2, arg3, arg4), exception, LogValues.Callback); } + if (skipEnabledCheck) + { + return Log; + } + return (logger, arg1, arg2, arg3, arg4, exception) => { if (logger.IsEnabled(logLevel)) @@ -263,14 +358,40 @@ void Log(ILogger logger, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Exception? exceptio /// The named format string /// A delegate which when invoked creates a log message. public static Action Define(LogLevel logLevel, EventId eventId, string formatString) + => Define(logLevel, eventId, formatString, skipEnabledCheck: false); + + /// + /// Creates a delegate which can be invoked for logging a message. + /// + /// The type of the first parameter passed to the named format string. + /// The type of the second parameter passed to the named format string. + /// The type of the third parameter passed to the named format string. + /// The type of the fourth parameter passed to the named format string. + /// The type of the fifth parameter passed to the named format string. + /// The + /// The event id + /// The named format string + /// Skips the check if the logging category is enabled. + /// A delegate which when invoked creates a log message. + public static Action Define(LogLevel logLevel, EventId eventId, string formatString, bool skipEnabledCheck) { LogValuesFormatter formatter = CreateLogValuesFormatter(formatString, expectedNamedParameterCount: 5); + void Log(ILogger logger, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Exception? exception) + { + logger.Log(logLevel, eventId, new LogValues(formatter, arg1, arg2, arg3, arg4, arg5), exception, LogValues.Callback); + } + + if (skipEnabledCheck) + { + return Log; + } + return (logger, arg1, arg2, arg3, arg4, arg5, exception) => { if (logger.IsEnabled(logLevel)) { - logger.Log(logLevel, eventId, new LogValues(formatter, arg1, arg2, arg3, arg4, arg5), exception, LogValues.Callback); + Log(logger, arg1, arg2, arg3, arg4, arg5, exception); } }; } @@ -289,14 +410,41 @@ void Log(ILogger logger, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Exception? exceptio /// The named format string /// A delegate which when invoked creates a log message. public static Action Define(LogLevel logLevel, EventId eventId, string formatString) + => Define(logLevel, eventId, formatString, skipEnabledCheck: false); + + /// + /// Creates a delegate which can be invoked for logging a message. + /// + /// The type of the first parameter passed to the named format string. + /// The type of the second parameter passed to the named format string. + /// The type of the third parameter passed to the named format string. + /// The type of the fourth parameter passed to the named format string. + /// The type of the fifth parameter passed to the named format string. + /// The type of the sixth parameter passed to the named format string. + /// The + /// The event id + /// The named format string + /// Skips the check if the logging category is enabled. + /// A delegate which when invoked creates a log message. + public static Action Define(LogLevel logLevel, EventId eventId, string formatString, bool skipEnabledCheck) { LogValuesFormatter formatter = CreateLogValuesFormatter(formatString, expectedNamedParameterCount: 6); + void Log(ILogger logger, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, Exception? exception) + { + logger.Log(logLevel, eventId, new LogValues(formatter, arg1, arg2, arg3, arg4, arg5, arg6), exception, LogValues.Callback); + } + + if (skipEnabledCheck) + { + return Log; + } + return (logger, arg1, arg2, arg3, arg4, arg5, arg6, exception) => { if (logger.IsEnabled(logLevel)) { - logger.Log(logLevel, eventId, new LogValues(formatter, arg1, arg2, arg3, arg4, arg5, arg6), exception, LogValues.Callback); + Log(logger, arg1, arg2, arg3, arg4, arg5, arg6, exception); } }; } From ff465c70b649240892ec368ace99f6330d798996 Mon Sep 17 00:00:00 2001 From: Sergey Andreenko Date: Mon, 29 Mar 2021 08:42:16 -0700 Subject: [PATCH 13/98] Reenable threadstart tests. (#50343) The failures do not look jit-related. If they fail again please open another issue. --- src/tests/issues.targets | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 78d51c1ccb9e..b2658574d4bb 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -729,10 +729,6 @@ https://github.com/dotnet/runtime/issues/47294 - - - https://github.com/dotnet/runtime/issues/48791 - https://github.com/dotnet/runtime/issues/48727 From a55b7e04b261d0852318c69996b7f2f24117bbd6 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Mon, 29 Mar 2021 08:55:15 -0700 Subject: [PATCH 14/98] JIT: tolerate edge profile inconsistencies better. (#50213) We should not blow up jitting just because edge counts are inconsistent. Also, make sure we're not doing any guarded devirt if we disable pgo. And, add an option JitEnablePgoRange to selectively enable use of PGO for problem isolation. --- src/coreclr/jit/compiler.cpp | 29 +++++- src/coreclr/jit/compiler.h | 1 + src/coreclr/jit/fgbasic.cpp | 1 + src/coreclr/jit/fgprofile.cpp | 152 +++++++++++++++++------------- src/coreclr/jit/importer.cpp | 10 +- src/coreclr/jit/jitconfigvalues.h | 5 +- 6 files changed, 122 insertions(+), 76 deletions(-) diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 7d87e0e51bfa..421614c291fd 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -2879,17 +2879,38 @@ void Compiler::compInitOptions(JitFlags* jitFlags) fgPgoData = nullptr; fgPgoSchema = nullptr; } - // Optionally, discard the profile data. + // Optionally, disable use of profile data. // - else if (JitConfig.JitDisablePGO() != 0) + else if (JitConfig.JitDisablePgo() > 0) { - fgPgoFailReason = "PGO data available, but JitDisablePGO != 0"; + fgPgoFailReason = "PGO data available, but JitDisablePgo > 0"; fgPgoQueryResult = E_FAIL; fgPgoData = nullptr; fgPgoSchema = nullptr; + fgPgoDisabled = true; } - #ifdef DEBUG + // Optionally, enable use of profile data for only some methods. + // + else + { + static ConfigMethodRange JitEnablePgoRange; + JitEnablePgoRange.EnsureInit(JitConfig.JitEnablePgoRange()); + + // Base this decision on the root method hash, so a method either sees all available + // profile data (including that for inlinees), or none of it. + // + const unsigned hash = impInlineRoot()->info.compMethodHash(); + if (!JitEnablePgoRange.Contains(hash)) + { + fgPgoFailReason = "PGO data available, but method hash NOT within JitEnablePgoRange"; + fgPgoQueryResult = E_FAIL; + fgPgoData = nullptr; + fgPgoSchema = nullptr; + fgPgoDisabled = true; + } + } + // A successful result implies a non-NULL fgPgoSchema // if (SUCCEEDED(fgPgoQueryResult)) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 5e2c23795cd3..6138d87379c2 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -5644,6 +5644,7 @@ class Compiler public: const char* fgPgoFailReason; + bool fgPgoDisabled; ICorJitInfo::PgoInstrumentationSchema* fgPgoSchema; BYTE* fgPgoData; UINT32 fgPgoSchemaCount; diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index a67212ab85a5..9ac2e8bc9fe5 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -165,6 +165,7 @@ void Compiler::fgInit() #endif fgHasSwitch = false; + fgPgoDisabled = false; fgPgoSchema = nullptr; fgPgoData = nullptr; fgPgoSchemaCount = 0; diff --git a/src/coreclr/jit/fgprofile.cpp b/src/coreclr/jit/fgprofile.cpp index 8c158ddc458a..384c42d80f14 100644 --- a/src/coreclr/jit/fgprofile.cpp +++ b/src/coreclr/jit/fgprofile.cpp @@ -2521,6 +2521,11 @@ void Compiler::fgIncorporateEdgeCounts() // slop - profile slush fund // wbUsedSlop [out] - true if we tapped into the slush fund // +// Returns: +// true if the edge weight was adjusted +// false if the edge weight update was inconsistent with the +// edge's current [min,max} +// bool flowList::setEdgeWeightMinChecked(BasicBlock::weight_t newWeight, BasicBlock* bDst, BasicBlock::weight_t slop, @@ -2556,10 +2561,8 @@ bool flowList::setEdgeWeightMinChecked(BasicBlock::weight_t newWeight, } } } - else + else if (flEdgeWeightMin > newWeight) { - assert(flEdgeWeightMin > newWeight); - // We have already determined that this edge's weight // is more than newWeight, so we just allow for the slop if ((newWeight + slop) >= flEdgeWeightMin) @@ -2581,31 +2584,28 @@ bool flowList::setEdgeWeightMinChecked(BasicBlock::weight_t newWeight, // If we are returning true then we should have adjusted the range so that // the newWeight is in new range [Min..Max] or fgEdgeWeightMax is zero. // Also we should have set wbUsedSlop to true. - if (result == true) + if (result) { assert((flEdgeWeightMax == BB_ZERO_WEIGHT) || ((newWeight <= flEdgeWeightMax) && (newWeight >= flEdgeWeightMin))); - if (wbUsedSlop != nullptr) - { - assert(*wbUsedSlop == true); - } + assert((wbUsedSlop == nullptr) || (*wbUsedSlop)); } } #if DEBUG - if (result == false) + if (result) + { + JITDUMP("Updated min weight of " FMT_BB " -> " FMT_BB " to [" FMT_WT ".." FMT_WT "]\n", getBlock()->bbNum, + bDst->bbNum, flEdgeWeightMin, flEdgeWeightMax); + } + else { JITDUMP("Not adjusting min weight of " FMT_BB " -> " FMT_BB "; new value " FMT_WT " not in range [" FMT_WT ".." FMT_WT "] (+/- " FMT_WT ")\n", getBlock()->bbNum, bDst->bbNum, newWeight, flEdgeWeightMin, flEdgeWeightMax, slop); result = false; // break here } - else - { - JITDUMP("Updated min weight of " FMT_BB " -> " FMT_BB " to [" FMT_WT ".." FMT_WT "]\n", getBlock()->bbNum, - bDst->bbNum, flEdgeWeightMin, flEdgeWeightMax); - } #endif // DEBUG return result; @@ -2620,6 +2620,11 @@ bool flowList::setEdgeWeightMinChecked(BasicBlock::weight_t newWeight, // slop - profile slush fund // wbUsedSlop [out] - true if we tapped into the slush fund // +// Returns: +// true if the edge weight was adjusted +// false if the edge weight update was inconsistent with the +// edge's current [min,max} +// bool flowList::setEdgeWeightMaxChecked(BasicBlock::weight_t newWeight, BasicBlock* bDst, BasicBlock::weight_t slop, @@ -2654,10 +2659,8 @@ bool flowList::setEdgeWeightMaxChecked(BasicBlock::weight_t newWeight, } } } - else + else if (flEdgeWeightMin > newWeight) { - assert(flEdgeWeightMin > newWeight); - // We have already determined that this edge's weight // is more than newWeight, so we just allow for the slop if ((newWeight + slop) >= flEdgeWeightMin) @@ -2680,28 +2683,28 @@ bool flowList::setEdgeWeightMaxChecked(BasicBlock::weight_t newWeight, // If we are returning true then we should have adjusted the range so that // the newWeight is in new range [Min..Max] or fgEdgeWeightMax is zero // Also we should have set wbUsedSlop to true, unless it is NULL - if (result == true) + if (result) { assert((flEdgeWeightMax == BB_ZERO_WEIGHT) || ((newWeight <= flEdgeWeightMax) && (newWeight >= flEdgeWeightMin))); - assert((wbUsedSlop == nullptr) || (*wbUsedSlop == true)); + assert((wbUsedSlop == nullptr) || (*wbUsedSlop)); } } #if DEBUG - if (result == false) + if (result) + { + JITDUMP("Updated max weight of " FMT_BB " -> " FMT_BB " to [" FMT_WT ".." FMT_WT "]\n", getBlock()->bbNum, + bDst->bbNum, flEdgeWeightMin, flEdgeWeightMax); + } + else { JITDUMP("Not adjusting max weight of " FMT_BB " -> " FMT_BB "; new value " FMT_WT " not in range [" FMT_WT ".." FMT_WT "] (+/- " FMT_WT ")\n", getBlock()->bbNum, bDst->bbNum, newWeight, flEdgeWeightMin, flEdgeWeightMax, slop); result = false; // break here } - else - { - JITDUMP("Updated max weight of " FMT_BB " -> " FMT_BB " to [" FMT_WT ".." FMT_WT "]\n", getBlock()->bbNum, - bDst->bbNum, flEdgeWeightMin, flEdgeWeightMax); - } #endif // DEBUG return result; @@ -3099,31 +3102,40 @@ void Compiler::fgComputeEdgeWeights() } otherEdge = fgGetPredForBlock(otherDst, bSrc); - noway_assert(edge->edgeWeightMin() <= edge->edgeWeightMax()); - noway_assert(otherEdge->edgeWeightMin() <= otherEdge->edgeWeightMax()); + // If we see min/max violations, just give up on the computations + // + const bool edgeWeightSensible = edge->edgeWeightMin() <= edge->edgeWeightMax(); + const bool otherEdgeWeightSensible = otherEdge->edgeWeightMin() <= otherEdge->edgeWeightMax(); - // Adjust edge->flEdgeWeightMin up or adjust otherEdge->flEdgeWeightMax down - diff = bSrc->bbWeight - (edge->edgeWeightMin() + otherEdge->edgeWeightMax()); - if (diff > 0) - { - assignOK &= edge->setEdgeWeightMinChecked(edge->edgeWeightMin() + diff, bDst, slop, &usedSlop); - } - else if (diff < 0) - { - assignOK &= otherEdge->setEdgeWeightMaxChecked(otherEdge->edgeWeightMax() + diff, otherDst, - slop, &usedSlop); - } + assignOK &= edgeWeightSensible && otherEdgeWeightSensible; - // Adjust otherEdge->flEdgeWeightMin up or adjust edge->flEdgeWeightMax down - diff = bSrc->bbWeight - (otherEdge->edgeWeightMin() + edge->edgeWeightMax()); - if (diff > 0) - { - assignOK &= otherEdge->setEdgeWeightMinChecked(otherEdge->edgeWeightMin() + diff, otherDst, - slop, &usedSlop); - } - else if (diff < 0) + if (assignOK) { - assignOK &= edge->setEdgeWeightMaxChecked(edge->edgeWeightMax() + diff, bDst, slop, &usedSlop); + // Adjust edge->flEdgeWeightMin up or adjust otherEdge->flEdgeWeightMax down + diff = bSrc->bbWeight - (edge->edgeWeightMin() + otherEdge->edgeWeightMax()); + if (diff > 0) + { + assignOK &= + edge->setEdgeWeightMinChecked(edge->edgeWeightMin() + diff, bDst, slop, &usedSlop); + } + else if (diff < 0) + { + assignOK &= otherEdge->setEdgeWeightMaxChecked(otherEdge->edgeWeightMax() + diff, otherDst, + slop, &usedSlop); + } + + // Adjust otherEdge->flEdgeWeightMin up or adjust edge->flEdgeWeightMax down + diff = bSrc->bbWeight - (otherEdge->edgeWeightMin() + edge->edgeWeightMax()); + if (diff > 0) + { + assignOK &= otherEdge->setEdgeWeightMinChecked(otherEdge->edgeWeightMin() + diff, otherDst, + slop, &usedSlop); + } + else if (diff < 0) + { + assignOK &= + edge->setEdgeWeightMaxChecked(edge->edgeWeightMax() + diff, bDst, slop, &usedSlop); + } } if (!assignOK) @@ -3194,33 +3206,41 @@ void Compiler::fgComputeEdgeWeights() // otherMaxEdgesWeightSum is the sum of all of the other edges flEdgeWeightMax values // This can be used to compute a lower bound for our minimum edge weight - noway_assert(maxEdgeWeightSum >= edge->edgeWeightMax()); - BasicBlock::weight_t otherMaxEdgesWeightSum = maxEdgeWeightSum - edge->edgeWeightMax(); - - // otherMinEdgesWeightSum is the sum of all of the other edges flEdgeWeightMin values - // This can be used to compute an upper bound for our maximum edge weight - noway_assert(minEdgeWeightSum >= edge->edgeWeightMin()); - BasicBlock::weight_t otherMinEdgesWeightSum = minEdgeWeightSum - edge->edgeWeightMin(); + // + BasicBlock::weight_t const otherMaxEdgesWeightSum = maxEdgeWeightSum - edge->edgeWeightMax(); - if (bDstWeight >= otherMaxEdgesWeightSum) + if (otherMaxEdgesWeightSum >= BB_ZERO_WEIGHT) { - // minWeightCalc is our minWeight when every other path to bDst takes it's flEdgeWeightMax value - BasicBlock::weight_t minWeightCalc = - (BasicBlock::weight_t)(bDstWeight - otherMaxEdgesWeightSum); - if (minWeightCalc > edge->edgeWeightMin()) + if (bDstWeight >= otherMaxEdgesWeightSum) { - assignOK &= edge->setEdgeWeightMinChecked(minWeightCalc, bDst, slop, &usedSlop); + // minWeightCalc is our minWeight when every other path to bDst takes it's flEdgeWeightMax + // value + BasicBlock::weight_t minWeightCalc = + (BasicBlock::weight_t)(bDstWeight - otherMaxEdgesWeightSum); + if (minWeightCalc > edge->edgeWeightMin()) + { + assignOK &= edge->setEdgeWeightMinChecked(minWeightCalc, bDst, slop, &usedSlop); + } } } - if (bDstWeight >= otherMinEdgesWeightSum) + // otherMinEdgesWeightSum is the sum of all of the other edges flEdgeWeightMin values + // This can be used to compute an upper bound for our maximum edge weight + // + BasicBlock::weight_t const otherMinEdgesWeightSum = minEdgeWeightSum - edge->edgeWeightMin(); + + if (otherMinEdgesWeightSum >= BB_ZERO_WEIGHT) { - // maxWeightCalc is our maxWeight when every other path to bDst takes it's flEdgeWeightMin value - BasicBlock::weight_t maxWeightCalc = - (BasicBlock::weight_t)(bDstWeight - otherMinEdgesWeightSum); - if (maxWeightCalc < edge->edgeWeightMax()) + if (bDstWeight >= otherMinEdgesWeightSum) { - assignOK &= edge->setEdgeWeightMaxChecked(maxWeightCalc, bDst, slop, &usedSlop); + // maxWeightCalc is our maxWeight when every other path to bDst takes it's flEdgeWeightMin + // value + BasicBlock::weight_t maxWeightCalc = + (BasicBlock::weight_t)(bDstWeight - otherMinEdgesWeightSum); + if (maxWeightCalc < edge->edgeWeightMax()) + { + assignOK &= edge->setEdgeWeightMaxChecked(maxWeightCalc, bDst, slop, &usedSlop); + } } } diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index e8250cd56dd0..54deec92534c 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -21539,12 +21539,12 @@ void Compiler::considerGuardedDevirtualization( JITDUMP("Considering guarded devirtualization\n"); - // We currently only get likely class guesses when there is PGO data. So if we've disabled - // PGO, just bail out. - - if (JitConfig.JitDisablePGO() != 0) + // We currently only get likely class guesses when there is PGO data + // with class profiles. + // + if (fgPgoClassProfiles == 0) { - JITDUMP("Not guessing for class; pgo disabled\n"); + JITDUMP("Not guessing for class: no class profile pgo data, or pgo disabled\n"); return; } diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index d283522b2cdd..055750272e33 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -463,7 +463,10 @@ CONFIG_INTEGER(JitClassProfiling, W("JitClassProfiling"), 1) CONFIG_INTEGER(JitEdgeProfiling, W("JitEdgeProfiling"), 1) // Profile consumption options -CONFIG_INTEGER(JitDisablePGO, W("JitDisablePGO"), 0) // Ignore pgo data +CONFIG_INTEGER(JitDisablePgo, W("JitDisablePgo"), 0) // Ignore pgo data for all methods +#if defined(DEBUG) +CONFIG_STRING(JitEnablePgoRange, W("JitEnablePgoRange")) // Enable pgo data for only some methods +#endif // debug // Control when Virtual Calls are expanded CONFIG_INTEGER(JitExpandCallsEarly, W("JitExpandCallsEarly"), 1) // Expand Call targets early (in the global morph From 8fc5789cad3fce1ff42c46790d350382d7a27ce0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20G=C3=A5rdebrink?= Date: Mon, 29 Mar 2021 18:22:06 +0200 Subject: [PATCH 15/98] Do not mutate IEnumerable and null values when formatting (#49228) Co-authored-by: Stephen Toub --- .../src/LogValuesFormatter.cs | 28 +++++++++++++++++++ .../src/LoggerMessage.cs | 6 ++-- .../JsonConsoleFormatterTests.cs | 4 +-- .../tests/Common/FormattedLogValuesTest.cs | 24 ++++++++++++++++ .../tests/Common/LoggerMessageTest.cs | 28 +++++++++++++++++++ 5 files changed, 85 insertions(+), 5 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LogValuesFormatter.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LogValuesFormatter.cs index 0ea20baa236d..aed4014fdc6b 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LogValuesFormatter.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LogValuesFormatter.cs @@ -128,6 +128,34 @@ private static int FindIndexOfAny(string format, char[] chars, int startIndex, i } public string Format(object?[]? values) + { + object?[]? formattedValues = values; + + if (values != null) + { + for (int i = 0; i < values.Length; i++) + { + object formattedValue = FormatArgument(values[i]); + // If the formatted value is changed, we allocate and copy items to a new array to avoid mutating the array passed in to this method + if (!ReferenceEquals(formattedValue, values[i])) + { + formattedValues = new object[values.Length]; + Array.Copy(values, formattedValues, i); + formattedValues[i++] = formattedValue; + for (; i < values.Length; i++) + { + formattedValues[i] = FormatArgument(values[i]); + } + break; + } + } + } + + return string.Format(CultureInfo.InvariantCulture, _format, formattedValues ?? Array.Empty()); + } + + // NOTE: This method mutates the items in the array if needed to avoid extra allocations, and should only be used when caller expects this to happen + internal string FormatWithOverwrite(object?[]? values) { if (values != null) { diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerMessage.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerMessage.cs index c10693ce3a0f..ee40fc116bc3 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerMessage.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerMessage.cs @@ -700,7 +700,7 @@ public LogValues(LogValuesFormatter formatter, T0 value0, T1 value1, T2 value2, private object?[] ToArray() => new object?[] { _value0, _value1, _value2, _value3 }; - public override string ToString() => _formatter.Format(ToArray()); + public override string ToString() => _formatter.FormatWithOverwrite(ToArray()); public IEnumerator> GetEnumerator() { @@ -765,7 +765,7 @@ public LogValues(LogValuesFormatter formatter, T0 value0, T1 value1, T2 value2, private object?[] ToArray() => new object?[] { _value0, _value1, _value2, _value3, _value4 }; - public override string ToString() => _formatter.Format(ToArray()); + public override string ToString() => _formatter.FormatWithOverwrite(ToArray()); public IEnumerator> GetEnumerator() { @@ -834,7 +834,7 @@ public LogValues(LogValuesFormatter formatter, T0 value0, T1 value1, T2 value2, private object?[] ToArray() => new object?[] { _value0, _value1, _value2, _value3, _value4, _value5 }; - public override string ToString() => _formatter.Format(ToArray()); + public override string ToString() => _formatter.FormatWithOverwrite(ToArray()); public IEnumerator> GetEnumerator() { diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/JsonConsoleFormatterTests.cs b/src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/JsonConsoleFormatterTests.cs index e4df53adbc49..f58e10b84035 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/JsonConsoleFormatterTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/JsonConsoleFormatterTests.cs @@ -406,8 +406,8 @@ public static TheoryData SpecialCaseValues // Dynamic object serialized as string { new { a = 1, b = 2 }, "\"{ a = 1, b = 2 }\"" }, - // null serialized as special string - { null, "\"(null)\"" } + // null should not be serialized as special string in the state value, only in message + { null, "null" } }; return data; } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Common/FormattedLogValuesTest.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Common/FormattedLogValuesTest.cs index 65355751b7a8..c20ede7d712b 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Common/FormattedLogValuesTest.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Common/FormattedLogValuesTest.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; using System.Linq; using Xunit; @@ -94,6 +95,29 @@ public void LogValues_With_UnbalancedBraces(string format) }); } + [Fact] + public void LogValues_WithNullAndEnumerable_IsNotMutatingParameter() + { + string format = "TestMessage {Param1} {Param2} {Param3} {Param4}"; + int param1 = 1; + string param2 = null; + int[] param3 = new[] { 1, 2, 3, 4 }; + string param4 = "string"; + + var logValues = new FormattedLogValues(format, param1, param2, param3, param4); + logValues.ToString(); + + var state = logValues.ToArray(); + Assert.Equal(new[] + { + new KeyValuePair("Param1", param1), + new KeyValuePair("Param2", param2), + new KeyValuePair("Param3", param3), + new KeyValuePair("Param4", param4), + new KeyValuePair("{OriginalFormat}", format), + }, state); + } + [Fact] public void CachedFormattersAreCapped() { diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Common/LoggerMessageTest.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Common/LoggerMessageTest.cs index 6b4e70a4c005..49e2a17c54fd 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Common/LoggerMessageTest.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Common/LoggerMessageTest.cs @@ -193,6 +193,34 @@ public void LogMessages(Delegate messageDelegate, int argumentCount) Assert.Equal(expectedToString, actualLogValues.ToString()); } + [Fact] + public void LogMessage_WithNullParameter_DoesNotMutateArgument() + { + // Arrange + string format = "TestMessage {param1} {param2} {param3}"; + string param1 = "foo"; + string param2 = null; + int param3 = 10; + var testSink = new TestSink(); + var testLogger = new TestLogger("testlogger", testSink, enabled: true); + + // Act + testLogger.LogInformation(format, param1, param2, param3); + + // Assert + Assert.Single(testSink.Writes); + var write = testSink.Writes.First(); + var actualLogValues = Assert.IsAssignableFrom>>(write.State); + AssertLogValues(new[] + { + new KeyValuePair("param1", param1), + new KeyValuePair("param2", param2), + new KeyValuePair("param3", param3), + new KeyValuePair("{OriginalFormat}", format) + }, + actualLogValues.ToArray()); + } + [Fact] public void DefineMessage_WithNoParameters_ThrowsException_WhenFormatString_HasNamedParameters() { From 860518b0011e7f7faf556faaa862d95619446112 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 29 Mar 2021 12:15:54 -0500 Subject: [PATCH 16/98] [main] Update dependencies from mono/linker (#50232) * Update dependencies from https://github.com/mono/linker build 20210324.1 Microsoft.NET.ILLink.Tasks From Version 6.0.100-preview.2.21172.2 -> To Version 6.0.100-preview.2.21174.1 * Update dependencies from https://github.com/mono/linker build 20210326.1 Microsoft.NET.ILLink.Tasks From Version 6.0.100-preview.2.21172.2 -> To Version 6.0.100-preview.2.21176.1 * Update dependencies from https://github.com/mono/linker build 20210327.1 Microsoft.NET.ILLink.Tasks From Version 6.0.100-preview.2.21172.2 -> To Version 6.0.100-preview.2.21177.1 * Add missing annotations * Always keep ValueType constructor * Fix exceptions descriptors to match what native lookup expects Co-authored-by: dotnet-maestro[bot] Co-authored-by: Marek Safar --- eng/Version.Details.xml | 4 +- eng/Versions.props | 2 +- .../src/System/RuntimeHandles.cs | 4 +- .../src/ILLink/ILLink.Descriptors.xml | 39 ++++++++++--------- 4 files changed, 25 insertions(+), 24 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index b926f2348771..abea99b10f5f 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -194,9 +194,9 @@ https://github.com/dotnet/runtime 76954b4ed7b5cd48ace16fefb1209fe56779b247 - + https://github.com/mono/linker - 2f5594e95e1a5227103b9b7d6b92f4b2250be2d7 + 318105ce4619c651d26caeed4cb32c63eefdf3ee https://github.com/dotnet/xharness diff --git a/eng/Versions.props b/eng/Versions.props index d974f9a2ff7c..320a69968a16 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -159,7 +159,7 @@ 5.0.0-preview-20201009.2 - 6.0.100-preview.2.21172.2 + 6.0.100-preview.2.21177.1 6.0.0-preview.4.21172.5 diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs index 60fe3a180d24..f5eb863c158e 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -209,7 +209,7 @@ internal static bool HasElementType(RuntimeType type) return outHandles; } - internal static object CreateInstanceForAnotherGenericParameter(RuntimeType type, RuntimeType genericParameter) + internal static object CreateInstanceForAnotherGenericParameter([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType type, RuntimeType genericParameter) { object? instantiatedObject = null; @@ -224,7 +224,7 @@ internal static object CreateInstanceForAnotherGenericParameter(RuntimeType type return instantiatedObject!; } - internal static object CreateInstanceForAnotherGenericParameter(RuntimeType type, RuntimeType genericParameter1, RuntimeType genericParameter2) + internal static object CreateInstanceForAnotherGenericParameter([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType type, RuntimeType genericParameter1, RuntimeType genericParameter2) { object? instantiatedObject = null; diff --git a/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml b/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml index f066b8f010d4..a04c78e98cc0 100644 --- a/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml +++ b/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml @@ -23,7 +23,7 @@ - + @@ -61,7 +61,7 @@ - + @@ -125,7 +125,7 @@ - + @@ -156,13 +156,13 @@ - + - + @@ -195,13 +195,12 @@ - + - @@ -214,15 +213,12 @@ - - - - + @@ -386,7 +382,7 @@ - + @@ -448,7 +444,7 @@ - + @@ -457,7 +453,7 @@ - + @@ -537,13 +533,13 @@ - + - + @@ -566,7 +562,8 @@ - + + @@ -585,7 +582,7 @@ - + @@ -608,7 +605,7 @@ - + @@ -651,5 +648,9 @@ + + + + From 473f8f6be3497e0c4665d971ac4978b40448d3f9 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 29 Mar 2021 13:26:22 -0400 Subject: [PATCH 17/98] Fix typo in comment in BitOperation.cs --- .../System.Private.CoreLib/src/System/Numerics/BitOperations.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs index b3b03d06ee93..826a129be7f8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs @@ -76,7 +76,7 @@ public static class BitOperations internal static uint RoundUpToPowerOf2(uint value) { // TODO: https://github.com/dotnet/runtime/issues/43135 - // When this is exposed publicly, decide on behavior be for the boundary cases... + // When this is exposed publicly, decide on the behavior for the boundary cases... // the accelerated and fallback paths differ. Debug.Assert(value > 0 && value <= (uint.MaxValue / 2) + 1); From 93b8e18029e784fa8e96e7a01381137125ca632d Mon Sep 17 00:00:00 2001 From: Omair Majid Date: Mon, 29 Mar 2021 13:49:48 -0400 Subject: [PATCH 18/98] Use a NewHolder for pEmitter to avoid memory leak (#50242) It seems like we are allocating memory, but never releasing it. --- src/coreclr/vm/ceeload.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/ceeload.cpp b/src/coreclr/vm/ceeload.cpp index 8a5ad542c5a5..12c12f41b0b2 100644 --- a/src/coreclr/vm/ceeload.cpp +++ b/src/coreclr/vm/ceeload.cpp @@ -11855,7 +11855,7 @@ HRESULT Module::WriteMethodProfileDataLogFile(bool cleanup) { if (GetAssembly()->IsInstrumented() && (m_pProfilingBlobTable != NULL) && (m_tokenProfileData != NULL)) { - ProfileEmitter * pEmitter = new ProfileEmitter(); + NewHolder pEmitter; // Get this ahead of time - metadata access may be logged, which will // take the m_tokenProfileData->crst, which we take a couple lines below From de867a9d6d82b27f7752d7257022d44acef6c9c4 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Mon, 29 Mar 2021 11:06:44 -0700 Subject: [PATCH 19/98] Propagate XUnitMethodName/XUnitClassName properties to Android test runner (#50295) --- eng/testing/AndroidRunnerTemplate.sh | 4 ++-- eng/testing/AppleRunnerTemplate.sh | 4 ++-- eng/testing/tests.mobile.targets | 10 ++++++++++ eng/testing/tests.targets | 2 +- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/eng/testing/AndroidRunnerTemplate.sh b/eng/testing/AndroidRunnerTemplate.sh index 26698577798c..3dab96f0bf3c 100644 --- a/eng/testing/AndroidRunnerTemplate.sh +++ b/eng/testing/AndroidRunnerTemplate.sh @@ -10,7 +10,7 @@ TEST_NAME=$4 XHARNESS_OUT="$EXECUTION_DIR/xharness-output" if [ -n "$5" ]; then - EXPECTED_EXIT_CODE="--expected-exit-code $5" + ADDITIONAL_ARGS=${@:5} fi cd $EXECUTION_DIR @@ -41,7 +41,7 @@ $HARNESS_RUNNER android test \ --app="$EXECUTION_DIR/bin/$TEST_NAME.apk" \ --output-directory="$XHARNESS_OUT" \ --timeout=1800 \ - $EXPECTED_EXIT_CODE + $ADDITIONAL_ARGS _exitCode=$? diff --git a/eng/testing/AppleRunnerTemplate.sh b/eng/testing/AppleRunnerTemplate.sh index 5ec52b36c7ee..0f2eb7f2abed 100644 --- a/eng/testing/AppleRunnerTemplate.sh +++ b/eng/testing/AppleRunnerTemplate.sh @@ -13,7 +13,7 @@ XCODE_PATH=$(xcode-select -p)/../.. if [ -n "$5" ]; then XHARNESS_CMD="run" - EXPECTED_EXIT_CODE="--expected-exit-code $5" + ADDITIONAL_ARGS=${@:5} fi if [[ "$TARGET_OS" == "MacCatalyst" ]]; then TARGET=maccatalyst; fi @@ -62,7 +62,7 @@ $HARNESS_RUNNER apple $XHARNESS_CMD \ --targets="$TARGET" \ --xcode="$XCODE_PATH" \ --output-directory="$XHARNESS_OUT" \ - $EXPECTED_EXIT_CODE + $ADDITIONAL_ARGS _exitCode=$? diff --git a/eng/testing/tests.mobile.targets b/eng/testing/tests.mobile.targets index 8abdbbb54c93..81246339652c 100644 --- a/eng/testing/tests.mobile.targets +++ b/eng/testing/tests.mobile.targets @@ -50,6 +50,16 @@ true + + --expected-exit-code $(ExpectedExitCode) + + + + + $(AdditionalXHarnessArguments) --arg=-m=$(XUnitMethodName) + $(AdditionalXHarnessArguments) --arg=-c=$(XUnitClassName) + + diff --git a/eng/testing/tests.targets b/eng/testing/tests.targets index 1ad1ae7df0df..b7b642b3527e 100644 --- a/eng/testing/tests.targets +++ b/eng/testing/tests.targets @@ -96,7 +96,7 @@ $(RunTestsCommand) --runtime-path "$(TestHostRootPath.TrimEnd('\/'))" $(RunTestsCommand) --rsp-file "$(TestRspFile)" - "$(RunScriptOutputPath)" $(AssemblyName) $(TargetArchitecture) $(TargetOS) $(TestProjectName) $(ExpectedExitCode) + "$(RunScriptOutputPath)" $(AssemblyName) $(TargetArchitecture) $(TargetOS) $(TestProjectName) $(AdditionalXHarnessArguments) "$(RunScriptOutputPath)" $(JSEngine) $(AssemblyName).dll $(Scenario) From 3870c9f7f85df78aa12e48e0157998d8f4dc233a Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 29 Mar 2021 14:52:23 -0400 Subject: [PATCH 20/98] Remove unnecessary delegate/closure allocations from PollForValues (#50357) It allocates a delegate/closure for each counter group each time the loop runs. --- .../src/System/Diagnostics/Tracing/CounterGroup.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs index 3146d1f70546..10da258166b0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs @@ -271,10 +271,9 @@ private static void PollForValues() // We cache these outside of the scope of s_counterGroupLock because // calling into the callbacks can cause a re-entrancy into CounterGroup.Enable() // and result in a deadlock. (See https://github.com/dotnet/runtime/issues/40190 for details) - List onTimers = new List(); + var onTimers = new List(); while (true) { - onTimers.Clear(); int sleepDurationInMilliseconds = int.MaxValue; lock (s_counterGroupLock) { @@ -284,7 +283,7 @@ private static void PollForValues() DateTime now = DateTime.UtcNow; if (counterGroup._nextPollingTimeStamp < now + new TimeSpan(0, 0, 0, 0, 1)) { - onTimers.Add(() => counterGroup.OnTimer()); + onTimers.Add(counterGroup); } int millisecondsTillNextPoll = (int)((counterGroup._nextPollingTimeStamp - now).TotalMilliseconds); @@ -292,10 +291,11 @@ private static void PollForValues() sleepDurationInMilliseconds = Math.Min(sleepDurationInMilliseconds, millisecondsTillNextPoll); } } - foreach (Action onTimer in onTimers) + foreach (CounterGroup onTimer in onTimers) { - onTimer.Invoke(); + onTimer.OnTimer(); } + onTimers.Clear(); if (sleepDurationInMilliseconds == int.MaxValue) { sleepDurationInMilliseconds = -1; // WaitOne uses -1 to mean infinite From 617a18d1af7acc86185214ddb951d57f07fd1587 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Mon, 29 Mar 2021 15:33:54 -0400 Subject: [PATCH 21/98] [wasm] Wasm.Build.Tests: add support for shared builds (#49398) * [wasm] Wasm.Build.Tests: add support for shared builds - Essentially, we want to share builds wherever possible. Example cases: - Same build, but run with different hosts like v8/chrome/safari, as separate test runs - Same build but run with different command line arguments - Sharing builds especially helps when we are AOT'ing, which is slow! - This is done by caching the builds with the key: `public record BuildArgs(string ProjectName, string Config, bool AOT, string ProjectFileContents, string? ExtraBuildArgs);` - Also, ` SharedBuildClassFixture` is added, so that the builds can be cleaned up after all the tests in a particular class have finished running. - Each test run gets a randomly generated test id. This is used for creating: 1. build paths, like `artifacts/bin/Wasm.Build.Tests/net6.0-Release/browser-wasm/xharness-output/logs/n1xwbqxi.ict` 2. and the log for running with xharness, eg. for Chrome, are in `artifacts/bin/Wasm.Build.Tests/net6.0-Release/browser-wasm/xharness-output/logs/n1xwbqxi.ict/Chrome/` - split `WasmBuildAppTest.cs` into : `BuildTestBase.cs`, and `MainWithArgsTests.cs`. * [wasm] Wasm.Build.Tests: Add test for relinking with InvariantGlobalization * [wasm] Wasm.Build.Tests - Check `CultureInfo` for invariant culture .. tests. Code stolen from @maximlipin's https://github.com/dotnet/runtime/pull/49204/ * fix invariant+aot test * [wasm] Fix the order of include paths For AOT we generate `pinvoke-table.h` in the obj directory. But there is one present in the runtime pack too. In my earlier changes the order in which these were passed as include search paths was changed from: `"-I/runtime/pack/microsoft.netcore.app.runtime.browser-wasm/Release/runtimes/browser-wasm/native/include/wasm" "-Iartifacts/obj/mono/Wasm.Console.Sample/wasm/Release/browser-wasm/wasm/"` .. which meant that the one from the runtime pack took precedence, and got used. So, fix the order! And change the property names to indicate where they are sourced from. * [wasm] Only test with Release config on CI * [wasm] Fallback to `dotnet xharness` if `XHARNESS_CLI_PATH` is not set. The environment variable is set on helix. During local testing it can be useful when using a locally built xharness. * [wasm] fix invariant test - 'en-ES' -> 'es-ES' * [wasm] RunWithEmSdkEnv: log the working directory also * [wasm] Re-enable wasm build tests * [wasm] Add regression test for issue #49588 * fix test * [wasm] Cleanup, and add more tests * Update tests to track wasm relinking being default in some cases * Fix InvariantGlobalization to track change in wasm relinking defaults * [wasm] Update emsdk check message to track changes * [wasm] Update tests to track changes * [wasm] Move Scenario=BuildWasmApps to be submitted first TLDR; - this might help with the job getting scheduled first, and thus having a chance at completing at the same time as others. Reasoning: The problem this is trying to fix is: 1. The helix step submits 3 jobs: a. library tests to be run with v8 b. library tests to be run with browser (scenario=wasmtestonbrowser) c. Wasm.Build.Tests (scenario=buildwasmapps) 2. The 3 jobs, individually take roughly 30mins each 3. And they get submitted at roughly the same time 4. But .. the first two seem to complete earlier, and the 3rd one completes 25-30mins later. The hypothesis is that all the machines might be busy processing the 200+ work items from each of the first two steps, and so Wasm.Build.Tests get scheduled pretty late. So, here we move that to be submitted first, in the hope that it would be able to run in parallel with the other jobs. --- eng/pipelines/runtime.yml | 1 + src/mono/wasm/build/WasmApp.targets | 28 +- src/tasks/WasmAppBuilder/RunWithEmSdkEnv.cs | 3 + .../Wasm.Build.Tests/BuildAndRunAttribute.cs | 40 + .../Wasm.Build.Tests/BuildTestBase.cs | 589 +++++++++++++++ .../Wasm.Build.Tests/HelperExtensions.cs | 96 +++ .../InvariantGlobalizationTests.cs | 85 +++ .../Wasm.Build.Tests/MainWithArgsTests.cs | 100 +++ .../Wasm.Build.Tests/NativeBuildTests.cs | 70 ++ .../BuildWasmApps/Wasm.Build.Tests/RunHost.cs | 19 + .../SharedBuildPerTestClassFixture.cs | 57 ++ .../Wasm.Build.Tests/Wasm.Build.Tests.csproj | 4 +- .../Wasm.Build.Tests/WasmBuildAppTest.cs | 683 ++---------------- 13 files changed, 1155 insertions(+), 620 deletions(-) create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/BuildAndRunAttribute.cs create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/HelperExtensions.cs create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/InvariantGlobalizationTests.cs create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/MainWithArgsTests.cs create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/RunHost.cs create mode 100644 src/tests/BuildWasmApps/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs diff --git a/eng/pipelines/runtime.yml b/eng/pipelines/runtime.yml index 4819accff31f..df3f7ca8184b 100644 --- a/eng/pipelines/runtime.yml +++ b/eng/pipelines/runtime.yml @@ -293,6 +293,7 @@ jobs: creator: dotnet-bot testRunNamePrefixSuffix: Mono_$(_BuildConfig) scenarios: + - buildwasmapps - normal - wasmtestonbrowser condition: >- diff --git a/src/mono/wasm/build/WasmApp.targets b/src/mono/wasm/build/WasmApp.targets index 44b256bf2f72..287f963beabd 100644 --- a/src/mono/wasm/build/WasmApp.targets +++ b/src/mono/wasm/build/WasmApp.targets @@ -198,6 +198,11 @@ + + + true true @@ -390,7 +395,8 @@ - + $([MSBuild]::NormalizePath($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'src', 'emcc-flags.txt')) @@ -429,8 +435,8 @@ OutputPath="$(_WasmIntermediateOutputPath)icall-table.h" /> $(EmccFlags) -DLINK_ICALLS=1 - <_WasmIncludeDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'include')) - <_WasmSrcDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'src')) + <_WasmRuntimePackIncludeDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'include')) + <_WasmRuntimePackSrcDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'src')) @@ -447,15 +453,15 @@ <_WasmObjectsToBuild Include="$(_WasmIntermediateOutputPath)driver.o"/> <_WasmObjectsToBuild Include="$(_WasmIntermediateOutputPath)pinvoke.o"/> <_WasmObjectsToBuild Include="$(_WasmIntermediateOutputPath)corebindings.o"/> - <_WasmObjectsToBuild SourcePath="$(_WasmSrcDir)%(FileName).c" /> + <_WasmObjectsToBuild SourcePath="$(_WasmRuntimePackSrcDir)%(FileName).c" /> <_WasmObjects Include="@(_WasmRuntimePackNativeLibs->'$(MicrosoftNetCoreAppRuntimePackRidNativeDir)%(FileName)%(Extension)')" /> <_WasmObjects Include="@(_WasmObjectsToBuild)" /> - <_DotnetJSSrcFile Include="$(_WasmSrcDir)library_mono.js" /> - <_DotnetJSSrcFile Include="$(_WasmSrcDir)binding_support.js" /> - <_DotnetJSSrcFile Include="$(_WasmSrcDir)dotnet_support.js" /> - <_DotnetJSSrcFile Include="$(_WasmSrcDir)pal_random.js" /> + <_DotnetJSSrcFile Include="$(_WasmRuntimePackSrcDir)library_mono.js" /> + <_DotnetJSSrcFile Include="$(_WasmRuntimePackSrcDir)binding_support.js" /> + <_DotnetJSSrcFile Include="$(_WasmRuntimePackSrcDir)dotnet_support.js" /> + <_DotnetJSSrcFile Include="$(_WasmRuntimePackSrcDir)pal_random.js" /> <_AOTAssemblies Include="@(_WasmAssembliesInternal)" Condition="'%(_WasmAssembliesInternal._InternalForceInterpret)' != 'true'" /> <_BitcodeFile Include="%(_WasmAssembliesInternal.LlvmBitcodeFile)" /> @@ -465,7 +471,7 @@ Text="Bug: Number of aot assemblies doesn't match the number of generated bitcode files. BitcodeFiles: @(_BitcodeFile->Count()) vs Assemblies: @(_AOTAssemblies->Count())" /> - $(EmccFlags) -DCORE_BINDINGS -DGEN_PINVOKE=1 "-I$(_WasmIncludeDir)mono-2.0" "-I$(_WasmIncludeDir)wasm" + $(EmccFlags) -DCORE_BINDINGS -DGEN_PINVOKE=1 $(EmccCFlags) -g @@ -27,8 +29,6 @@ - - diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs index 29b31eea76d7..3778d1fcdaae 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs @@ -1,12 +1,8 @@ // 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.Collections.Generic; -using System.Diagnostics; using System.IO; -using System.Linq; -using System.Text; using Xunit; using Xunit.Abstractions; @@ -14,185 +10,28 @@ namespace Wasm.Build.Tests { - public class WasmBuildAppTest : IDisposable + public class WasmBuildAppTest : BuildTestBase { - private const string TestLogPathEnvVar = "TEST_LOG_PATH"; - private const string SkipProjectCleanupEnvVar = "SKIP_PROJECT_CLEANUP"; - private const string XHarnessRunnerCommandEnvVar = "XHARNESS_CLI_PATH"; + public WasmBuildAppTest(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) : base(output, buildContext) + {} - private readonly string _tempDir; - private readonly ITestOutputHelper _testOutput; - private readonly string _id; - private readonly string _logPath; + public static IEnumerable MainMethodTestData(bool aot, RunHost host) + => ConfigWithAOTData(aot) + .WithRunHosts(host) + .UnwrapItemsAsArrays(); - private const string s_targetFramework = "net5.0"; - private static string s_runtimeConfig = "Release"; - private static string s_runtimePackDir; - private static string s_defaultBuildArgs; - private static readonly string s_logRoot; - private static readonly string s_emsdkPath; - private static readonly bool s_skipProjectCleanup; - private static readonly string s_xharnessRunnerCommand; - - static WasmBuildAppTest() - { - DirectoryInfo? solutionRoot = new (AppContext.BaseDirectory); - while (solutionRoot != null) - { - if (File.Exists(Path.Combine(solutionRoot.FullName, "NuGet.config"))) - { - break; - } - - solutionRoot = solutionRoot.Parent; - } - - if (solutionRoot == null) - { - string? buildDir = Environment.GetEnvironmentVariable("WasmBuildSupportDir"); - - if (buildDir == null || !Directory.Exists(buildDir)) - throw new Exception($"Could not find the solution root, or a build dir: {buildDir}"); - - s_emsdkPath = Path.Combine(buildDir, "emsdk"); - s_runtimePackDir = Path.Combine(buildDir, "microsoft.netcore.app.runtime.browser-wasm"); - s_defaultBuildArgs = $" /p:WasmBuildSupportDir={buildDir} /p:EMSDK_PATH={s_emsdkPath} "; - } - else - { - string artifactsBinDir = Path.Combine(solutionRoot.FullName, "artifacts", "bin"); - s_runtimePackDir = Path.Combine(artifactsBinDir, "microsoft.netcore.app.runtime.browser-wasm", s_runtimeConfig); - - string? emsdk = Environment.GetEnvironmentVariable("EMSDK_PATH"); - if (string.IsNullOrEmpty(emsdk)) - emsdk = Path.Combine(solutionRoot.FullName, "src", "mono", "wasm", "emsdk"); - s_emsdkPath = emsdk; - - s_defaultBuildArgs = $" /p:RuntimeSrcDir={solutionRoot.FullName} /p:RuntimeConfig={s_runtimeConfig} /p:EMSDK_PATH={s_emsdkPath} "; - } - - string? logPathEnvVar = Environment.GetEnvironmentVariable(TestLogPathEnvVar); - if (!string.IsNullOrEmpty(logPathEnvVar)) - { - s_logRoot = logPathEnvVar; - if (!Directory.Exists(s_logRoot)) - { - Directory.CreateDirectory(s_logRoot); - } - } - else - { - s_logRoot = Environment.CurrentDirectory; - } - - string? cleanupVar = Environment.GetEnvironmentVariable(SkipProjectCleanupEnvVar); - s_skipProjectCleanup = !string.IsNullOrEmpty(cleanupVar) && cleanupVar == "1"; - - string? harnessVar = Environment.GetEnvironmentVariable(XHarnessRunnerCommandEnvVar); - if (string.IsNullOrEmpty(harnessVar)) - { - throw new Exception($"{XHarnessRunnerCommandEnvVar} not set"); - } - - s_xharnessRunnerCommand = harnessVar; - } - - public WasmBuildAppTest(ITestOutputHelper output) - { - _testOutput = output; - _id = Path.GetRandomFileName(); - _tempDir = Path.Combine(AppContext.BaseDirectory, _id); - Directory.CreateDirectory(_tempDir); - - _logPath = Path.Combine(s_logRoot, _id); - Directory.CreateDirectory(_logPath); - - _testOutput.WriteLine($"Test Id: {_id}"); - } - - - /* - * TODO: - - AOT modes - - llvmonly - - aotinterp - - skipped assemblies should get have their pinvoke/icall stuff scanned - - - only buildNative - - aot but no wrapper - check that AppBundle wasn't generated - */ - - - public static TheoryData ConfigWithAOTData(bool include_aot=true) - { - TheoryData data = new() - { - { "Debug", false }, - { "Release", false } - }; - - if (include_aot) - { - data.Add("Debug", true); - data.Add("Release", true); - } - - return data; - } - - public static TheoryData InvariantGlobalizationTestData() - { - var data = new TheoryData(); - foreach (var configData in ConfigWithAOTData()) - { - data.Add((string)configData[0], (bool)configData[1], null); - data.Add((string)configData[0], (bool)configData[1], true); - data.Add((string)configData[0], (bool)configData[1], false); - } - return data; - } - - // TODO: check that icu bits have been linked out [Theory] - [MemberData(nameof(InvariantGlobalizationTestData))] - public void InvariantGlobalization(string config, bool aot, bool? invariantGlobalization) - { - File.WriteAllText(Path.Combine(_tempDir, "Program.cs"), @" - using System; - using System.Threading.Tasks; - - public class TestClass { - public static int Main() - { - Console.WriteLine(""Hello, World!""); - return 42; - } - } - "); - - string? extraProperties = null; - if (invariantGlobalization != null) - extraProperties = $"{invariantGlobalization}"; - - string projectName = $"invariant_{invariantGlobalization?.ToString() ?? "unset"}"; - BuildProject(projectName, config, aot: aot, extraProperties: extraProperties, - hasIcudt: invariantGlobalization == null || invariantGlobalization.Value == false, - dotnetWasmFromRuntimePack: !(aot || config == "Release")); - - RunAndTestWasmApp(projectName, config, isAOT: aot, expectedExitCode: 42, - test: output => Assert.Contains("Hello, World!", output)); - } - - [Theory] - [MemberData(nameof(ConfigWithAOTData), parameters: /*aot*/ true)] - public void TopLevelMain(string config, bool aot) + [MemberData(nameof(MainMethodTestData), parameters: new object[] { /*aot*/ true, RunHost.All })] + [MemberData(nameof(MainMethodTestData), parameters: new object[] { /*aot*/ false, RunHost.All })] + public void TopLevelMain(BuildArgs buildArgs, RunHost host, string id) => TestMain("top_level", @"System.Console.WriteLine(""Hello, World!""); return await System.Threading.Tasks.Task.FromResult(42);", - config, aot); + buildArgs, host, id); [Theory] - [MemberData(nameof(ConfigWithAOTData), parameters: /*aot*/ true)] - public void AsyncMain(string config, bool aot) + [MemberData(nameof(MainMethodTestData), parameters: new object[] { /*aot*/ true, RunHost.All })] + [MemberData(nameof(MainMethodTestData), parameters: new object[] { /*aot*/ false, RunHost.All })] + public void AsyncMain(BuildArgs buildArgs, RunHost host, string id) => TestMain("async_main", @" using System; using System.Threading.Tasks; @@ -203,11 +42,12 @@ public static async Task Main() Console.WriteLine(""Hello, World!""); return await Task.FromResult(42); } - }", config, aot); + }", buildArgs, host, id); [Theory] - [MemberData(nameof(ConfigWithAOTData), parameters: /*aot*/ true)] - public void NonAsyncMain(string config, bool aot) + [MemberData(nameof(MainMethodTestData), parameters: new object[] { /*aot*/ true, RunHost.All })] + [MemberData(nameof(MainMethodTestData), parameters: new object[] { /*aot*/ false, RunHost.All })] + public void NonAsyncMain(BuildArgs buildArgs, RunHost host, string id) => TestMain("non_async_main", @" using System; using System.Threading.Tasks; @@ -218,447 +58,74 @@ public static int Main() Console.WriteLine(""Hello, World!""); return 42; } - }", config, aot); - - public static TheoryData MainWithArgsTestData() - { - var data = new TheoryData(); - foreach (var configData in ConfigWithAOTData()) - { - data.Add((string)configData[0], (bool)configData[1], new string[] { "abc", "foobar" }); - data.Add((string)configData[0], (bool)configData[1], new string[0]); - } - - return data; - } - - [Theory] - [MemberData(nameof(MainWithArgsTestData))] - public void NonAsyncMainWithArgs(string config, bool aot, string[] args) - => TestMainWithArgs("non_async_main_args", @" - public class TestClass { - public static int Main(string[] args) - { - ##CODE## - return 42 + count; - } - }", config, aot, args); - - [Theory] - [MemberData(nameof(MainWithArgsTestData))] - public void AsyncMainWithArgs(string config, bool aot, string[] args) - => TestMainWithArgs("async_main_args", @" - public class TestClass { - public static async System.Threading.Tasks.Task Main(string[] args) - { - ##CODE## - return await System.Threading.Tasks.Task.FromResult(42 + count); - } - }", config, aot, args); + }", buildArgs, host, id); [Theory] - [MemberData(nameof(MainWithArgsTestData))] - public void TopLevelWithArgs(string config, bool aot, string[] args) - => TestMainWithArgs("top_level_args", - @"##CODE## return await System.Threading.Tasks.Task.FromResult(42 + count);", - config, aot, args); - - void TestMain(string projectName, string programText, string config, bool aot) - { - File.WriteAllText(Path.Combine(_tempDir, "Program.cs"), programText); - BuildProject(projectName, config, aot: aot, dotnetWasmFromRuntimePack: !(aot || config == "Release")); - RunAndTestWasmApp(projectName, config, isAOT: aot, expectedExitCode: 42, - test: output => Assert.Contains("Hello, World!", output)); - } - - void TestMainWithArgs(string projectName, string programFormatString, string config, bool aot, string[] args) - { - string code = @" - int count = args == null ? 0 : args.Length; - System.Console.WriteLine($""args#: {args?.Length}""); - foreach (var arg in args ?? System.Array.Empty()) - System.Console.WriteLine($""arg: {arg}""); - "; - string programText = programFormatString.Replace("##CODE##", code); - - File.WriteAllText(Path.Combine(_tempDir, "Program.cs"), programText); - BuildProject(projectName, config, aot: aot, dotnetWasmFromRuntimePack: !(aot || config == "Release")); - RunAndTestWasmApp(projectName, config, isAOT: aot, expectedExitCode: 42 + args.Length, args: string.Join(' ', args), - test: output => - { - Assert.Contains($"args#: {args.Length}", output); - foreach (var arg in args) - Assert.Contains($"arg: {arg}", output); - }); - } - - private void RunAndTestWasmApp(string projectName, string config, bool isAOT, Action test, int expectedExitCode=0, string? args=null) - { - Dictionary? envVars = new(); - envVars["XHARNESS_DISABLE_COLORED_OUTPUT"] = "true"; - if (isAOT) - { - envVars["EMSDK_PATH"] = s_emsdkPath; - envVars["MONO_LOG_LEVEL"] = "debug"; - envVars["MONO_LOG_MASK"] = "aot"; - } - - string bundleDir = Path.Combine(GetBinDir(config: config), "AppBundle"); - string v8output = RunWasmTest(projectName, bundleDir, envVars, expectedExitCode, appArgs: args); - Test(v8output); - - string browserOutput = RunWasmTestBrowser(projectName, bundleDir, envVars, expectedExitCode, appArgs: args); - Test(browserOutput); - - void Test(string output) - { - if (isAOT) - { - Assert.Contains("AOT: image 'System.Private.CoreLib' found.", output); - Assert.Contains($"AOT: image '{projectName}' found.", output); - } - else - { - Assert.DoesNotContain("AOT: image 'System.Private.CoreLib' found.", output); - Assert.DoesNotContain($"AOT: image '{projectName}' found.", output); - } - } - } - - private string RunWithXHarness(string testCommand, string relativeLogPath, string projectName, string bundleDir, IDictionary? envVars=null, - int expectedAppExitCode=0, int xharnessExitCode=0, string? extraXHarnessArgs=null, string? appArgs=null) - { - _testOutput.WriteLine($"============== {testCommand} ============="); - Console.WriteLine($"============== {testCommand} ============="); - string testLogPath = Path.Combine(_logPath, relativeLogPath); - - StringBuilder args = new(); - args.Append($"exec {s_xharnessRunnerCommand}"); - args.Append($" {testCommand}"); - args.Append($" --app=."); - args.Append($" --output-directory={testLogPath}"); - args.Append($" --expected-exit-code={expectedAppExitCode}"); - args.Append($" {extraXHarnessArgs ?? string.Empty}"); - - args.Append(" -- "); - // App arguments - - if (envVars != null) - { - var setenv = string.Join(' ', envVars.Select(kvp => $"--setenv={kvp.Key}={kvp.Value}").ToArray()); - args.Append($" {setenv}"); - } - - args.Append($" --run {projectName}.dll"); - args.Append($" {appArgs ?? string.Empty}"); - - var (exitCode, output) = RunProcess("dotnet", - args: args.ToString(), - workingDir: bundleDir, - envVars: envVars, - label: testCommand); - - File.WriteAllText(Path.Combine(testLogPath, $"xharness.log"), output); - - if (exitCode != xharnessExitCode) - { - _testOutput.WriteLine($"Exit code: {exitCode}"); - Assert.True(exitCode == expectedAppExitCode, $"[{testCommand}] Exit code, expected {expectedAppExitCode} but got {exitCode}"); - } - - return output; - } - private string RunWasmTest(string projectName, string bundleDir, IDictionary? envVars=null, int expectedAppExitCode=0, int xharnessExitCode=0, string? appArgs=null) - => RunWithXHarness("wasm test", "wasm-test", projectName, bundleDir, - envVars: envVars, - expectedAppExitCode: expectedAppExitCode, - extraXHarnessArgs: "--js-file=runtime.js --engine=V8 -v trace", - appArgs: appArgs); - - private string RunWasmTestBrowser(string projectName, string bundleDir, IDictionary? envVars=null, int expectedAppExitCode=0, int xharnessExitCode=0, string? appArgs=null) - => RunWithXHarness("wasm test-browser", "wasm-test-browser", projectName, bundleDir, - envVars: envVars, - expectedAppExitCode: expectedAppExitCode, - extraXHarnessArgs: "-v trace", // needed to get messages like those for AOT loading - appArgs: appArgs); - - private static void InitProjectDir(string dir) - { - File.WriteAllText(Path.Combine(dir, "Directory.Build.props"), s_directoryBuildProps); - File.WriteAllText(Path.Combine(dir, "Directory.Build.targets"), s_directoryBuildTargets); - } - - private void BuildProject(string projectName, - string config, - string? extraBuildArgs = null, - string? extraProperties = null, - bool aot = false, - bool? dotnetWasmFromRuntimePack = null, - bool hasIcudt = true) - { - if (aot) - extraProperties = $"{extraProperties}\ntrue\n"; - - InitProjectDir(_tempDir); - - File.WriteAllText(Path.Combine(_tempDir, $"{projectName}.csproj"), -@$" - - {s_targetFramework} - Exe - true - runtime-test.js - {extraProperties ?? string.Empty} - -"); - - File.Copy(Path.Combine(AppContext.BaseDirectory, "runtime-test.js"), Path.Combine(_tempDir, "runtime-test.js")); - - StringBuilder sb = new(); - sb.Append("publish"); - sb.Append(s_defaultBuildArgs); - - sb.Append($" /p:Configuration={config}"); - - string logFilePath = Path.Combine(_logPath, $"{projectName}.binlog"); - _testOutput.WriteLine($"Binlog path: {logFilePath}"); - sb.Append($" /bl:\"{logFilePath}\" /v:minimal /nologo"); - if (extraBuildArgs != null) - sb.Append($" {extraBuildArgs} "); - - AssertBuild(sb.ToString()); - - string bundleDir = Path.Combine(GetBinDir(config: config), "AppBundle"); - AssertBasicAppBundle(bundleDir, projectName, config, hasIcudt); - - dotnetWasmFromRuntimePack ??= !aot; - AssertDotNetWasmJs(bundleDir, fromRuntimePack: dotnetWasmFromRuntimePack.Value); - } - - private static void AssertBasicAppBundle(string bundleDir, string projectName, string config, bool hasIcudt=true) - { - AssertFilesExist(bundleDir, new [] - { - "index.html", - "runtime.js", - "dotnet.timezones.blat", - "dotnet.wasm", - "mono-config.js", - "dotnet.js", - "run-v8.sh" - }); - - AssertFilesExist(bundleDir, new[] { "icudt.dat" }, expectToExist: hasIcudt); - - string managedDir = Path.Combine(bundleDir, "managed"); - AssertFilesExist(managedDir, new[] { $"{projectName}.dll" }); - - bool is_debug = config == "Debug"; - if (is_debug) - { - // Use cecil to check embedded pdb? - // AssertFilesExist(managedDir, new[] { $"{projectName}.pdb" }); - - //FIXME: um.. what about these? embedded? why is linker omitting them? - //foreach (string file in Directory.EnumerateFiles(managedDir, "*.dll")) - //{ - //string pdb = Path.ChangeExtension(file, ".pdb"); - //Assert.True(File.Exists(pdb), $"Could not find {pdb} for {file}"); - //} - } - } - - private void AssertDotNetWasmJs(string bundleDir, bool fromRuntimePack) - { - string nativeDir = GetRuntimeNativeDir(); - - AssertFile(Path.Combine(nativeDir, "dotnet.wasm"), Path.Combine(bundleDir, "dotnet.wasm"), "Expected dotnet.wasm to be same as the runtime pack", same: fromRuntimePack); - AssertFile(Path.Combine(nativeDir, "dotnet.js"), Path.Combine(bundleDir, "dotnet.js"), "Expected dotnet.js to be same as the runtime pack", same: fromRuntimePack); - } - - private static void AssertFilesDontExist(string dir, string[] filenames, string? label = null) - => AssertFilesExist(dir, filenames, label, expectToExist: false); - - private static void AssertFilesExist(string dir, string[] filenames, string? label = null, bool expectToExist=true) - { - Assert.True(Directory.Exists(dir), $"[{label}] {dir} not found"); - foreach (string filename in filenames) - { - string path = Path.Combine(dir, filename); - - if (expectToExist) - { - Assert.True(File.Exists(path), - label != null - ? $"{label}: {path} doesn't exist" - : $"{path} doesn't exist"); - } - else - { - Assert.False(File.Exists(path), - label != null - ? $"{label}: {path} should not exist" - : $"{path} should not exist"); - } - } - } - - private static void AssertSameFile(string file0, string file1, string? label=null) => AssertFile(file0, file1, label, same: true); - private static void AssertNotSameFile(string file0, string file1, string? label=null) => AssertFile(file0, file1, label, same: false); - - private static void AssertFile(string file0, string file1, string? label=null, bool same=true) - { - Assert.True(File.Exists(file0), $"{label}: Expected to find {file0}"); - Assert.True(File.Exists(file1), $"{label}: Expected to find {file1}"); - - FileInfo finfo0 = new(file0); - FileInfo finfo1 = new(file1); - - if (same) - Assert.True(finfo0.Length == finfo1.Length, $"{label}: File sizes don't match for {file0} ({finfo0.Length}), and {file1} ({finfo1.Length})"); - else - Assert.True(finfo0.Length != finfo1.Length, $"{label}: File sizes should not match for {file0} ({finfo0.Length}), and {file1} ({finfo1.Length})"); - } - - private void AssertBuild(string args) - { - (int exitCode, _) = RunProcess("dotnet", args, workingDir: _tempDir, label: "build"); - Assert.True(0 == exitCode, $"Build process exited with non-zero exit code: {exitCode}"); - } - - private string GetObjDir(string targetFramework=s_targetFramework, string? baseDir=null, string config="Debug") - => Path.Combine(baseDir ?? _tempDir, "obj", config, targetFramework, "browser-wasm", "wasm"); - - private string GetBinDir(string targetFramework=s_targetFramework, string? baseDir=null, string config="Debug") - => Path.Combine(baseDir ?? _tempDir, "bin", config, targetFramework, "browser-wasm"); - - private string GetRuntimePackDir() => s_runtimePackDir; - - private string GetRuntimeNativeDir() - => Path.Combine(GetRuntimePackDir(), "runtimes", "browser-wasm", "native"); - - public void Dispose() - { - if (s_skipProjectCleanup) - return; - - try - { - Directory.Delete(_tempDir, recursive: true); - } - catch - { - Console.Error.WriteLine($"Failed to delete '{_tempDir}' during test cleanup"); - } - } - - private (int, string) RunProcess(string path, - string args = "", - IDictionary? envVars = null, - string? workingDir = null, - string? label = null, - bool logToXUnit = true) - { - _testOutput.WriteLine($"Running: {path} {args}"); - StringBuilder outputBuilder = new (); - var processStartInfo = new ProcessStartInfo - { - FileName = path, - UseShellExecute = false, - CreateNoWindow = true, - RedirectStandardError = true, - RedirectStandardOutput = true, - Arguments = args, + [BuildAndRun(aot: true, host: RunHost.None, parameters: new object[] + { "", "error :.*emscripten.*required for AOT" })] + [BuildAndRun(aot: true, host: RunHost.None, parameters: new object[] + { "/non-existant/foo", "error.*\\(EMSDK_PATH\\)=/non-existant/foo.*required for AOT" })] + public void AOT_ErrorWhenMissingEMSDK(BuildArgs buildArgs, string emsdkPath, string errorPattern, string id) + { + string projectName = $"missing_emsdk"; + buildArgs = buildArgs with { + ProjectName = projectName, + ExtraBuildArgs = $"/p:EMSDK_PATH={emsdkPath}" }; + buildArgs = GetBuildArgsWith(buildArgs); - if (workingDir != null) - processStartInfo.WorkingDirectory = workingDir; + (_, string buildOutput) = BuildProject(buildArgs, + initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), + id: id, + expectSuccess: false); - if (envVars != null) - { - if (envVars.Count > 0) - _testOutput.WriteLine("Setting environment variables for execution:"); + Assert.Matches(errorPattern, buildOutput); + } - foreach (KeyValuePair envVar in envVars) + private static string s_bug49588_ProgramCS = @" + using System; + public class TestClass { + public static int Main() { - processStartInfo.EnvironmentVariables[envVar.Key] = envVar.Value; - _testOutput.WriteLine($"\t{envVar.Key} = {envVar.Value}"); + Console.WriteLine($""tc: {Environment.TickCount}, tc64: {Environment.TickCount64}""); + return 42; } - } - - Process? process = Process.Start(processStartInfo); - if (process == null) - throw new ArgumentException($"Process.Start({path} {args}) returned null process"); - - process.ErrorDataReceived += (sender, e) => LogData("[stderr]", e.Data); - process.OutputDataReceived += (sender, e) => LogData("[stdout]", e.Data); - - try - { - process.BeginOutputReadLine(); - process.BeginErrorReadLine(); - process.WaitForExit(); + }"; - return (process.ExitCode, outputBuilder.ToString().Trim('\r', '\n')); - } - catch - { - Console.WriteLine(outputBuilder.ToString()); - throw; - } + [Theory] + [MemberData(nameof(MainMethodTestData), parameters: new object[] { /*aot*/ true, RunHost.All })] + public void Bug49588_RegressionTest_AOT(BuildArgs buildArgs, RunHost host, string id) + => TestMain("bug49588_aot", s_bug49588_ProgramCS, buildArgs, host, id); - void LogData(string label, string? message) - { - if (logToXUnit && message != null) - { - _testOutput.WriteLine($"{label} {message}"); - } - outputBuilder.AppendLine($"{label} {message}"); - } + [Theory] + [MemberData(nameof(MainMethodTestData), parameters: new object[] { /*aot*/ false, RunHost.All })] + public void Bug49588_RegressionTest_NativeRelinking(BuildArgs buildArgs, RunHost host, string id) + => TestMain("bug49588_native_relinking", s_bug49588_ProgramCS, buildArgs, host, id, + extraProperties: "true", + dotnetWasmFromRuntimePack: false); + + void TestMain(string projectName, + string programText, + BuildArgs buildArgs, + RunHost host, + string id, + string? extraProperties = null, + bool? dotnetWasmFromRuntimePack = null) + { + buildArgs = buildArgs with { ProjectName = projectName }; + buildArgs = GetBuildArgsWith(buildArgs, extraProperties); + + if (dotnetWasmFromRuntimePack == null) + dotnetWasmFromRuntimePack = !(buildArgs.AOT || buildArgs.Config == "Release"); + + BuildProject(buildArgs, + initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText), + id: id, + dotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack); + + RunAndTestWasmApp(buildArgs, expectedExitCode: 42, + test: output => Assert.Contains("Hello, World!", output), host: host, id: id); } - - private static string s_directoryBuildProps = @" - - <_WasmTargetsDir Condition=""'$(RuntimeSrcDir)' != ''"">$(RuntimeSrcDir)\src\mono\wasm\build\ - <_WasmTargetsDir Condition=""'$(WasmBuildSupportDir)' != ''"">$(WasmBuildSupportDir)\wasm\ - $(WasmBuildSupportDir)\emsdk\ - - - - - - - PrepareForWasmBuild;$(WasmBuildAppDependsOn) - -"; - - private static string s_directoryBuildTargets = @" - - - - - - - - - - - - - - - - - - -"; - } } From c7ab19e06340a92d89c5fac26aeb4fd0eec3eadc Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 29 Mar 2021 16:06:30 -0400 Subject: [PATCH 22/98] Remove some delegate/closures from X509Pal (#50376) --- .../Pal.Windows/Native/Helpers.cs | 17 ++-- .../Pal.Windows/X509Pal.CustomExtensions.cs | 82 +++++++------------ .../Pal.Windows/X509Pal.PublicKey.cs | 32 ++------ 3 files changed, 47 insertions(+), 84 deletions(-) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/Native/Helpers.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/Native/Helpers.cs index 5a7b3ea8ef3e..afec7590d3e9 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/Native/Helpers.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/Native/Helpers.cs @@ -2,10 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Text; using System.Diagnostics; -using System.Security.Cryptography; using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Text; namespace Internal.Cryptography.Pal.Native { @@ -73,11 +73,12 @@ public static byte[] ValueAsAscii(this Oid oid) } public unsafe delegate void DecodedObjectReceiver(void* pvDecodedObject, int cbDecodedObject); + public unsafe delegate TResult DecodedObjectReceiver(void* pvDecodedObject, int cbDecodedObject); - public static void DecodeObject( + public static TResult DecodeObject( this byte[] encoded, CryptDecodeObjectStructType lpszStructType, - DecodedObjectReceiver receiver) + DecodedObjectReceiver receiver) { unsafe { @@ -109,14 +110,14 @@ public static void DecodeObject( throw Marshal.GetLastWin32Error().ToCryptographicException(); } - receiver(decoded, cb); + return receiver(decoded, cb); } } - public static void DecodeObject( + public static TResult DecodeObject( this byte[] encoded, string lpszStructType, - DecodedObjectReceiver receiver) + DecodedObjectReceiver receiver) { unsafe { @@ -148,7 +149,7 @@ public static void DecodeObject( throw Marshal.GetLastWin32Error().ToCryptographicException(); } - receiver(decoded, cb); + return receiver(decoded, cb); } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/X509Pal.CustomExtensions.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/X509Pal.CustomExtensions.cs index 57883ba6d909..f7f1c5a5ebb2 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/X509Pal.CustomExtensions.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/X509Pal.CustomExtensions.cs @@ -41,14 +41,12 @@ public void DecodeX509KeyUsageExtension(byte[] encoded, out X509KeyUsageFlags ke { unsafe { - uint keyUsagesAsUint = 0; - encoded.DecodeObject( + uint keyUsagesAsUint = encoded.DecodeObject( CryptDecodeObjectStructType.X509_KEY_USAGE, - delegate (void* pvDecoded, int cbDecoded) + static delegate (void* pvDecoded, int cbDecoded) { Debug.Assert(cbDecoded >= sizeof(CRYPT_BIT_BLOB)); CRYPT_BIT_BLOB* pBlob = (CRYPT_BIT_BLOB*)pvDecoded; - keyUsagesAsUint = 0; byte* pbData = pBlob->pbData; if (pbData != null) @@ -58,13 +56,13 @@ public void DecodeX509KeyUsageExtension(byte[] encoded, out X509KeyUsageFlags ke switch (pBlob->cbData) { case 1: - keyUsagesAsUint = *pbData; - break; + return *pbData; case 2: - keyUsagesAsUint = *(ushort*)(pbData); - break; + return *(ushort*)(pbData); } } + + return 0u; } ); keyUsages = (X509KeyUsageFlags)keyUsagesAsUint; @@ -95,25 +93,16 @@ public void DecodeX509BasicConstraintsExtension(byte[] encoded, out bool certifi { unsafe { - bool localCertificateAuthority = false; - bool localHasPathLengthConstraint = false; - int localPathLengthConstraint = 0; - - encoded.DecodeObject( + (certificateAuthority, hasPathLengthConstraint, pathLengthConstraint) = encoded.DecodeObject( CryptDecodeObjectStructType.X509_BASIC_CONSTRAINTS, - delegate (void* pvDecoded, int cbDecoded) + static delegate (void* pvDecoded, int cbDecoded) { Debug.Assert(cbDecoded >= sizeof(CERT_BASIC_CONSTRAINTS_INFO)); CERT_BASIC_CONSTRAINTS_INFO* pBasicConstraints = (CERT_BASIC_CONSTRAINTS_INFO*)pvDecoded; - localCertificateAuthority = (pBasicConstraints->SubjectType.pbData[0] & CERT_BASIC_CONSTRAINTS_INFO.CERT_CA_SUBJECT_FLAG) != 0; - localHasPathLengthConstraint = pBasicConstraints->fPathLenConstraint != 0; - localPathLengthConstraint = pBasicConstraints->dwPathLenConstraint; - } - ); - - certificateAuthority = localCertificateAuthority; - hasPathLengthConstraint = localHasPathLengthConstraint; - pathLengthConstraint = localPathLengthConstraint; + return ((pBasicConstraints->SubjectType.pbData[0] & CERT_BASIC_CONSTRAINTS_INFO.CERT_CA_SUBJECT_FLAG) != 0, + pBasicConstraints->fPathLenConstraint != 0, + pBasicConstraints->dwPathLenConstraint); + }); } } @@ -121,25 +110,16 @@ public void DecodeX509BasicConstraints2Extension(byte[] encoded, out bool certif { unsafe { - bool localCertificateAuthority = false; - bool localHasPathLengthConstraint = false; - int localPathLengthConstraint = 0; - - encoded.DecodeObject( + (certificateAuthority, hasPathLengthConstraint, pathLengthConstraint) = encoded.DecodeObject( CryptDecodeObjectStructType.X509_BASIC_CONSTRAINTS2, - delegate (void* pvDecoded, int cbDecoded) + static delegate (void* pvDecoded, int cbDecoded) { Debug.Assert(cbDecoded >= sizeof(CERT_BASIC_CONSTRAINTS2_INFO)); CERT_BASIC_CONSTRAINTS2_INFO* pBasicConstraints2 = (CERT_BASIC_CONSTRAINTS2_INFO*)pvDecoded; - localCertificateAuthority = pBasicConstraints2->fCA != 0; - localHasPathLengthConstraint = pBasicConstraints2->fPathLenConstraint != 0; - localPathLengthConstraint = pBasicConstraints2->dwPathLenConstraint; - } - ); - - certificateAuthority = localCertificateAuthority; - hasPathLengthConstraint = localHasPathLengthConstraint; - pathLengthConstraint = localPathLengthConstraint; + return (pBasicConstraints2->fCA != 0, + pBasicConstraints2->fPathLenConstraint != 0, + pBasicConstraints2->dwPathLenConstraint); + }); } } @@ -163,14 +143,14 @@ public byte[] EncodeX509EnhancedKeyUsageExtension(OidCollection usages) public void DecodeX509EnhancedKeyUsageExtension(byte[] encoded, out OidCollection usages) { - OidCollection localUsages = new OidCollection(); - unsafe { - encoded.DecodeObject( + usages = encoded.DecodeObject( CryptDecodeObjectStructType.X509_ENHANCED_KEY_USAGE, - delegate (void* pvDecoded, int cbDecoded) + static delegate (void* pvDecoded, int cbDecoded) { + var localUsages = new OidCollection(); + Debug.Assert(cbDecoded >= sizeof(CERT_ENHKEY_USAGE)); CERT_ENHKEY_USAGE* pEnhKeyUsage = (CERT_ENHKEY_USAGE*)pvDecoded; int count = pEnhKeyUsage->cUsageIdentifier; @@ -181,11 +161,10 @@ public void DecodeX509EnhancedKeyUsageExtension(byte[] encoded, out OidCollectio Oid oid = new Oid(oidValue); localUsages.Add(oid); } - } - ); - } - usages = localUsages; + return localUsages; + }); + } } public byte[] EncodeX509SubjectKeyIdentifierExtension(ReadOnlySpan subjectKeyIdentifier) @@ -204,17 +183,14 @@ public void DecodeX509SubjectKeyIdentifierExtension(byte[] encoded, out byte[] s { unsafe { - byte[] localSubjectKeyIdentifier = null!; - encoded.DecodeObject( + subjectKeyIdentifier = encoded.DecodeObject( Oids.SubjectKeyIdentifier, - delegate (void* pvDecoded, int cbDecoded) + static delegate (void* pvDecoded, int cbDecoded) { Debug.Assert(cbDecoded >= sizeof(CRYPTOAPI_BLOB)); CRYPTOAPI_BLOB* pBlob = (CRYPTOAPI_BLOB*)pvDecoded; - localSubjectKeyIdentifier = pBlob->ToByteArray(); - } - ); - subjectKeyIdentifier = localSubjectKeyIdentifier; + return pBlob->ToByteArray(); + }); } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/X509Pal.PublicKey.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/X509Pal.PublicKey.cs index dd11ea04d083..19bce7dfa75f 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/X509Pal.PublicKey.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/X509Pal.PublicKey.cs @@ -283,46 +283,32 @@ private static byte[] ConstructDSSPublicKeyCspBlob(byte[] encodedKeyValue, byte[ { unsafe { - byte[]? decodedKeyValue = null; - - encodedKeyValue.DecodeObject( + return encodedKeyValue.DecodeObject( CryptDecodeObjectStructType.X509_DSS_PUBLICKEY, - delegate (void* pvDecoded, int cbDecoded) + static delegate (void* pvDecoded, int cbDecoded) { Debug.Assert(cbDecoded >= sizeof(CRYPTOAPI_BLOB)); CRYPTOAPI_BLOB* pBlob = (CRYPTOAPI_BLOB*)pvDecoded; - decodedKeyValue = pBlob->ToByteArray(); - } - ); - - return decodedKeyValue; + return pBlob->ToByteArray(); + }); } } private static void DecodeDssParameters(byte[] encodedParameters, out byte[] p, out byte[] q, out byte[] g) { - byte[] pLocal = null!; - byte[] qLocal = null!; - byte[] gLocal = null!; - unsafe { - encodedParameters.DecodeObject( + (p, q, g) = encodedParameters.DecodeObject( CryptDecodeObjectStructType.X509_DSS_PARAMETERS, delegate (void* pvDecoded, int cbDecoded) { Debug.Assert(cbDecoded >= sizeof(CERT_DSS_PARAMETERS)); CERT_DSS_PARAMETERS* pCertDssParameters = (CERT_DSS_PARAMETERS*)pvDecoded; - pLocal = pCertDssParameters->p.ToByteArray(); - qLocal = pCertDssParameters->q.ToByteArray(); - gLocal = pCertDssParameters->g.ToByteArray(); - } - ); + return (pCertDssParameters->p.ToByteArray(), + pCertDssParameters->q.ToByteArray(), + pCertDssParameters->g.ToByteArray()); + }); } - - p = pLocal; - q = qLocal; - g = gLocal; } private static bool HasExplicitParameters(SafeBCryptKeyHandle bcryptHandle) From 0124dbc311a7e6f6b9cf7111683380b81b9027d9 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 29 Mar 2021 16:11:13 -0400 Subject: [PATCH 23/98] Fix unintended closure in RsaPaddingProcessor.OpenProcessor (#50377) --- .../src/System/Security/Cryptography/RSACng.SignVerify.cs | 4 ++-- .../src/System/Security/Cryptography/RsaPaddingProcessor.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/Common/src/System/Security/Cryptography/RSACng.SignVerify.cs b/src/libraries/Common/src/System/Security/Cryptography/RSACng.SignVerify.cs index 7eaf3f9e9a6d..82709832cd88 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/RSACng.SignVerify.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/RSACng.SignVerify.cs @@ -32,9 +32,9 @@ private static int GetHashSizeInBytes(HashAlgorithmName hashAlgorithm) { return s_hashSizes.GetOrAdd( hashAlgorithm, - alg => + static hashAlgorithm => { - using (HashProviderCng hashProvider = new HashProviderCng(alg.Name!, null)) + using (HashProviderCng hashProvider = new HashProviderCng(hashAlgorithm.Name!, null)) { return hashProvider.HashSizeInBytes; } diff --git a/src/libraries/Common/src/System/Security/Cryptography/RsaPaddingProcessor.cs b/src/libraries/Common/src/System/Security/Cryptography/RsaPaddingProcessor.cs index 8ee516f1cf5a..9538db141432 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/RsaPaddingProcessor.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/RsaPaddingProcessor.cs @@ -74,7 +74,7 @@ internal static RsaPaddingProcessor OpenProcessor(HashAlgorithmName hashAlgorith { return s_lookup.GetOrAdd( hashAlgorithmName, - alg => + static hashAlgorithmName => { using (IncrementalHash hasher = IncrementalHash.CreateHash(hashAlgorithmName)) { From 98ace7d4837fcd81c1f040b1f67e63e9e1973e13 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 29 Mar 2021 13:14:51 -0700 Subject: [PATCH 24/98] Set IsTrimmable on CompilerServices.Unsafe (#50370) Fixes https://github.com/dotnet/runtime/issues/50354 --- .../src/System.Runtime.CompilerServices.Unsafe.il | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.il b/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.il index c746a05cfab9..f7d5718f456a 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.il +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.il @@ -28,6 +28,10 @@ 01 00 0b 53 65 72 76 69 63 65 61 62 6c 65 04 54 72 75 65 00 00 ) // "Serviceable", "True" + .custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyMetadataAttribute::.ctor(string, string) = ( + 01 00 0b 49 73 54 72 69 6d 6d 61 62 6c 65 04 54 + 72 75 65 00 00 + ) // "IsTrimmable", "True" .custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyCopyrightAttribute::.ctor(string) = ( 01 00 2F C2 A9 20 4D 69 63 72 6F 73 6F 66 74 20 // ../.. Microsoft 43 6F 72 70 6F 72 61 74 69 6F 6E 2E 20 20 41 6C // Corporation. Al 6C 20 72 69 67 68 74 73 20 72 65 73 65 72 76 65 // l rights reserve From d2613848efd92dfaad21499a8de81916346a3b60 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Mon, 29 Mar 2021 13:58:50 -0700 Subject: [PATCH 25/98] Clean up REGUTIL/CLRConfig system (#50314) * Convert all configuration options from REGUTIL to CLRConfig. * Remove uses of REGUTIL outside of CLRConfig impl. --- src/coreclr/debug/daccess/daccess.cpp | 2 +- .../debug/di/eventredirectionpipeline.cpp | 16 +- .../debug/di/eventredirectionpipeline.h | 8 +- src/coreclr/debug/di/rsmain.cpp | 10 +- src/coreclr/debug/ee/debugger.cpp | 5 +- src/coreclr/debug/ee/debugger.h | 10 -- src/coreclr/debug/inc/dbgtransportsession.h | 4 +- src/coreclr/inc/clrconfig.h | 23 +-- src/coreclr/inc/clrconfigvalues.h | 117 ++++++------- src/coreclr/inc/stresslog.h | 2 +- src/coreclr/inc/utilcode.h | 57 ------- src/coreclr/md/debug_metadata.h | 2 +- src/coreclr/md/enc/mdinternalrw.cpp | 4 +- src/coreclr/md/inc/metamodel.h | 2 +- src/coreclr/utilcode/clrconfig.cpp | 72 +++----- src/coreclr/utilcode/configuration.cpp | 4 +- src/coreclr/utilcode/log.cpp | 20 +-- src/coreclr/utilcode/stresslog.cpp | 2 +- src/coreclr/utilcode/util.cpp | 16 -- src/coreclr/vm/appdomain.cpp | 2 +- src/coreclr/vm/ceemain.cpp | 12 +- src/coreclr/vm/commodule.cpp | 2 +- src/coreclr/vm/comthreadpool.cpp | 2 +- src/coreclr/vm/eeconfig.cpp | 155 ++++-------------- src/coreclr/vm/eeconfig.h | 49 ------ src/coreclr/vm/i386/cgenx86.cpp | 4 +- src/coreclr/vm/interoputil.cpp | 3 +- src/coreclr/vm/methodtablebuilder.cpp | 2 +- src/coreclr/vm/stackingallocator.cpp | 13 -- src/coreclr/vm/stublink.cpp | 2 +- src/coreclr/zap/zaplog.h | 2 +- src/coreclr/zap/zapper.cpp | 2 +- 32 files changed, 165 insertions(+), 461 deletions(-) diff --git a/src/coreclr/debug/daccess/daccess.cpp b/src/coreclr/debug/daccess/daccess.cpp index 424cff1b9148..71e35abec93e 100644 --- a/src/coreclr/debug/daccess/daccess.cpp +++ b/src/coreclr/debug/daccess/daccess.cpp @@ -7084,7 +7084,7 @@ HRESULT ClrDataAccess::VerifyDlls() // Note that we check this knob every time because it may be handy to turn it on in // the environment mid-flight. DWORD dwAssertDefault = m_fEnableDllVerificationAsserts ? 1 : 0; - if (REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_DbgDACAssertOnMismatch, dwAssertDefault)) + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgDACAssertOnMismatch, dwAssertDefault)) { // Output a nice error message that contains the timestamps in string format. time_t actualTime = timestamp; diff --git a/src/coreclr/debug/di/eventredirectionpipeline.cpp b/src/coreclr/debug/di/eventredirectionpipeline.cpp index 2332ec25e39c..382319dc8fe8 100644 --- a/src/coreclr/debug/di/eventredirectionpipeline.cpp +++ b/src/coreclr/debug/di/eventredirectionpipeline.cpp @@ -58,10 +58,10 @@ void EventRedirectionPipeline::Delete() void EventRedirectionPipeline::InitConfiguration() { // We need some config strings. See header for possible values. - m_DebuggerCmd.Init_DontUse_(CLRConfig::EXTERNAL_DbgRedirectApplication); - m_AttachParams.Init_DontUse_(CLRConfig::EXTERNAL_DbgRedirectAttachCmd); - m_CreateParams.Init_DontUse_(CLRConfig::EXTERNAL_DbgRedirectCreateCmd); - m_CommonParams.Init_DontUse_(CLRConfig::EXTERNAL_DbgRedirectCommonCmd); + m_DebuggerCmd = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_DbgRedirectApplication); + m_AttachParams = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_DbgRedirectAttachCmd); + m_CreateParams = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_DbgRedirectCreateCmd); + m_CommonParams = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_DbgRedirectCommonCmd); } @@ -108,11 +108,11 @@ HRESULT EventRedirectionPipeline::AttachDebuggerToTarget(LPCWSTR szOptions, DWOR // Initialize m_pBlock->m_versionCookie = EVENT_REDIRECTION_CURRENT_VERSION; - s.Printf(m_CommonParams.Value(), GetCurrentProcessId(), m_pBlock, szOptions, pidTarget); + s.Printf(m_CommonParams, GetCurrentProcessId(), m_pBlock, szOptions, pidTarget); lpCommandLine = s.GetUnicode(); - lpApplicationName = m_DebuggerCmd.Value(); // eg, something like L"c:\\debuggers_amd64\\windbg.exe"; + lpApplicationName = m_DebuggerCmd; // eg, something like L"c:\\debuggers_amd64\\windbg.exe"; // Initialize events. const BOOL kManualResetEvent = TRUE; @@ -283,7 +283,7 @@ HRESULT EventRedirectionPipeline::CreateProcessUnderDebugger( } // Attach the real debugger. - AttachDebuggerToTarget(m_CreateParams.Value(), lpProcessInformation->dwProcessId); + AttachDebuggerToTarget(m_CreateParams, lpProcessInformation->dwProcessId); m_dwProcessId = lpProcessInformation->dwProcessId; @@ -298,7 +298,7 @@ HRESULT EventRedirectionPipeline::DebugActiveProcess(MachineInfo machineInfo, co // Use redirected pipeline // Spin up debugger to attach to target. - return AttachDebuggerToTarget(m_AttachParams.Value(), processDescriptor.m_Pid); + return AttachDebuggerToTarget(m_AttachParams, processDescriptor.m_Pid); } // Detach diff --git a/src/coreclr/debug/di/eventredirectionpipeline.h b/src/coreclr/debug/di/eventredirectionpipeline.h index e219c182980a..a8a0aeed8067 100644 --- a/src/coreclr/debug/di/eventredirectionpipeline.h +++ b/src/coreclr/debug/di/eventredirectionpipeline.h @@ -102,7 +102,7 @@ class EventRedirectionPipeline : // The debugger application to launch. eg: // c:\debuggers_amd64\windbg.exe - ConfigStringHolder m_DebuggerCmd; + CLRConfigStringHolder m_DebuggerCmd; // The common format string for the command line. // This will get the following printf args: @@ -113,7 +113,7 @@ class EventRedirectionPipeline : // target debuggee (%d or %x): pid of the debuggee. // eg (for windbg): // -c ".load C:\vbl\ClrDbg\ndp\clr\src\Tools\strikeRS\objc\amd64\strikeRS.dll; !watch %x %p" %s -p %d - ConfigStringHolder m_CommonParams; + CLRConfigStringHolder m_CommonParams; // Command parameters for create case. // Note that we must always physically call CreateProcess on the debuggee so that we get the proper out-parameters @@ -128,12 +128,12 @@ class EventRedirectionPipeline : // to not create the break-in thread (which it can't do on a pre-initialized process). // eg: // "-WX -pb -pr" - ConfigStringHolder m_CreateParams; + CLRConfigStringHolder m_CreateParams; // command parameters for attach. The WFDE server will send a loader breakpoint. // eg: // "-WX" - ConfigStringHolder m_AttachParams; + CLRConfigStringHolder m_AttachParams; DWORD m_dwProcessId; }; diff --git a/src/coreclr/debug/di/rsmain.cpp b/src/coreclr/debug/di/rsmain.cpp index 779a6eac56d2..ff49f2186a40 100644 --- a/src/coreclr/debug/di/rsmain.cpp +++ b/src/coreclr/debug/di/rsmain.cpp @@ -480,15 +480,15 @@ void CordbCommonBase::InitializeCommon() // StressLog will turn on stress logging for the entire runtime. // RSStressLog is only used here and only effects just the RS. fStressLog = - (REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_StressLog, fStressLog) != 0) || + (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_StressLog, fStressLog) != 0) || (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_RSStressLog) != 0); if (fStressLog == true) { - unsigned facilities = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_LogFacility, LF_ALL); - unsigned level = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::EXTERNAL_LogLevel, LL_INFO1000); - unsigned bytesPerThread = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_StressLogSize, STRESSLOG_CHUNK_SIZE * 2); - unsigned totalBytes = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_TotalStressLogSize, STRESSLOG_CHUNK_SIZE * 1024); + unsigned facilities = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_LogFacility, LF_ALL); + unsigned level = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_LogLevel, LL_INFO1000); + unsigned bytesPerThread = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_StressLogSize, STRESSLOG_CHUNK_SIZE * 2); + unsigned totalBytes = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TotalStressLogSize, STRESSLOG_CHUNK_SIZE * 1024); StressLog::Initialize(facilities, level, bytesPerThread, totalBytes, GetClrModuleBase()); } } diff --git a/src/coreclr/debug/ee/debugger.cpp b/src/coreclr/debug/ee/debugger.cpp index d64bb94e905a..d8791362ca72 100644 --- a/src/coreclr/debug/ee/debugger.cpp +++ b/src/coreclr/debug/ee/debugger.cpp @@ -1048,11 +1048,11 @@ void Debugger::InitDebugEventCounting() memset(&g_iDbgDebuggerCounter, 0, DBG_DEBUGGER_MAX*sizeof(int)); // retrieve the possible counter for break point - LPWSTR wstrValue = NULL; + CLRConfigStringHolder wstrValue = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DebuggerBreakPoint); // The string value is of the following format // =Count;=Count;....; // The string must end with ; - if ((wstrValue = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DebuggerBreakPoint)) != NULL) + if (wstrValue != NULL) { LPSTR strValue; int cbReq; @@ -1108,7 +1108,6 @@ void Debugger::InitDebugEventCounting() // free the ansi buffer delete [] strValue; - REGUTIL::FreeConfigString(wstrValue); } #endif // _DEBUG } diff --git a/src/coreclr/debug/ee/debugger.h b/src/coreclr/debug/ee/debugger.h index 52ed73615ba4..374f4ded1e65 100644 --- a/src/coreclr/debug/ee/debugger.h +++ b/src/coreclr/debug/ee/debugger.h @@ -326,16 +326,6 @@ class GCHolderEEInterface #define GCX_PREEMP_EEINTERFACE_TOGGLE_IFTHREAD_COND(cond) \ GCHolderEEInterface __gcCoop_onlyOneAllowedPerScope((cond)) - - -// There are still some APIs that call new that we call from the helper thread. -// These are unsafe operations, so we wrap them here. Each of these is a potential hang. -inline DWORD UnsafeGetConfigDWORD_DontUse_(LPCWSTR name, DWORD defValue) -{ - SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE; - return REGUTIL::GetConfigDWORD_DontUse_(name, defValue); -} - inline DWORD UnsafeGetConfigDWORD(const CLRConfig::ConfigDWORDInfo & info) { SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE; diff --git a/src/coreclr/debug/inc/dbgtransportsession.h b/src/coreclr/debug/inc/dbgtransportsession.h index f90f4db12ca7..5c305b178187 100644 --- a/src/coreclr/debug/inc/dbgtransportsession.h +++ b/src/coreclr/debug/inc/dbgtransportsession.h @@ -145,8 +145,8 @@ inline void DbgTransportLog(DbgTransportLogClass eClass, const char *szFormat, . if (s_dwLoggingEnabled == LE_Unknown) { - s_dwLoggingEnabled = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_DbgTransportLog, LE_None); - s_dwLoggingClass = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_DbgTransportLogClass, LC_All); + s_dwLoggingEnabled = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgTransportLog); + s_dwLoggingClass = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgTransportLogClass); } if ((s_dwLoggingEnabled & DBG_TRANSPORT_LOG_THIS_SIDE) && diff --git a/src/coreclr/inc/clrconfig.h b/src/coreclr/inc/clrconfig.h index bd5e41cdbc0a..715a5ada9f24 100644 --- a/src/coreclr/inc/clrconfig.h +++ b/src/coreclr/inc/clrconfig.h @@ -82,14 +82,6 @@ class CLRConfig #define RETAIL_CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions) \ static const ConfigStringInfo symbol; - // TEMPORARY macros that declare strings. These are used for config value accesses that haven't been - // moved over to CLRConfig yet. Once all accesses have been moved, these macros (and corresponding - // instantiations in file:../utilcode/CLRConfig.cpp) should be removed. - #define RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(symbol, name, description) \ - static const LPCWSTR symbol; - #define RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS(symbol, name, description) \ - static const LPCWSTR symbol; - // // Debug versions of the macros // @@ -102,17 +94,11 @@ class CLRConfig static const ConfigStringInfo symbol; #define CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions) \ static const ConfigStringInfo symbol; - #define CONFIG_DWORD_INFO_DIRECT_ACCESS(symbol, name, description) \ - static const LPCWSTR symbol; - #define CONFIG_STRING_INFO_DIRECT_ACCESS(symbol, name, description) \ - static const LPCWSTR symbol; #else #define CONFIG_DWORD_INFO(symbol, name, defaultValue, description) #define CONFIG_DWORD_INFO_EX(symbol, name, defaultValue, description, lookupOptions) #define CONFIG_STRING_INFO(symbol, name, description) #define CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions) - #define CONFIG_DWORD_INFO_DIRECT_ACCESS(symbol, name, description) - #define CONFIG_STRING_INFO_DIRECT_ACCESS(symbol, name, description) #endif // _DEBUG // Now that we have defined what what the macros in file:CLRConfigValues.h mean, include it to generate the code. #include "clrconfigvalues.h" @@ -121,14 +107,10 @@ class CLRConfig #undef RETAIL_CONFIG_STRING_INFO #undef RETAIL_CONFIG_DWORD_INFO_EX #undef RETAIL_CONFIG_STRING_INFO_EX - #undef RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS - #undef RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS #undef CONFIG_DWORD_INFO #undef CONFIG_STRING_INFO #undef CONFIG_DWORD_INFO_EX #undef CONFIG_STRING_INFO_EX - #undef CONFIG_DWORD_INFO_DIRECT_ACCESS - #undef CONFIG_STRING_INFO_DIRECT_ACCESS // // Methods to do config value (DWORD and String) lookups. @@ -139,7 +121,10 @@ class CLRConfig static DWORD GetConfigValue(const ConfigDWORDInfo & info); // Look up a DWORD config value. - static DWORD GetConfigValue(const ConfigDWORDInfo & info, bool acceptExplicitDefaultFromRegutil, /* [Out] */ bool *isDefault); + static DWORD GetConfigValue(const ConfigDWORDInfo & info, /* [Out] */ bool *isDefault); + + // Look up a DWORD config value. + static DWORD GetConfigValue(const ConfigDWORDInfo & info, DWORD defaultValue); // Look up a string config value. // You own the string that's returned. diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 6051c2aabd55..a3acc7ed03a1 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -9,7 +9,7 @@ // registry and config file. // // Given any config knob below that looks like this example: -// RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_LogEnable, W("LogEnable"), "Turns on the traditional CLR log.") +// RETAIL_CONFIG_DWORD_INFO(INTERNAL_LogEnable, W("LogEnable"), 0, "Turns on the traditional CLR log.") // --------- // | // -------------------- @@ -117,7 +117,7 @@ CONFIG_DWORD_INFO(INTERNAL_ADForceSB, W("ADForceSB"), 0, "Forces sync block crea CONFIG_DWORD_INFO(INTERNAL_ADLogMemory, W("ADLogMemory"), 0, "Superseded by test hooks") CONFIG_DWORD_INFO(INTERNAL_ADTakeDHSnapShot, W("ADTakeDHSnapShot"), 0, "Superseded by test hooks") CONFIG_DWORD_INFO(INTERNAL_ADTakeSnapShot, W("ADTakeSnapShot"), 0, "Superseded by test hooks") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_EnableFullDebug, W("EnableFullDebug"), "Heavy-weight checking for AD boundary violations (AD leaks)") +CONFIG_DWORD_INFO(INTERNAL_EnableFullDebug, W("EnableFullDebug"), 0, "Heavy-weight checking for AD boundary violations (AD leaks)") /// /// Jit Pitching @@ -176,7 +176,8 @@ CONFIG_DWORD_INFO_EX(INTERNAL_DbgBreakOnRawInt3, W("DbgBreakOnRawInt3"), 0, "All CONFIG_DWORD_INFO_EX(INTERNAL_DbgBreakOnSendBreakpoint, W("DbgBreakOnSendBreakpoint"), 0, "Allows an assert when sending a breakpoint to the right side", CLRConfig::EEConfig_default) CONFIG_DWORD_INFO_EX(INTERNAL_DbgBreakOnSetIP, W("DbgBreakOnSetIP"), 0, "Allows an assert when setting the IP", CLRConfig::EEConfig_default) CONFIG_DWORD_INFO_EX(INTERNAL_DbgCheckInt3, W("DbgCheckInt3"), 0, "Asserts if the debugger explicitly writes int3 instead of calling SetUnmanagedBreakpoint", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_DbgDACAssertOnMismatch, W("DbgDACAssertOnMismatch"), "Allows an assert when the mscordacwks and mscorwks dll versions don't match") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_DbgForcePDBSymbols, W("DbgForcePDBSymbols"), 0, "") +CONFIG_DWORD_INFO(INTERNAL_DbgDACAssertOnMismatch, W("DbgDACAssertOnMismatch"), 0, "Allows an assert when the mscordacwks and mscorwks dll versions don't match") CONFIG_DWORD_INFO_EX(INTERNAL_DbgDACEnableAssert, W("DbgDACEnableAssert"), 0, "Enables extra validity checking in DAC - assumes target isn't corrupt", CLRConfig::EEConfig_default) RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_DbgDACSkipVerifyDlls, W("DbgDACSkipVerifyDlls"), 0, "Allows disabling the check to ensure mscordacwks and mscorwks dll versions match", CLRConfig::EEConfig_default) CONFIG_DWORD_INFO_EX(INTERNAL_DbgDelayHelper, W("DbgDelayHelper"), 0, "Varies the wait in the helper thread startup for testing race between threads", CLRConfig::EEConfig_default) @@ -195,21 +196,20 @@ CONFIG_DWORD_INFO_EX(INTERNAL_DbgNoDebugger, W("DbgNoDebugger"), 0, "Allows brea RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_DbgNoForceContinue, W("DbgNoForceContinue"), 1, "Used to force a continue on longhorn", CLRConfig::EEConfig_default) CONFIG_DWORD_INFO_EX(INTERNAL_DbgNoOpenMDByFile, W("DbgNoOpenMDByFile"), 0, "Allows opening MD by memory for perf testing", CLRConfig::EEConfig_default) CONFIG_DWORD_INFO(INTERNAL_DbgOOBinFEEE, W("DbgOOBinFEEE"), 0, "Allows forcing oob breakpoints when a fatal error occurs") -RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS(EXTERNAL_DbgPackShimPath, W("DbgPackShimPath"), "CoreCLR path to dbgshim.dll - we are trying to figure out if we can remove this") CONFIG_DWORD_INFO_EX(INTERNAL_DbgPingInterop, W("DbgPingInterop"), 0, "Allows checking for deadlocks in interop debugging", CLRConfig::EEConfig_default) CONFIG_DWORD_INFO_EX(INTERNAL_DbgRace, W("DbgRace"), 0, "Allows pausing for native debug events to get hijicked", CLRConfig::EEConfig_default) RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_DbgRedirect, W("DbgRedirect"), 0, "Allows for redirecting the event pipeline", CLRConfig::EEConfig_default) -RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS(EXTERNAL_DbgRedirectApplication, W("DbgRedirectApplication"), "Specifies the auxiliary debugger application to launch.") -RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS(EXTERNAL_DbgRedirectAttachCmd, W("DbgRedirectAttachCmd"), "Specifies command parameters for attaching the auxiliary debugger.") -RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS(EXTERNAL_DbgRedirectCommonCmd, W("DbgRedirectCommonCmd"), "Specifies a command line format string for the auxiliary debugger.") -RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS(EXTERNAL_DbgRedirectCreateCmd, W("DbgRedirectCreateCmd"), "Specifies command parameters when creating the auxiliary debugger.") +RETAIL_CONFIG_STRING_INFO(EXTERNAL_DbgRedirectApplication, W("DbgRedirectApplication"), "Specifies the auxiliary debugger application to launch.") +RETAIL_CONFIG_STRING_INFO(EXTERNAL_DbgRedirectAttachCmd, W("DbgRedirectAttachCmd"), "Specifies command parameters for attaching the auxiliary debugger.") +RETAIL_CONFIG_STRING_INFO(EXTERNAL_DbgRedirectCommonCmd, W("DbgRedirectCommonCmd"), "Specifies a command line format string for the auxiliary debugger.") +RETAIL_CONFIG_STRING_INFO(EXTERNAL_DbgRedirectCreateCmd, W("DbgRedirectCreateCmd"), "Specifies command parameters when creating the auxiliary debugger.") CONFIG_DWORD_INFO_EX(INTERNAL_DbgShortcutCanary, W("DbgShortcutCanary"), 0, "Allows a way to force canary to fail to be able to test failure paths", CLRConfig::EEConfig_default) CONFIG_DWORD_INFO_EX(INTERNAL_DbgSkipMEOnStep, W("DbgSkipMEOnStep"), 0, "Turns off MethodEnter checks", CLRConfig::EEConfig_default) CONFIG_DWORD_INFO_EX(INTERNAL_DbgSkipVerCheck, W("DbgSkipVerCheck"), 0, "Allows different RS and LS versions (for servicing work)", CLRConfig::EEConfig_default) CONFIG_DWORD_INFO_EX(INTERNAL_DbgTC, W("DbgTC"), 0, "Allows checking boundary compression for offset mappings", CLRConfig::EEConfig_default) CONFIG_DWORD_INFO_EX(INTERNAL_DbgTransportFaultInject, W("DbgTransportFaultInject"), 0, "Allows injecting a fault for testing the debug transport", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_DbgTransportLog, W("DbgTransportLog"), "Turns on logging for the debug transport") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_DbgTransportLogClass, W("DbgTransportLogClass"), "Mask to control what is logged in DbgTransportLog") +CONFIG_DWORD_INFO(INTERNAL_DbgTransportLog, W("DbgTransportLog"), 0 /* LE_None */, "Turns on logging for the debug transport") +CONFIG_DWORD_INFO(INTERNAL_DbgTransportLogClass, W("DbgTransportLogClass"), (DWORD)-1 /* LC_All */, "Mask to control what is logged in DbgTransportLog") RETAIL_CONFIG_STRING_INFO_EX(UNSUPPORTED_DbgTransportProxyAddress, W("DbgTransportProxyAddress"), "Allows specifying the transport proxy address", CLRConfig::EEConfig_default) CONFIG_DWORD_INFO_EX(INTERNAL_DbgTrapOnSkip, W("DbgTrapOnSkip"), 0, "Allows breaking when we skip a breakpoint", CLRConfig::EEConfig_default) CONFIG_DWORD_INFO_EX(INTERNAL_DbgWaitTimeout, W("DbgWaitTimeout"), 1, "Specifies the timeout value for waits", CLRConfig::EEConfig_default) @@ -245,13 +245,13 @@ CONFIG_DWORD_INFO_EX(INTERNAL_DbgNativeCodeBpBindsAcrossVersions, W("DbgNativeCo /// /// Diagnostics (internal general-purpose) /// -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_ConditionalContracts, W("ConditionalContracts"), "If ENABLE_CONTRACTS_IMPL is defined, sets whether contracts are conditional. (?)") +CONFIG_DWORD_INFO(INTERNAL_ConditionalContracts, W("ConditionalContracts"), 0, "If ENABLE_CONTRACTS_IMPL is defined, sets whether contracts are conditional. (?)") CONFIG_DWORD_INFO(INTERNAL_ConsistencyCheck, W("ConsistencyCheck"), 0, "") CONFIG_DWORD_INFO_EX(INTERNAL_ContinueOnAssert, W("ContinueOnAssert"), 0, "If set, doesn't break on asserts.", CLRConfig::EEConfig_default) RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_disableStackOverflowProbing, W("disableStackOverflowProbing"), 0, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_InjectFatalError, W("InjectFatalError"), "") +CONFIG_DWORD_INFO(INTERNAL_InjectFatalError, W("InjectFatalError"), 0, "") CONFIG_DWORD_INFO_EX(INTERNAL_InjectFault, W("InjectFault"), 0, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_SuppressChecks, W("SuppressChecks"), "") +CONFIG_DWORD_INFO(INTERNAL_SuppressChecks, W("SuppressChecks"),0, "") #ifdef FEATURE_EH_FUNCLETS CONFIG_DWORD_INFO(INTERNAL_SuppressLockViolationsOnReentryFromOS, W("SuppressLockViolationsOnReentryFromOS"), 0, "64 bit OOM tests re-enter the CLR via RtlVirtualUnwind. This indicates whether to suppress resulting locking violations.") #endif // FEATURE_EH_FUNCLETS @@ -259,7 +259,7 @@ CONFIG_DWORD_INFO(INTERNAL_SuppressLockViolationsOnReentryFromOS, W("SuppressLoc /// /// Exception Handling /// -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_AssertOnFailFast, W("AssertOnFailFast"), "") +CONFIG_DWORD_INFO(INTERNAL_AssertOnFailFast, W("AssertOnFailFast"), 1, "") RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_legacyCorruptedStateExceptionsPolicy, W("legacyCorruptedStateExceptionsPolicy"), 0, "Enabled Pre-V4 CSE behavior", CLRConfig::EEConfig_default) CONFIG_DWORD_INFO_EX(INTERNAL_SuppressLostExceptionTypeAssert, W("SuppressLostExceptionTypeAssert"), 0, "", CLRConfig::EEConfig_default) RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_UseEntryPointFilter, W("UseEntryPointFilter"), 0, "", CLRConfig::EEConfig_default) @@ -269,8 +269,8 @@ RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_Corhost_Swallow_Uncaught_Exceptions, W("Cor /// Garbage collector /// CONFIG_DWORD_INFO(INTERNAL_FastGCCheckStack, W("FastGCCheckStack"), 0, "") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_FastGCStress, W("FastGCStress"), "Reduce the number of GCs done by enabling GCStress") -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(UNSUPPORTED_GCBreakOnOOM, W("GCBreakOnOOM"), "Does a DebugBreak at the soonest time we detect an OOM") +CONFIG_DWORD_INFO(INTERNAL_FastGCStress, W("FastGCStress"), 0, "Reduce the number of GCs done by enabling GCStress") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_GCBreakOnOOM, W("GCBreakOnOOM"), 0, "Does a DebugBreak at the soonest time we detect an OOM") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_gcConcurrent, W("gcConcurrent"), (DWORD)-1, "Enables/Disables concurrent GC") #ifdef FEATURE_CONSERVATIVE_GC @@ -285,7 +285,7 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_GCLOHThreshold, W("GCLOHThreshold"), 0, "Speci RETAIL_CONFIG_DWORD_INFO(EXTERNAL_gcAllowVeryLargeObjects, W("gcAllowVeryLargeObjects"), 1, "Allow allocation of 2GB+ objects on GC heap") RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_GCStress, W("GCStress"), 0, "Trigger GCs at regular intervals", CLRConfig::EEConfig_default) CONFIG_DWORD_INFO_EX(INTERNAL_GcStressOnDirectCalls, W("GcStressOnDirectCalls"), 0, "Whether to trigger a GC on direct calls", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(UNSUPPORTED_HeapVerify, W("HeapVerify"), "When set verifies the integrity of the managed heap on entry and exit of each GC") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_HeapVerify, W("HeapVerify"), 0, "When set verifies the integrity of the managed heap on entry and exit of each GC") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_GCNumaAware, W("GCNumaAware"), 1, "Specifies if to enable GC NUMA aware") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_GCCpuGroup, W("GCCpuGroup"), 0, "Specifies if to enable GC to support CPU groups") RETAIL_CONFIG_STRING_INFO(EXTERNAL_GCName, W("GCName"), "") @@ -294,7 +294,6 @@ RETAIL_CONFIG_STRING_INFO(EXTERNAL_GCName, W("GCName"), "") /// IBC /// RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_ConvertIbcData, W("ConvertIbcData"), 1, "Converts between v1 and v2 IBC data", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(UNSUPPORTED_DisableHotCold, W("DisableHotCold"), "Master hot/cold splitting switch in Jit64") RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_DisableIBC, W("DisableIBC"), 0, "Disables the use of IBC data", CLRConfig::EEConfig_default) RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_UseIBCFile, W("UseIBCFile"), 0, "", CLRConfig::EEConfig_default) @@ -303,18 +302,18 @@ RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_UseIBCFile, W("UseIBCFile"), 0, "", CLRConf /// JIT /// CONFIG_DWORD_INFO_EX(INTERNAL_JitBreakEmit, W("JitBreakEmit"), (DWORD)-1, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_JitDebuggable, W("JitDebuggable"), "") +CONFIG_DWORD_INFO(INTERNAL_JitDebuggable, W("JitDebuggable"), 0, "") #if !defined(DEBUG) && !defined(_DEBUG) #define INTERNAL_JitEnableNoWayAssert_Default 0 #else #define INTERNAL_JitEnableNoWayAssert_Default 1 #endif RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_JitEnableNoWayAssert, W("JitEnableNoWayAssert"), INTERNAL_JitEnableNoWayAssert_Default, "", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(UNSUPPORTED_JitFramed, W("JitFramed"), "Forces EBP frames") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_JitFramed, W("JitFramed"), 0, "Forces EBP frames") CONFIG_DWORD_INFO_EX(INTERNAL_JitGCStress, W("JitGCStress"), 0, "GC stress mode for jit", CLRConfig::EEConfig_default) CONFIG_DWORD_INFO(INTERNAL_JitHeartbeat, W("JitHeartbeat"), 0, "") CONFIG_DWORD_INFO(INTERNAL_JitHelperLogging, W("JitHelperLogging"), 0, "") -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(UNSUPPORTED_JITMinOpts, W("JITMinOpts"), "Forces MinOpts") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_JITMinOpts, W("JITMinOpts"), 0, "Forces MinOpts") RETAIL_CONFIG_STRING_INFO(EXTERNAL_JitName, W("JitName"), "Primary Jit to use") #if defined(ALLOW_SXS_JIT) RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_AltJitName, W("AltJitName"), "Alternative Jit to use, will fall back to primary jit.", CLRConfig::EEConfig_default) @@ -335,13 +334,13 @@ RETAIL_CONFIG_STRING_INFO_EX(INTERNAL_AltJitNgen, W("AltJitNgen"), "Enables AltJ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_JitHostMaxSlabCache, W("JitHostMaxSlabCache"), 0x1000000, "Sets jit host max slab cache size, 16MB default") -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_JitOptimizeType, W("JitOptimizeType"), "") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_JitOptimizeType, W("JitOptimizeType"), 0 /* OPT_DEFAULT */, "") RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_JitPrintInlinedMethods, W("JitPrintInlinedMethods"), 0, "", CLRConfig::EEConfig_default) RETAIL_CONFIG_DWORD_INFO(EXTERNAL_JitTelemetry, W("JitTelemetry"), 1, "If non-zero, gather JIT telemetry data") RETAIL_CONFIG_STRING_INFO(INTERNAL_JitTimeLogFile, W("JitTimeLogFile"), "If set, gather JIT throughput data and write to this file.") RETAIL_CONFIG_STRING_INFO(INTERNAL_JitTimeLogCsv, W("JitTimeLogCsv"), "If set, gather JIT throughput data and write to a CSV file. This mode must be used in internal retail builds.") RETAIL_CONFIG_STRING_INFO(INTERNAL_JitFuncInfoLogFile, W("JitFuncInfoLogFile"), "If set, gather JIT function info and write to this file.") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_JitVerificationDisable, W("JitVerificationDisable"), "") +CONFIG_DWORD_INFO(INTERNAL_JitVerificationDisable, W("JitVerificationDisable"), 0, "") RETAIL_CONFIG_DWORD_INFO(INTERNAL_JitLockWrite, W("JitLockWrite"), 0, "Force all volatile writes to be 'locked'") CONFIG_STRING_INFO_EX(INTERNAL_TailCallMax, W("TailCallMax"), "", CLRConfig::EEConfig_default) RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_TailCallOpt, W("TailCallOpt"), "", CLRConfig::EEConfig_default) @@ -424,24 +423,24 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_BreakOnOutOfMemoryWithinRange, W("BreakOnOutOf /// /// Log /// -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_LogEnable, W("LogEnable"), "Turns on the traditional CLR log.") -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_LogFacility, W("LogFacility"), "Specifies a facility mask for CLR log. (See 'loglf.h'; VM interprets string value as hex number.) Also used by stresslog.") -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_LogFacility2, W("LogFacility2"), "Specifies a facility mask for CLR log. (See 'loglf.h'; VM interprets string value as hex number.) Also used by stresslog.") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_LogEnable, W("LogEnable"), 0, "Turns on the traditional CLR log.") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_LogFacility, W("LogFacility"), 0, "Specifies a facility mask for CLR log. (See 'loglf.h'; VM interprets string value as hex number.) Also used by stresslog.") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_LogFacility2, W("LogFacility2"), 0, "Specifies a facility mask for CLR log. (See 'loglf.h'; VM interprets string value as hex number.) Also used by stresslog.") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_logFatalError, W("logFatalError"), 1, "Specifies whether EventReporter logs fatal errors in the Windows event log.") CONFIG_STRING_INFO_EX(INTERNAL_LogFile, W("LogFile"), "Specifies a file name for the CLR log.", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_LogFileAppend, W("LogFileAppend"), "Specifies whether to append to or replace the CLR log file.") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_LogFlushFile, W("LogFlushFile"), "Specifies whether to flush the CLR log file on each write.") -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_LogLevel, W("LogLevel"), "4=10 msgs, 9=1000000, 10=everything") -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_LogToConsole, W("LogToConsole"), "Writes the CLR log to console.") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_LogToDebugger, W("LogToDebugger"), "Writes the CLR log to debugger (OutputDebugStringA).") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_LogToFile, W("LogToFile"), "Writes the CLR log to a file.") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_LogWithPid, W("LogWithPid"), "Appends pid to filename for the CLR log.") +CONFIG_DWORD_INFO(INTERNAL_LogFileAppend, W("LogFileAppend"), 0 , "Specifies whether to append to or replace the CLR log file.") +CONFIG_DWORD_INFO(INTERNAL_LogFlushFile, W("LogFlushFile"), 0 , "Specifies whether to flush the CLR log file on each write.") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_LogLevel, W("LogLevel"), 0 , "4=10 msgs, 9=1000000, 10=everything") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_LogToConsole, W("LogToConsole"), 0 , "Writes the CLR log to console.") +CONFIG_DWORD_INFO(INTERNAL_LogToDebugger, W("LogToDebugger"), 0 , "Writes the CLR log to debugger (OutputDebugStringA).") +CONFIG_DWORD_INFO(INTERNAL_LogToFile, W("LogToFile"), 0 , "Writes the CLR log to a file.") +CONFIG_DWORD_INFO(INTERNAL_LogWithPid, W("LogWithPid"), FALSE, "Appends pid to filename for the CLR log.") /// /// MetaData /// CONFIG_DWORD_INFO_EX(INTERNAL_MD_ApplyDeltaBreak, W("MD_ApplyDeltaBreak"), 0, "ASSERT when applying EnC", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_AssertOnBadImageFormat, W("AssertOnBadImageFormat"), "ASSERT when invalid MD read") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_AssertOnBadImageFormat, W("AssertOnBadImageFormat"), 0, "ASSERT when invalid MD read") RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_MD_DeltaCheck, W("MD_DeltaCheck"), 1, "Some checks of GUID when applying EnC (?)", CLRConfig::EEConfig_default) CONFIG_DWORD_INFO_EX(INTERNAL_MD_EncDelta, W("MD_EncDelta"), 0, "Forces EnC Delta format in MD (?)", CLRConfig::EEConfig_default) RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_MD_ForceNoColDesSharing, W("MD_ForceNoColDesSharing"), 0, "Don't know - the only usage I could find is #if 0 (?)", CLRConfig::EEConfig_default) @@ -545,12 +544,11 @@ RETAIL_CONFIG_STRING_INFO(EXTERNAL_StartupDelayMS, W("StartupDelayMS"), "") /// Stress /// CONFIG_DWORD_INFO_EX(INTERNAL_StressCOMCall, W("StressCOMCall"), 0, "", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(UNSUPPORTED_StressLog, W("StressLog"), "Turns on the stress log.") -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(UNSUPPORTED_ForceEnc, W("ForceEnc"), "Forces Edit and Continue to be on for all eligible modules.") -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(UNSUPPORTED_StressLogSize, W("StressLogSize"), "Stress log size in bytes per thread.") -RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS(UNSUPPORTED_StressLogFilename, W("StressLogFilename"), "Stress log filename for memory mapped stress log.") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_StressLog, W("StressLog"), 0, "Turns on the stress log.") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_ForceEnc, W("ForceEnc"), 0, "Forces Edit and Continue to be on for all eligible modules.") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_StressLogSize, W("StressLogSize"), 0, "Stress log size in bytes per thread.") +RETAIL_CONFIG_STRING_INFO(UNSUPPORTED_StressLogFilename, W("StressLogFilename"), "Stress log filename for memory mapped stress log.") CONFIG_DWORD_INFO_EX(INTERNAL_stressSynchronized, W("stressSynchronized"), 0, "Unknown if or where this is used; unless a test is specifically depending on this, it can be removed.", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_StressThreadCount, W("StressThreadCount"), "") /// /// Thread Suspend @@ -686,7 +684,7 @@ RETAIL_CONFIG_STRING_INFO(EXTERNAL_ZapBBInstrDir, W("ZapBBInstrDir"), "") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_ZapDisable, W("ZapDisable"), 0, "") CONFIG_STRING_INFO_EX(INTERNAL_ZapExclude, W("ZapExclude"), "", CLRConfig::EEConfig_default) CONFIG_STRING_INFO_EX(INTERNAL_ZapOnly, W("ZapOnly"), "", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_ZapRequire, W("ZapRequire"), "") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_ZapRequire, W("ZapRequire"), 0, "") RETAIL_CONFIG_STRING_INFO(EXTERNAL_ZapRequireExcludeList, W("ZapRequireExcludeList"), "") RETAIL_CONFIG_STRING_INFO(EXTERNAL_ZapRequireList, W("ZapRequireList"), "") RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_ZapSet, W("ZapSet"), "", CLRConfig::EEConfig_default) @@ -704,7 +702,7 @@ RETAIL_CONFIG_STRING_INFO(INTERNAL_EventNameFilter, W("EventNameFilter"), "") /// /// Interop /// -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_ExposeExceptionsInCOM, W("ExposeExceptionsInCOM"), "") +CONFIG_DWORD_INFO(INTERNAL_ExposeExceptionsInCOM, W("ExposeExceptionsInCOM"), 0, "") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_InteropValidatePinnedObjects, W("InteropValidatePinnedObjects"), 0, "After returning from a managed-to-unmanaged interop call, validate GC heap around objects pinned by IL stubs.") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_InteropLogArguments, W("InteropLogArguments"), 0, "Log all pinned arguments passed to an interop call") RETAIL_CONFIG_STRING_INFO(UNSUPPORTED_LogCCWRefCountChange, W("LogCCWRefCountChange"), "Outputs debug information and calls LogCCWRefCountChange_BREAKPOINT when AddRef or Release is called on a CCW.") @@ -766,27 +764,24 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_GDBJitEmitDebugFrame, W("GDBJitEmitDebugFrame" // DO NOT ADD ANY MORE CONFIG SWITCHES TO THIS SECTION! // ** CONFIG_DWORD_INFO_EX(INTERNAL_ActivatePatchSkip, W("ActivatePatchSkip"), 0, "Allows an assert when ActivatePatchSkip is called", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_AlwaysUseMetadataInterfaceMapLayout, W("AlwaysUseMetadataInterfaceMapLayout"), "Used for debugging generic interface map layout.") +CONFIG_DWORD_INFO(INTERNAL_AlwaysUseMetadataInterfaceMapLayout, W("AlwaysUseMetadataInterfaceMapLayout"), 0, "Used for debugging generic interface map layout.") CONFIG_DWORD_INFO(INTERNAL_AssertOnUnneededThis, W("AssertOnUnneededThis"), 0, "While the ConfigDWORD is unnecessary, the contained ASSERT should be kept. This may result in some work tracking down violating MethodDescCallSites.") CONFIG_DWORD_INFO_EX(INTERNAL_AssertStacktrace, W("AssertStacktrace"), 1, "", CLRConfig::EEConfig_default) CONFIG_DWORD_INFO_EX(INTERNAL_clearNativeImageStress, W("clearNativeImageStress"), 0, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_CPUFamily, W("CPUFamily"), "") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_CPUFeatures, W("CPUFeatures"), "") +CONFIG_DWORD_INFO(INTERNAL_CPUFamily, W("CPUFamily"), 0xFFFFFFFF, "") +CONFIG_DWORD_INFO(INTERNAL_CPUFeatures, W("CPUFeatures"), 0xFFFFFFFF, "") RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_DisableConfigCache, W("DisableConfigCache"), 0, "Used to disable the \"probabilistic\" config cache, which walks through the appropriate config registry keys on init and probabilistically keeps track of which exist.", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_DisableStackwalkCache, W("DisableStackwalkCache"), "") -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(UNSUPPORTED_DoubleArrayToLargeObjectHeap, W("DoubleArrayToLargeObjectHeap"), "Controls double[] placement") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_DisableStackwalkCache, W("DisableStackwalkCache"), 0, "") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_DoubleArrayToLargeObjectHeap, W("DoubleArrayToLargeObjectHeap"), 0, "Controls double[] placement") CONFIG_STRING_INFO(INTERNAL_DumpOnClassLoad, W("DumpOnClassLoad"), "Dumps information about loaded class to log.") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_ExpandAllOnLoad, W("ExpandAllOnLoad"), "") -CONFIG_STRING_INFO_DIRECT_ACCESS(INTERNAL_ForcedRuntime, W("ForcedRuntime"), "Verify version of CLR loaded") +CONFIG_DWORD_INFO(INTERNAL_ExpandAllOnLoad, W("ExpandAllOnLoad"), 0, "") CONFIG_DWORD_INFO_EX(INTERNAL_ForceRelocs, W("ForceRelocs"), 0, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_GenerateLongJumpDispatchStubRatio, W("GenerateLongJumpDispatchStubRatio"), "Useful for testing VSD on AMD64") +CONFIG_DWORD_INFO(INTERNAL_GenerateLongJumpDispatchStubRatio, W("GenerateLongJumpDispatchStubRatio"), 0, "Useful for testing VSD on AMD64") CONFIG_DWORD_INFO_EX(INTERNAL_HashStack, W("HashStack"), 0, "", CLRConfig::EEConfig_default) CONFIG_DWORD_INFO(INTERNAL_HostManagerConfig, W("HostManagerConfig"), (DWORD)-1, "") CONFIG_DWORD_INFO(INTERNAL_HostTestThreadAbort, W("HostTestThreadAbort"), 0, "") CONFIG_STRING_INFO(INTERNAL_InvokeHalt, W("InvokeHalt"), "Throws an assert when the given method is invoked through reflection.") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_MaxStackDepth, W("MaxStackDepth"), "") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_MaxStubUnwindInfoSegmentSize, W("MaxStubUnwindInfoSegmentSize"), "") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_MaxThreadRecord, W("MaxThreadRecord"), "") +CONFIG_DWORD_INFO(INTERNAL_MaxStubUnwindInfoSegmentSize, W("MaxStubUnwindInfoSegmentSize"), 0, "") CONFIG_DWORD_INFO(INTERNAL_MessageDebugOut, W("MessageDebugOut"), 0, "") RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_NativeImageRequire, W("NativeImageRequire"), 0, "", CLRConfig::EEConfig_default) CONFIG_DWORD_INFO_EX(INTERNAL_NestedEhOom, W("NestedEhOom"), 0, "", CLRConfig::EEConfig_default) @@ -794,8 +789,7 @@ CONFIG_DWORD_INFO_EX(INTERNAL_NestedEhOom, W("NestedEhOom"), 0, "", CLRConfig::E RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_NoGuiOnAssert, W("NoGuiOnAssert"), INTERNAL_NoGuiOnAssert_Default, "", CLRConfig::EEConfig_default) RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_NoProcedureSplitting, W("NoProcedureSplitting"), 0, "", CLRConfig::EEConfig_default) CONFIG_DWORD_INFO_EX(INTERNAL_NoStringInterning, W("NoStringInterning"), 1, "Disallows string interning. I see no value in it anymore.", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_NotifyBadAppCfg, W("NotifyBadAppCfg"), "Whether to show a message box for bad application config file.") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_PauseOnLoad, W("PauseOnLoad"), "Stops in SystemDomain::init. I think it can be removed.") +CONFIG_DWORD_INFO(INTERNAL_PauseOnLoad, W("PauseOnLoad"), 0, "Stops in SystemDomain::init. I think it can be removed.") CONFIG_DWORD_INFO(INTERNAL_PerfAllocsSizeThreshold, W("PerfAllocsSizeThreshold"), 0x3FFFFFFF, "Log facility LF_GCALLOC logs object allocations. This flag controls which ones also log stacktraces. Predates ClrProfiler.") CONFIG_DWORD_INFO(INTERNAL_PerfNumAllocsThreshold, W("PerfNumAllocsThreshold"), 0x3FFFFFFF, "Log facility LF_GCALLOC logs object allocations. This flag controls which ones also log stacktraces. Predates ClrProfiler.") CONFIG_STRING_INFO(INTERNAL_PerfTypesToLog, W("PerfTypesToLog"), "Log facility LF_GCALLOC logs object allocations. This flag controls which ones also log stacktraces. Predates ClrProfiler.") @@ -805,31 +799,24 @@ CONFIG_STRING_INFO(INTERNAL_PrestubHalt, W("PrestubHalt"), "") RETAIL_CONFIG_STRING_INFO(EXTERNAL_RestrictedGCStressExe, W("RestrictedGCStressExe"), "") CONFIG_DWORD_INFO_EX(INTERNAL_ReturnSourceTypeForTesting, W("ReturnSourceTypeForTesting"), 0, "Allows returning the (internal only) source type of an IL to Native mapping for debugging purposes", CLRConfig::EEConfig_default) RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_RSStressLog, W("RSStressLog"), 0, "Allows turning on logging for RS startup", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_SaveThreadInfo, W("SaveThreadInfo"), "") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_SaveThreadInfoMask, W("SaveThreadInfoMask"), "") CONFIG_DWORD_INFO(INTERNAL_SBDumpOnNewIndex, W("SBDumpOnNewIndex"), 0, "Used for Syncblock debugging. It's been a while since any of those have been used.") CONFIG_DWORD_INFO(INTERNAL_SBDumpOnResize, W("SBDumpOnResize"), 0, "Used for Syncblock debugging. It's been a while since any of those have been used.") CONFIG_DWORD_INFO(INTERNAL_SBDumpStyle, W("SBDumpStyle"), 0, "Used for Syncblock debugging. It's been a while since any of those have been used.") -RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS(UNSUPPORTED_ShimDatabaseVersion, W("ShimDatabaseVersion"), "Force using shim database version in registry") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_SleepOnExit, W("SleepOnExit"), 0, "Used for lrak detection. I'd say deprecated by umdh.") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_StubLinkerUnwindInfoVerificationOn, W("StubLinkerUnwindInfoVerificationOn"), "") +CONFIG_DWORD_INFO(INTERNAL_StubLinkerUnwindInfoVerificationOn, W("StubLinkerUnwindInfoVerificationOn"), 0, "") RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_SuccessExit, W("SuccessExit"), 0, "", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_SymbolReadingPolicy, W("SymbolReadingPolicy"), "Specifies when PDBs may be read") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TestDataConsistency, W("TestDataConsistency"), FALSE, "Allows ensuring the left side is not holding locks (and may thus be in an inconsistent state) when inspection occurs") RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_ThreadGuardPages, W("ThreadGuardPages"), 0, "", CLRConfig::EEConfig_default) RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_Timeline, W("Timeline"), 0, "", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(UNSUPPORTED_TotalStressLogSize, W("TotalStressLogSize"), "Total stress log size in bytes.") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TotalStressLogSize, W("TotalStressLogSize"), 0, "Total stress log size in bytes.") #ifdef _DEBUG -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_TraceIUnknown, W("TraceIUnknown"), "") -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_TraceWrap, W("TraceWrap"), "") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TraceWrap, W("TraceWrap"), 0, "") #endif -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_TURNOFFDEBUGINFO, W("TURNOFFDEBUGINFO"), "") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_UseMethodDataCache, W("UseMethodDataCache"), FALSE, "Used during feature development; may now be removed.") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_UseParentMethodData, W("UseParentMethodData"), TRUE, "Used during feature development; may now be removed.") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_VerifierOff, W("VerifierOff"), "") -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_VerifyAllOnLoad, W("VerifyAllOnLoad"), "") +CONFIG_DWORD_INFO(INTERNAL_VerifierOff, W("VerifierOff"), 0, "") // ** // PLEASE MOVE ANY CONFIG SWITCH YOU OWN OUT OF THIS SECTION INTO A CATEGORY ABOVE // diff --git a/src/coreclr/inc/stresslog.h b/src/coreclr/inc/stresslog.h index 706f3c942377..d125591b7364 100644 --- a/src/coreclr/inc/stresslog.h +++ b/src/coreclr/inc/stresslog.h @@ -267,7 +267,7 @@ struct StressLogMsg; class StressLog { public: static void Initialize(unsigned facilities, unsigned level, unsigned maxBytesPerThread, - ULONGLONG maxBytesTotal, void* moduleBase, LPWSTR logFilename = nullptr); + unsigned maxBytesTotal, void* moduleBase, LPWSTR logFilename = nullptr); static void Terminate(BOOL fProcessDetach=FALSE); static void ThreadDetach(); // call at DllMain THREAD_DETACH if you want to recycle thread logs #ifndef STRESS_LOG_ANALYZER diff --git a/src/coreclr/inc/utilcode.h b/src/coreclr/inc/utilcode.h index 22193da0ff36..d2aea0041f7b 100644 --- a/src/coreclr/inc/utilcode.h +++ b/src/coreclr/inc/utilcode.h @@ -1134,51 +1134,8 @@ class REGUTIL static HKEY s_hUserFrameworkKey; }; -// need this here because CLRConfig depends on REGUTIL, and ConfigStringHolder depends on CLRConfig #include "clrconfig.h" -//----------------------------------------------------------------------------- -// Wrapper for configuration strings. -// This serves as a holder to call FreeConfigString. -class ConfigStringHolder -{ -public: - ConfigStringHolder() { m_wszString = NULL; } - ~ConfigStringHolder() - { - Clear(); - } - - // - // NOTE: The following function is deprecated; use the CLRConfig class instead. - // To access a configuration value through CLRConfig, add an entry in file:../inc/CLRConfigValues.h. - // - void Init_DontUse_(LPCWSTR wszName) - { - Clear(); - m_wszString = REGUTIL::GetConfigString_DontUse_(wszName); - } - - // Free resources. - void Clear() - { - if (m_wszString != NULL) - { - REGUTIL::FreeConfigString(m_wszString); - m_wszString = NULL; - } - } - - // Get the string value. NULL if not set. - LPCWSTR Value() - { - return m_wszString; - } - -private: - LPWSTR m_wszString; -}; - #endif // defined(NO_CLRCONFIG) #include "ostype.h" @@ -3796,19 +3753,6 @@ class MethodNamesList : public MethodNamesListBase class ConfigDWORD { public: - // - // NOTE: The following function is deprecated; use the CLRConfig class instead. - // To access a configuration value through CLRConfig, add an entry in file:../inc/CLRConfigValues.h. - // - inline DWORD val_DontUse_(__in __in_z LPCWSTR keyName, DWORD defaultVal=0) - { - WRAPPER_NO_CONTRACT; - // make sure that the memory was zero initialized - _ASSERTE(m_inited == 0 || m_inited == 1); - - if (!m_inited) init_DontUse_(keyName, defaultVal); - return m_value; - } inline DWORD val(const CLRConfig::ConfigDWORDInfo & info) { WRAPPER_NO_CONTRACT; @@ -3820,7 +3764,6 @@ class ConfigDWORD } private: - void init_DontUse_(__in __in_z LPCWSTR keyName, DWORD defaultVal=0); void init(const CLRConfig::ConfigDWORDInfo & info); private: diff --git a/src/coreclr/md/debug_metadata.h b/src/coreclr/md/debug_metadata.h index 7f98c4c182b5..7e18e4affc0f 100644 --- a/src/coreclr/md/debug_metadata.h +++ b/src/coreclr/md/debug_metadata.h @@ -60,7 +60,7 @@ #define Debug_ReportError(strMessage) \ do { \ - if (REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_AssertOnBadImageFormat, 0)) \ + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_AssertOnBadImageFormat)) \ { _ASSERTE_MSG(FALSE, (strMessage)); } \ } while(0) #define Debug_ReportInternalError(strMessage) _ASSERTE_MSG(FALSE, (strMessage)) diff --git a/src/coreclr/md/enc/mdinternalrw.cpp b/src/coreclr/md/enc/mdinternalrw.cpp index 629300415ae2..c051bb405fd9 100644 --- a/src/coreclr/md/enc/mdinternalrw.cpp +++ b/src/coreclr/md/enc/mdinternalrw.cpp @@ -720,7 +720,7 @@ ULONG MDInternalRW::GetCountWithTokenKind( // return hresult break; default: #ifdef _DEBUG - if(REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_AssertOnBadImageFormat, 1)) + if(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_AssertOnBadImageFormat, 1)) _ASSERTE(!"Invalid Blob Offset"); #endif ulCount = 0; @@ -2367,7 +2367,7 @@ MDInternalRW::GetSigFromToken( // not a known token type. #ifdef _DEBUG - if(REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_AssertOnBadImageFormat, 1)) + if(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_AssertOnBadImageFormat, 1)) _ASSERTE(!"Unexpected token type"); #endif *pcbSig = 0; diff --git a/src/coreclr/md/inc/metamodel.h b/src/coreclr/md/inc/metamodel.h index a3a615fc6cd8..e3206a589ad9 100644 --- a/src/coreclr/md/inc/metamodel.h +++ b/src/coreclr/md/inc/metamodel.h @@ -1368,7 +1368,7 @@ template class CMiniMdTemplate : public CMiniMdBase break; case mdtString: default: - if(REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_AssertOnBadImageFormat, 0)) + if(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_AssertOnBadImageFormat)) _ASSERTE(!"Unexpected token type in FindCustomAttributeByName"); hr = COR_E_BADIMAGEFORMAT; goto ErrExit; diff --git a/src/coreclr/utilcode/clrconfig.cpp b/src/coreclr/utilcode/clrconfig.cpp index 5142e395ac0e..6c0f6eca6547 100644 --- a/src/coreclr/utilcode/clrconfig.cpp +++ b/src/coreclr/utilcode/clrconfig.cpp @@ -32,14 +32,6 @@ const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, CLRConfig::EEConfig_default}; #define RETAIL_CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions) \ const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, lookupOptions}; - -// TEMPORARY macros that intialize strings for config value accesses that haven't been moved over to -// CLRConfig yet. Once all accesses have been moved, these macros (and corresponding instantiations in -// file:../utilcode/CLRConfig.h) should be removed. -#define RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(symbol, name, description) \ - const LPCWSTR CLRConfig::symbol = name; -#define RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS(symbol, name, description) \ - const LPCWSTR CLRConfig::symbol = name; // // Debug versions of the macros // @@ -52,17 +44,11 @@ const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, CLRConfig::EEConfig_default}; #define CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions) \ const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, lookupOptions}; - #define CONFIG_DWORD_INFO_DIRECT_ACCESS(symbol, name, description) \ - const LPCWSTR CLRConfig::symbol = name; - #define CONFIG_STRING_INFO_DIRECT_ACCESS(symbol, name, description) \ - const LPCWSTR CLRConfig::symbol = name; #else #define CONFIG_DWORD_INFO(symbol, name, defaultValue, description) #define CONFIG_DWORD_INFO_EX(symbol, name, defaultValue, description, lookupOptions) #define CONFIG_STRING_INFO(symbol, name, description) #define CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions) - #define CONFIG_DWORD_INFO_DIRECT_ACCESS(symbol, name, description) - #define CONFIG_STRING_INFO_DIRECT_ACCESS(symbol, name, description) #endif // _DEBUG // Now that we have defined what what the macros in file:../inc/CLRConfigValues.h mean, include it to generate the code. @@ -72,14 +58,10 @@ #undef RETAIL_CONFIG_STRING_INFO #undef RETAIL_CONFIG_DWORD_INFO_EX #undef RETAIL_CONFIG_STRING_INFO_EX -#undef RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS -#undef RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS #undef CONFIG_DWORD_INFO #undef CONFIG_STRING_INFO #undef CONFIG_DWORD_INFO_EX #undef CONFIG_STRING_INFO_EX -#undef CONFIG_DWORD_INFO_DIRECT_ACCESS -#undef CONFIG_STRING_INFO_DIRECT_ACCESS // @@ -88,19 +70,13 @@ // Arguments: // * info - see file:../inc/CLRConfig.h for details. // -// * useDefaultIfNotSet - if true, fall back to the default value if the value is not set. -// -// * acceptExplicitDefaultFromRegutil - if false, only accept a value returned by REGUTIL if it is -// different from the default value. This parameter is useful as a way to preserve existing -// behavior. -// // * result - the result. // // Return value: // * true for success, false otherwise. // // static -DWORD CLRConfig::GetConfigValue(const ConfigDWORDInfo & info, bool acceptExplicitDefaultFromRegutil, /* [Out] */ bool *isDefault) +DWORD CLRConfig::GetConfigValue(const ConfigDWORDInfo & info, /* [Out] */ bool *isDefault) { CONTRACTL { @@ -112,7 +88,6 @@ DWORD CLRConfig::GetConfigValue(const ConfigDWORDInfo & info, bool acceptExplici _ASSERTE (isDefault != nullptr); - // // Set up REGUTIL options. // @@ -122,31 +97,36 @@ DWORD CLRConfig::GetConfigValue(const ConfigDWORDInfo & info, bool acceptExplici DWORD resultMaybe; HRESULT hr = REGUTIL::GetConfigDWORD_DontUse_(info.name, info.defaultValue, &resultMaybe, level, prependCOMPlus); - if (!acceptExplicitDefaultFromRegutil) + // Ignore the default value even if it's set explicitly. + if (resultMaybe != info.defaultValue) { - // Ignore the default value even if it's set explicitly. - if (resultMaybe != info.defaultValue) - { - *isDefault = false; - return resultMaybe; - } - } - else - { - // If we are willing to accept the default value when it's set explicitly, - // checking the HRESULT here is sufficient. E_FAIL is returned when the - // default is used. - if (SUCCEEDED(hr)) - { - *isDefault = false; - return resultMaybe; - } + *isDefault = false; + return resultMaybe; } *isDefault = true; return info.defaultValue; } +// +// Look up a DWORD config value. +// +// Arguments: +// * info - see file:../inc/CLRConfig.h for details +// +// static +DWORD CLRConfig::GetConfigValue(const ConfigDWORDInfo & info, DWORD defaultValue) +{ + bool isDefault = false; + DWORD valueMaybe = GetConfigValue(info, &isDefault); + + // If the default value was returned, defer to the user supplied version. + if (isDefault) + return defaultValue; + + return valueMaybe; +} + // // Look up a DWORD config value. // @@ -156,10 +136,8 @@ DWORD CLRConfig::GetConfigValue(const ConfigDWORDInfo & info, bool acceptExplici // static DWORD CLRConfig::GetConfigValue(const ConfigDWORDInfo & info) { - // We pass false for 'acceptExplicitDefaultFromRegutil' to maintain the existing behavior of this function. - // Callers who don't need that behavior should switch to the other version of this function and pass true. bool unused; - return GetConfigValue(info, false /* acceptExplicitDefaultFromRegutil */, &unused); + return GetConfigValue(info, &unused); } // diff --git a/src/coreclr/utilcode/configuration.cpp b/src/coreclr/utilcode/configuration.cpp index e67507d5bae0..50a5e335a742 100644 --- a/src/coreclr/utilcode/configuration.cpp +++ b/src/coreclr/utilcode/configuration.cpp @@ -52,7 +52,7 @@ static LPCWSTR GetConfigurationValue(LPCWSTR name) DWORD Configuration::GetKnobDWORDValue(LPCWSTR name, const CLRConfig::ConfigDWORDInfo& dwordInfo) { bool returnedDefaultValue; - DWORD legacyValue = CLRConfig::GetConfigValue(dwordInfo, true /* acceptExplicitDefaultFromRegutil */, &returnedDefaultValue); + DWORD legacyValue = CLRConfig::GetConfigValue(dwordInfo, &returnedDefaultValue); if (!returnedDefaultValue) { return legacyValue; @@ -108,7 +108,7 @@ LPCWSTR Configuration::GetKnobStringValue(LPCWSTR name) bool Configuration::GetKnobBooleanValue(LPCWSTR name, const CLRConfig::ConfigDWORDInfo& dwordInfo) { bool returnedDefaultValue; - DWORD legacyValue = CLRConfig::GetConfigValue(dwordInfo, true /* acceptExplicitDefaultFromRegutil */, &returnedDefaultValue); + DWORD legacyValue = CLRConfig::GetConfigValue(dwordInfo, &returnedDefaultValue); if (!returnedDefaultValue) { return (legacyValue != 0); diff --git a/src/coreclr/utilcode/log.cpp b/src/coreclr/utilcode/log.cpp index 37edc05b5cdb..b4a73f2cb6ba 100644 --- a/src/coreclr/utilcode/log.cpp +++ b/src/coreclr/utilcode/log.cpp @@ -47,16 +47,16 @@ VOID InitLogging() // FIX bit of a workaround for now, check for the log file in the // registry and if there, turn on file logging VPM - LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogEnable, LOG_ENABLE); - LogFacilityMask = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_LogFacility, LogFacilityMask) | LF_ALWAYS; - LogVMLevel = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::EXTERNAL_LogLevel, LogVMLevel); - LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogFileAppend, LOG_ENABLE_APPEND_FILE); - LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogFlushFile, LOG_ENABLE_FLUSH_FILE); - LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogToDebugger, LOG_ENABLE_DEBUGGER_LOGGING); - LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogToFile, LOG_ENABLE_FILE_LOGGING); - LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogToConsole, LOG_ENABLE_CONSOLE_LOGGING); + LogFlags |= (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_LogEnable) != 0) ? LOG_ENABLE : 0; + LogFacilityMask = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_LogFacility, LogFacilityMask) | LF_ALWAYS; + LogVMLevel = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_LogLevel, LogVMLevel); + LogFlags |= (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_LogFileAppend) != 0) ? LOG_ENABLE_APPEND_FILE : 0; + LogFlags |= (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_LogFlushFile) != 0) ? LOG_ENABLE_FLUSH_FILE : 0; + LogFlags |= (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_LogToDebugger) != 0) ? LOG_ENABLE_DEBUGGER_LOGGING : 0; + LogFlags |= (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_LogToFile) != 0) ? LOG_ENABLE_FILE_LOGGING : 0; + LogFlags |= (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_LogToConsole) != 0) ? LOG_ENABLE_CONSOLE_LOGGING : 0; - LogFacilityMask2 = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_LogFacility2, LogFacilityMask2) | LF_ALWAYS; + LogFacilityMask2 = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_LogFacility2, LogFacilityMask2) | LF_ALWAYS; if (SUCCEEDED(szLogFileName.ReSizeNoThrow(MAX_LONGPATH))) { @@ -73,7 +73,7 @@ VOID InitLogging() delete fileName; } - if (REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_LogWithPid, FALSE)) + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_LogWithPid)) { WCHAR szPid[20]; swprintf_s(szPid, COUNTOF(szPid), W(".%d"), GetCurrentProcessId()); diff --git a/src/coreclr/utilcode/stresslog.cpp b/src/coreclr/utilcode/stresslog.cpp index 2b81551eb387..6203d5727017 100644 --- a/src/coreclr/utilcode/stresslog.cpp +++ b/src/coreclr/utilcode/stresslog.cpp @@ -142,7 +142,7 @@ void StressLog::Leave(CRITSEC_COOKIE) { /*********************************************************************************/ void StressLog::Initialize(unsigned facilities, unsigned level, unsigned maxBytesPerThread, - ULONGLONG maxBytesTotal, void* moduleBase, LPWSTR logFilename) + unsigned maxBytesTotal, void* moduleBase, LPWSTR logFilename) { STATIC_CONTRACT_LEAF; diff --git a/src/coreclr/utilcode/util.cpp b/src/coreclr/utilcode/util.cpp index 018ea351c530..1be42772b6e2 100644 --- a/src/coreclr/utilcode/util.cpp +++ b/src/coreclr/utilcode/util.cpp @@ -1405,22 +1405,6 @@ bool ConfigMethodSet::contains(LPCUTF8 methodName, LPCUTF8 className, CORINFO_SI return(m_list.IsInList(methodName, className, pSigInfo)); } -/**************************************************************************/ -void ConfigDWORD::init_DontUse_(__in_z LPCWSTR keyName, DWORD defaultVal) -{ - CONTRACTL - { - NOTHROW; - } - CONTRACTL_END; - - // make sure that the memory was zero initialized - _ASSERTE(m_inited == 0 || m_inited == 1); - - m_value = REGUTIL::GetConfigDWORD_DontUse_(keyName, defaultVal); - m_inited = 1; -} - /**************************************************************************/ void ConfigString::init(const CLRConfig::ConfigStringInfo & info) { diff --git a/src/coreclr/vm/appdomain.cpp b/src/coreclr/vm/appdomain.cpp index 46aa6f3d6e17..8494ff4a6ed0 100644 --- a/src/coreclr/vm/appdomain.cpp +++ b/src/coreclr/vm/appdomain.cpp @@ -1291,7 +1291,7 @@ void SystemDomain::Init() } #ifdef _DEBUG - BOOL fPause = EEConfig::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_PauseOnLoad, FALSE); + BOOL fPause = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_PauseOnLoad); while (fPause) { diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index 62926cdef32c..0e8060ef65e0 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -705,12 +705,12 @@ void EEStartupHelper() #endif // TARGET_UNIX #ifdef STRESS_LOG - if (REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_StressLog, g_pConfig->StressLog ()) != 0) { - unsigned facilities = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_LogFacility, LF_ALL); - unsigned level = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::EXTERNAL_LogLevel, LL_INFO1000); - unsigned bytesPerThread = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_StressLogSize, STRESSLOG_CHUNK_SIZE * 4); - ULONGLONG totalBytes = REGUTIL::GetConfigULONGLONG_DontUse_(CLRConfig::UNSUPPORTED_TotalStressLogSize, STRESSLOG_CHUNK_SIZE * 1024); - LPWSTR logFilename = REGUTIL::GetConfigString_DontUse_(CLRConfig::UNSUPPORTED_StressLogFilename); + if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_StressLog, g_pConfig->StressLog()) != 0) { + unsigned facilities = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_LogFacility, LF_ALL); + unsigned level = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_LogLevel, LL_INFO1000); + unsigned bytesPerThread = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_StressLogSize, STRESSLOG_CHUNK_SIZE * 4); + unsigned totalBytes = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TotalStressLogSize, STRESSLOG_CHUNK_SIZE * 1024); + CLRConfigStringHolder logFilename = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_StressLogFilename); StressLog::Initialize(facilities, level, bytesPerThread, totalBytes, GetClrModuleBase(), logFilename); g_pStressLog = &StressLog::theLog; } diff --git a/src/coreclr/vm/commodule.cpp b/src/coreclr/vm/commodule.cpp index 9eb5e8ead771..2ac2b03392e9 100644 --- a/src/coreclr/vm/commodule.cpp +++ b/src/coreclr/vm/commodule.cpp @@ -44,7 +44,7 @@ static ISymUnmanagedWriter **CreateISymWriterForDynamicModule(ReflectionModule * static ConfigDWORD dbgForcePDBSymbols; - if(dbgForcePDBSymbols.val_DontUse_(W("DbgForcePDBSymbols"), 0) == 1) + if(dbgForcePDBSymbols.val(CLRConfig::INTERNAL_DbgForcePDBSymbols) == 1) { symFormatToUse = eSymbolFormatPDB; } diff --git a/src/coreclr/vm/comthreadpool.cpp b/src/coreclr/vm/comthreadpool.cpp index f3aac0e4db8d..578f30e4f3d1 100644 --- a/src/coreclr/vm/comthreadpool.cpp +++ b/src/coreclr/vm/comthreadpool.cpp @@ -150,7 +150,7 @@ FCIMPL4(INT32, ThreadPoolNative::GetNextConfigUInt32Value, [=](const CLRConfig::ConfigDWORDInfo &configInfo, bool isBoolean, const WCHAR *appContextConfigName) -> bool { bool wasNotConfigured = true; - *configValueRef = CLRConfig::GetConfigValue(configInfo, true /* acceptExplicitDefaultFromRegutil */, &wasNotConfigured); + *configValueRef = CLRConfig::GetConfigValue(configInfo, &wasNotConfigured); if (wasNotConfigured) { return false; diff --git a/src/coreclr/vm/eeconfig.cpp b/src/coreclr/vm/eeconfig.cpp index 6d5ea5a3d267..5dd61484cd48 100644 --- a/src/coreclr/vm/eeconfig.cpp +++ b/src/coreclr/vm/eeconfig.cpp @@ -123,6 +123,7 @@ HRESULT EEConfig::Init() fNgenBindOptimizeNonGac = false; fStressLog = false; + fForceEnc = false; fProbeForStackOverflow = true; INDEBUG(fStressLog = true;) @@ -150,8 +151,6 @@ HRESULT EEConfig::Init() pPerfTypesToLog = NULL; iFastGCStress = 0; iInjectFatalError = 0; - fSaveThreadInfo = FALSE; - dwSaveThreadInfoMask = (DWORD)-1; #ifdef TEST_DATA_CONSISTENCY // indicates whether to run the self test to determine that we are detecting when a lock is held by the // LS in DAC builds. Initialized via the environment variable TestDataConsistency @@ -180,6 +179,7 @@ HRESULT EEConfig::Init() fSuppressChecks = false; fConditionalContracts = false; fEnableFullDebug = false; + iExposeExceptionsInCOM = 0; #endif #ifdef FEATURE_DOUBLE_ALIGNMENT_HINT @@ -201,7 +201,6 @@ HRESULT EEConfig::Init() #ifdef _DEBUG // interop logging - m_pTraceIUnknown = NULL; m_TraceWrapper = 0; #endif @@ -343,81 +342,6 @@ HRESULT EEConfig::Cleanup() return S_OK; } - -// -// NOTE: This function is deprecated; use the CLRConfig class instead. -// To use the CLRConfig class, add an entry in file:../inc/CLRConfigValues.h. -// -HRESULT EEConfig::GetConfigString_DontUse_(__in_z LPCWSTR name, __deref_out_z LPWSTR *outVal, BOOL fPrependCOMPLUS) -{ - CONTRACT(HRESULT) { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - INJECT_FAULT (CONTRACT_RETURN E_OUTOFMEMORY); - PRECONDITION(CheckPointer(name)); - POSTCONDITION(CheckPointer(outVal, NULL_OK)); - } CONTRACT_END; - - *outVal = REGUTIL::GetConfigString_DontUse_(name, fPrependCOMPLUS); - - RETURN S_OK; -} - - -// -// NOTE: This function is deprecated; use the CLRConfig class instead. -// To use the CLRConfig class, add an entry in file:../inc/CLRConfigValues.h. -// -DWORD EEConfig::GetConfigDWORD_DontUse_(__in_z LPCWSTR name, DWORD defValue, DWORD level, BOOL fPrependCOMPLUS) -{ - CONTRACTL { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(CheckPointer(name)); - } CONTRACTL_END; - - // @TODO: After everyone has moved off registry, key remove the following line in golden - return REGUTIL::GetConfigDWORD_DontUse_(name, defValue, (REGUTIL::CORConfigLevel)level, fPrependCOMPLUS); -} - -// -// NOTE: This function is deprecated; use the CLRConfig class instead. -// To use the CLRConfig class, add an entry in file:../inc/CLRConfigValues.h. -// -// Note for PAL: right now PAL does not have a _wcstoui64 API, so I am temporarily reading in all numbers as -// a 32-bit number. When we have the _wcstoui64 API on MAC we will use that instead of wcstoul. -ULONGLONG EEConfig::GetConfigULONGLONG_DontUse_(__in_z LPCWSTR name, ULONGLONG defValue, DWORD level, BOOL fPrependCOMPLUS) -{ - CONTRACTL { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(CheckPointer(name)); - } CONTRACTL_END; - - // @TODO: After everyone has moved off registry, key remove the following line in golden - return REGUTIL::GetConfigULONGLONG_DontUse_(name, defValue, (REGUTIL::CORConfigLevel)level, fPrependCOMPLUS); -} - -// -// NOTE: This function is deprecated; use the CLRConfig class instead. -// To use the CLRConfig class, add an entry in file:../inc/CLRConfigValues.h. -// -DWORD EEConfig::GetConfigDWORDInternal_DontUse_(__in_z LPCWSTR name, DWORD defValue, DWORD level, BOOL fPrependCOMPLUS) -{ - CONTRACTL { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(CheckPointer(name)); - } CONTRACTL_END; - - // @TODO: After everyone has moved off registry, key remove the following line in golden - return REGUTIL::GetConfigDWORD_DontUse_(name, defValue, (REGUTIL::CORConfigLevel)level, fPrependCOMPLUS); -} - /**************************************************************/ HRESULT EEConfig::sync() @@ -436,10 +360,10 @@ HRESULT EEConfig::sync() // Note the global variable is not updated directly by the GetRegKey function // so we only update it once (to avoid reentrancy windows) -fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TrackDynamicMethodDebugInfo); + fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TrackDynamicMethodDebugInfo); #ifdef _DEBUG - iFastGCStress = GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_FastGCStress, iFastGCStress); + iFastGCStress = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_FastGCStress); IfFailRet(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GcCoverage, (LPWSTR*)&pszGcCoverageOnMethod)); pszGcCoverageOnMethod = NarrowWideChar((LPWSTR)pszGcCoverageOnMethod); @@ -527,7 +451,7 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ #ifdef VERIFY_HEAP if (bGCStressAndHeapVerifyAllowed) { - iGCHeapVerify = GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_HeapVerify, iGCHeapVerify); + iGCHeapVerify = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_HeapVerify); } #endif @@ -552,21 +476,10 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ iGCAllowVeryLargeObjects = (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_gcAllowVeryLargeObjects) != 0); #endif - fGCBreakOnOOM = (GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_GCBreakOnOOM, fGCBreakOnOOM) != 0); - -#ifdef TRACE_GC - iGCtraceStart = GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_GCtraceStart, iGCtraceStart); - iGCtraceEnd = GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_GCtraceEnd, iGCtraceEnd); - iGCtraceFac = GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_GCtraceFacility, iGCtraceFac); - iGCprnLvl = GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_GCprnLvl, iGCprnLvl); -#endif + fGCBreakOnOOM = (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_GCBreakOnOOM) != 0); #ifdef _DEBUG - iInjectFatalError = GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_InjectFatalError, iInjectFatalError); - - fSaveThreadInfo = (GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_SaveThreadInfo, fSaveThreadInfo) != 0); - - dwSaveThreadInfoMask = GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_SaveThreadInfoMask, dwSaveThreadInfoMask); + iInjectFatalError = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_InjectFatalError); { LPWSTR wszSkipGCCoverageList = NULL; @@ -581,8 +494,8 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ IfFailRet(hr); } #endif - fStressLog = GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_StressLog, fStressLog) != 0; - fForceEnc = GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_ForceEnc, fForceEnc) != 0; + fStressLog = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_StressLog, fStressLog) != 0; + fForceEnc = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ForceEnc) != 0; { NewArrayHolder wszModifiableAssemblies; @@ -591,7 +504,7 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ fDebugAssembliesModifiable = _wcsicmp(wszModifiableAssemblies, W("debug")) == 0; } - iRequireZaps = RequireZapsType(GetConfigDWORD_DontUse_(CLRConfig::EXTERNAL_ZapRequire, iRequireZaps)); + iRequireZaps = RequireZapsType(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_ZapRequire, iRequireZaps)); if (IsCompilationProcess() || iRequireZaps >= REQUIRE_ZAPS_COUNT) iRequireZaps = REQUIRE_ZAPS_NONE; @@ -644,7 +557,7 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ #endif #ifdef FEATURE_DOUBLE_ALIGNMENT_HINT - DoubleArrayToLargeObjectHeapThreshold = GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_DoubleArrayToLargeObjectHeap, DoubleArrayToLargeObjectHeapThreshold); + DoubleArrayToLargeObjectHeapThreshold = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_DoubleArrayToLargeObjectHeap, DoubleArrayToLargeObjectHeapThreshold); #endif IfFailRet(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ZapBBInstr, (LPWSTR*)&szZapBBInstr)); @@ -669,7 +582,7 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ g_IBCLogger.DisableAllInstr(); - dwDisableStackwalkCache = GetConfigDWORD_DontUse_(CLRConfig::EXTERNAL_DisableStackwalkCache, dwDisableStackwalkCache); + dwDisableStackwalkCache = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_DisableStackwalkCache, dwDisableStackwalkCache); #ifdef _DEBUG @@ -695,9 +608,9 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ dwJitHostMaxSlabCache = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_JitHostMaxSlabCache); - fJitFramed = (GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_JitFramed, fJitFramed) != 0); - fJitMinOpts = (GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_JITMinOpts, fJitMinOpts) == 1); - iJitOptimizeType = GetConfigDWORD_DontUse_(CLRConfig::EXTERNAL_JitOptimizeType, iJitOptimizeType); + fJitFramed = (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_JitFramed) != 0); + fJitMinOpts = (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_JITMinOpts) == 1); + iJitOptimizeType = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_JitOptimizeType); if (iJitOptimizeType > OPT_RANDOM) iJitOptimizeType = OPT_DEFAULT; #ifdef TARGET_X86 @@ -706,7 +619,7 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ #ifdef _DEBUG - fDebuggable = (GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_JitDebuggable, fDebuggable) != 0); + fDebuggable = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_JitDebuggable) != 0); LPWSTR wszPreStubStuff = NULL; @@ -741,25 +654,25 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ IfFailRet(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnStructMarshalSetup, (LPWSTR*)&pszBreakOnStructMarshalSetup)); pszBreakOnStructMarshalSetup = NarrowWideChar((LPWSTR)pszBreakOnStructMarshalSetup); - m_fAssertOnBadImageFormat = (GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_AssertOnBadImageFormat, m_fAssertOnBadImageFormat) != 0); - m_fAssertOnFailFast = (GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_AssertOnFailFast, m_fAssertOnFailFast) != 0); + m_fAssertOnBadImageFormat = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_AssertOnBadImageFormat) != 0); + m_fAssertOnFailFast = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_AssertOnFailFast) != 0); - fSuppressChecks = (GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_SuppressChecks, fSuppressChecks) != 0); + fSuppressChecks = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_SuppressChecks) != 0); CHECK::SetAssertEnforcement(!fSuppressChecks); - fConditionalContracts = (GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_ConditionalContracts, fConditionalContracts) != 0); + fConditionalContracts = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ConditionalContracts) != 0); #ifdef ENABLE_CONTRACTS_IMPL Contract::SetUnconditionalContractEnforcement(!fConditionalContracts); #endif - fEnableFullDebug = (GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_EnableFullDebug, fEnableFullDebug) != 0); + fEnableFullDebug = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EnableFullDebug) != 0); - fVerifierOff = (GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_VerifierOff, fVerifierOff) != 0); + fVerifierOff = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_VerifierOff) != 0); - fJitVerificationDisable = (GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_JitVerificationDisable, fJitVerificationDisable) != 0); + fJitVerificationDisable = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_JitVerificationDisable) != 0); - iExposeExceptionsInCOM = GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_ExposeExceptionsInCOM, iExposeExceptionsInCOM); + iExposeExceptionsInCOM = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ExposeExceptionsInCOM, iExposeExceptionsInCOM); #endif #ifdef FEATURE_COMINTEROP @@ -772,7 +685,7 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ #endif // FEATURE_COMINTEROP #ifdef _DEBUG - fExpandAllOnLoad = (GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_ExpandAllOnLoad, fExpandAllOnLoad) != 0); + fExpandAllOnLoad = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ExpandAllOnLoad) != 0); #endif //_DEBUG #ifdef ENABLE_STARTUP_DELAY @@ -803,19 +716,7 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ fInited = true; #ifdef _DEBUG - m_pTraceIUnknown = (IUnknown*)(DWORD_PTR)(GetConfigDWORD_DontUse_(CLRConfig::EXTERNAL_TraceIUnknown, (DWORD)(DWORD_PTR)(m_pTraceIUnknown))); // WIN64 - conversion from DWORD to IUnknown* of greater size - m_TraceWrapper = GetConfigDWORD_DontUse_(CLRConfig::EXTERNAL_TraceWrap, m_TraceWrapper); - - // can't have both - if (m_pTraceIUnknown != 0) - { - m_TraceWrapper = 0; - } - else - if (m_TraceWrapper != 0) - { - m_pTraceIUnknown = (IUnknown*)-1; - } + m_TraceWrapper = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_TraceWrap); #endif #ifdef _DEBUG @@ -849,7 +750,7 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ #endif #if defined(_DEBUG) && defined(STUBLINKER_GENERATES_UNWIND_INFO) - fStubLinkerUnwindInfoVerificationOn = (GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_StubLinkerUnwindInfoVerificationOn, fStubLinkerUnwindInfoVerificationOn) != 0); + fStubLinkerUnwindInfoVerificationOn = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_StubLinkerUnwindInfoVerificationOn) != 0); #endif if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_UseMethodDataCache) != 0) { @@ -862,7 +763,7 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ #if defined(_DEBUG) && defined(TARGET_AMD64) - m_cGenerateLongJumpDispatchStubRatio = GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_GenerateLongJumpDispatchStubRatio, + m_cGenerateLongJumpDispatchStubRatio = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GenerateLongJumpDispatchStubRatio, static_cast(m_cGenerateLongJumpDispatchStubRatio)); #endif diff --git a/src/coreclr/vm/eeconfig.h b/src/coreclr/vm/eeconfig.h index 11f1d286d9f9..b4941fd0bc2d 100644 --- a/src/coreclr/vm/eeconfig.h +++ b/src/coreclr/vm/eeconfig.h @@ -417,22 +417,11 @@ class EEConfig LIMITED_METHOD_CONTRACT; return iInjectFatalError; } - - inline BOOL SaveThreadInfo() const - { - return fSaveThreadInfo; - } - - inline DWORD SaveThreadInfoMask() const - { - return dwSaveThreadInfoMask; - } #endif #ifdef _DEBUG // Interop config - IUnknown* GetTraceIUnknown() const {LIMITED_METHOD_CONTRACT; return m_pTraceIUnknown; } int GetTraceWrapper() const {LIMITED_METHOD_CONTRACT; return m_TraceWrapper; } #endif @@ -469,36 +458,6 @@ class EEConfig HRESULT sync(); // check the registry again and update local state - // Helpers to read configuration - - // - // NOTE: The following function is deprecated; use the CLRConfig class instead. - // To access a configuration value through CLRConfig, add an entry in file:../inc/CLRConfigValues.h. - // - static HRESULT GetConfigString_DontUse_(__in_z LPCWSTR name, __deref_out_z LPWSTR*out, BOOL fPrependCOMPLUS = TRUE); // Note that you own the returned string! - - // - // NOTE: The following function is deprecated; use the CLRConfig class instead. - // To access a configuration value through CLRConfig, add an entry in file:../inc/CLRConfigValues.h. - // - static DWORD GetConfigDWORD_DontUse_(__in_z LPCWSTR name, DWORD defValue, - DWORD level=(DWORD) REGUTIL::COR_CONFIG_ALL, - BOOL fPrependCOMPLUS = TRUE); - - // - // NOTE: The following function is deprecated; use the CLRConfig class instead. - // To access a configuration value through CLRConfig, add an entry in file:../inc/CLRConfigValues.h. - // - static ULONGLONG GetConfigULONGLONG_DontUse_(__in_z LPCWSTR name, ULONGLONG defValue, - DWORD level=(DWORD) REGUTIL::COR_CONFIG_ALL, - BOOL fPrependCOMPLUS = TRUE); - - // - // NOTE: The following function is deprecated; use the CLRConfig class instead. - // To access a configuration value through CLRConfig, add an entry in file:../inc/CLRConfigValues.h. - // - static DWORD GetConfigFlag_DontUse_(__in_z LPCWSTR name, DWORD bitToSet, bool defValue = FALSE); - #ifdef _DEBUG // GC alloc logging bool ShouldLogAlloc(const char *pClass) const { LIMITED_METHOD_CONTRACT; return pPerfTypesToLog && pPerfTypesToLog->IsInList(pClass);} @@ -660,9 +619,6 @@ class EEConfig DWORD iInjectFatalError; - BOOL fSaveThreadInfo; - DWORD dwSaveThreadInfoMask; - AssemblyNamesList *pSkipGCCoverageList; #endif @@ -703,7 +659,6 @@ class EEConfig #ifdef _DEBUG // interop logging - IUnknown* m_pTraceIUnknown; int m_TraceWrapper; #endif @@ -764,10 +719,6 @@ class EEConfig #endif public: - DWORD GetConfigDWORDInternal_DontUse_ (__in_z LPCWSTR name, DWORD defValue, //for getting data in the constructor of EEConfig - DWORD level=(DWORD) REGUTIL::COR_CONFIG_ALL, - BOOL fPrependCOMPLUS = TRUE); - enum BitForMask { CallSite_1 = 0x0001, CallSite_2 = 0x0002, diff --git a/src/coreclr/vm/i386/cgenx86.cpp b/src/coreclr/vm/i386/cgenx86.cpp index caab476b15fa..c8a38e2f3b3f 100644 --- a/src/coreclr/vm/i386/cgenx86.cpp +++ b/src/coreclr/vm/i386/cgenx86.cpp @@ -109,7 +109,7 @@ void GetSpecificCpuInfo(CORINFO_CPU * cpuInfo) const DWORD cpuDefault = 0xFFFFFFFF; static ConfigDWORD cpuFamily; - DWORD configCpuFamily = cpuFamily.val_DontUse_(CLRConfig::INTERNAL_CPUFamily, cpuDefault); + DWORD configCpuFamily = cpuFamily.val(CLRConfig::INTERNAL_CPUFamily); if (configCpuFamily != cpuDefault) { assert((configCpuFamily & 0xFFF) == configCpuFamily); @@ -125,7 +125,7 @@ void GetSpecificCpuInfo(CORINFO_CPU * cpuInfo) const DWORD cpuFeaturesDefault = 0xFFFFFFFF; static ConfigDWORD cpuFeatures; - DWORD configCpuFeatures = cpuFeatures.val_DontUse_(CLRConfig::INTERNAL_CPUFeatures, cpuFeaturesDefault); + DWORD configCpuFeatures = cpuFeatures.val(CLRConfig::INTERNAL_CPUFeatures); if (configCpuFeatures != cpuFeaturesDefault) { tempVal.dwFeatures = configCpuFeatures; diff --git a/src/coreclr/vm/interoputil.cpp b/src/coreclr/vm/interoputil.cpp index a2d46af6cf61..738918911880 100644 --- a/src/coreclr/vm/interoputil.cpp +++ b/src/coreclr/vm/interoputil.cpp @@ -3890,13 +3890,12 @@ void InitializeComInterop() //------------------------------------------------------------------- static int g_TraceCount = 0; -static IUnknown* g_pTraceIUnknown = 0; +static IUnknown* g_pTraceIUnknown = NULL; VOID IntializeInteropLogging() { WRAPPER_NO_CONTRACT; - g_pTraceIUnknown = g_pConfig->GetTraceIUnknown(); g_TraceCount = g_pConfig->GetTraceWrapper(); } diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index 7ad02600f11e..f14d3e2c0202 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -9011,7 +9011,7 @@ MethodTableBuilder::LoadExactInterfaceMap(MethodTable *pMT) pMT->GetAssembly()->ThrowTypeLoadException(pMT->GetMDImport(), pMT->GetCl(), IDS_CLASSLOAD_BADFORMAT); } #ifdef _DEBUG - duplicates |= EEConfig::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_AlwaysUseMetadataInterfaceMapLayout, FALSE); + duplicates |= CLRConfig::GetConfigValue(CLRConfig::INTERNAL_AlwaysUseMetadataInterfaceMapLayout); //#InjectInterfaceDuplicates_LoadExactInterfaceMap // If we are injecting duplicates also for non-generic interfaces in check builds, we have to use diff --git a/src/coreclr/vm/stackingallocator.cpp b/src/coreclr/vm/stackingallocator.cpp index 911132afc86d..8806bceabb2f 100644 --- a/src/coreclr/vm/stackingallocator.cpp +++ b/src/coreclr/vm/stackingallocator.cpp @@ -26,21 +26,8 @@ #include "common.h" #include "excep.h" - -#if 0 -#define INC_COUNTER(_name, _amount) do { \ - unsigned _count = REGUTIL::GetLong(W("AllocCounter_") _name, 0, NULL, HKEY_CURRENT_USER); \ - REGUTIL::SetLong(W("AllocCounter_") _name, _count+(_amount), NULL, HKEY_CURRENT_USER); \ - } while (0) -#define MAX_COUNTER(_name, _amount) do { \ - unsigned _count = REGUTIL::GetLong(W("AllocCounter_") _name, 0, NULL, HKEY_CURRENT_USER); \ - REGUTIL::SetLong(W("AllocCounter_") _name, max(_count, (_amount)), NULL, HKEY_CURRENT_USER); \ - } while (0) -#else #define INC_COUNTER(_name, _amount) #define MAX_COUNTER(_name, _amount) -#endif - StackingAllocator::StackingAllocator() { diff --git a/src/coreclr/vm/stublink.cpp b/src/coreclr/vm/stublink.cpp index 00d5c84c9db2..261cd15f7607 100644 --- a/src/coreclr/vm/stublink.cpp +++ b/src/coreclr/vm/stublink.cpp @@ -1343,7 +1343,7 @@ bool StubLinker::EmitUnwindInfo(Stub* pStub, int globalsize, LoaderHeap* pHeap) #ifdef _DEBUG static SIZE_T MaxSegmentSize = -1; if (MaxSegmentSize == (SIZE_T)-1) - MaxSegmentSize = EEConfig::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_MaxStubUnwindInfoSegmentSize, DYNAMIC_FUNCTION_TABLE_MAX_RANGE); + MaxSegmentSize = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MaxStubUnwindInfoSegmentSize, DYNAMIC_FUNCTION_TABLE_MAX_RANGE); #else const SIZE_T MaxSegmentSize = DYNAMIC_FUNCTION_TABLE_MAX_RANGE; #endif diff --git a/src/coreclr/zap/zaplog.h b/src/coreclr/zap/zaplog.h index e07cb29e51e3..a9f5e2e27798 100644 --- a/src/coreclr/zap/zaplog.h +++ b/src/coreclr/zap/zaplog.h @@ -27,7 +27,7 @@ inline void ThrowAndLog(HRESULT hr, INDEBUG_COMMA(__in_z const char * szMsg) IND // Log failures when StressLog is on static ConfigDWORD g_iStressLog; - BOOL bLog = g_iStressLog.val_DontUse_(CLRConfig::UNSUPPORTED_StressLog, 0); + BOOL bLog = g_iStressLog.val(CLRConfig::UNSUPPORTED_StressLog); if (bLog) { #ifdef _DEBUG diff --git a/src/coreclr/zap/zapper.cpp b/src/coreclr/zap/zapper.cpp index a98fc6cc2934..248e82e1b0d2 100644 --- a/src/coreclr/zap/zapper.cpp +++ b/src/coreclr/zap/zapper.cpp @@ -1155,7 +1155,7 @@ void Zapper::InitializeCompilerFlags(CORCOMPILE_VERSION_INFO * pVersionInfo) // Set CORJIT_FLAG_MIN_OPT only if COMPlus_JitMinOpts == 1 static ConfigDWORD g_jitMinOpts; - if (g_jitMinOpts.val_DontUse_(CLRConfig::UNSUPPORTED_JITMinOpts, 0) == 1) + if (g_jitMinOpts.val(CLRConfig::UNSUPPORTED_JITMinOpts) == 1) { m_pOpt->m_compilerFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_MIN_OPT); } From aece15768033c95f90d84d111b490085269e188d Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 29 Mar 2021 18:10:00 -0400 Subject: [PATCH 26/98] Remove unnecessary delegate/closure from NtProcessInfoHelper on Windows (#50387) --- .../src/System/Diagnostics/ProcessManager.Win32.cs | 6 +++--- .../src/System/Diagnostics/ProcessManager.Windows.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Win32.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Win32.cs index 18243a75c567..53c9c743de85 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Win32.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Win32.cs @@ -252,7 +252,7 @@ internal static class NtProcessInfoHelper private const int DefaultCachedBufferSize = 128 * 1024; #endif - internal static ProcessInfo[] GetProcessInfos(Predicate? processIdFilter = null) + internal static ProcessInfo[] GetProcessInfos(int? processIdFilter = null) { ProcessInfo[] processInfos; @@ -342,7 +342,7 @@ private static int GetNewBufferSize(int existingBufferSize, int requiredSize) return newSize; } - private static unsafe ProcessInfo[] GetProcessInfos(ReadOnlySpan data, Predicate? processIdFilter) + private static unsafe ProcessInfo[] GetProcessInfos(ReadOnlySpan data, int? processIdFilter) { // Use a dictionary to avoid duplicate entries if any // 60 is a reasonable number for processes on a normal machine. @@ -356,7 +356,7 @@ private static unsafe ProcessInfo[] GetProcessInfos(ReadOnlySpan data, Pre // Process ID shouldn't overflow. OS API GetCurrentProcessID returns DWORD. int processInfoProcessId = pi.UniqueProcessId.ToInt32(); - if (processIdFilter == null || processIdFilter(processInfoProcessId)) + if (processIdFilter == null || processIdFilter.GetValueOrDefault() == processInfoProcessId) { // get information for a process ProcessInfo processInfo = new ProcessInfo((int)pi.NumberOfThreads) diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Windows.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Windows.cs index 98075165c63a..103443d7f232 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Windows.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Windows.cs @@ -75,7 +75,7 @@ public static ProcessInfo[] GetProcessInfos(string machineName) else { // local case: do not use performance counter and also attempt to get the matching (by pid) process only - ProcessInfo[] processInfos = NtProcessInfoHelper.GetProcessInfos(pid => pid == processId); + ProcessInfo[] processInfos = NtProcessInfoHelper.GetProcessInfos(processId); if (processInfos.Length == 1) { return processInfos[0]; From a0d882641068c51146f1675806f6372d8ab0a3a5 Mon Sep 17 00:00:00 2001 From: Steve Molloy Date: Mon, 29 Mar 2021 15:46:54 -0700 Subject: [PATCH 27/98] Don't throw when previous enum mapping is not found. (#49892) --- .../XmlSerializationReaderILGen.cs | 2 +- ...tem.Runtime.Serialization.Xml.Tests.csproj | 7 +- .../tests/XmlSerializerTests.cs | 66 +++++++++++++++++++ 3 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 src/libraries/System.Runtime.Serialization.Xml/tests/XmlSerializerTests.cs diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReaderILGen.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReaderILGen.cs index e80a951c76d3..feb269c3d752 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReaderILGen.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReaderILGen.cs @@ -936,7 +936,7 @@ private void WritePrimitive(TypeMapping mapping, string source) { i++; uniqueName = name + i.ToString(CultureInfo.InvariantCulture); - m = Enums[uniqueName]; + Enums.TryGetValue(uniqueName, out m); } } Enums.Add(uniqueName, mapping); diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/System.Runtime.Serialization.Xml.Tests.csproj b/src/libraries/System.Runtime.Serialization.Xml/tests/System.Runtime.Serialization.Xml.Tests.csproj index 3080bd813ac9..3992b33bcfa3 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/System.Runtime.Serialization.Xml.Tests.csproj +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/System.Runtime.Serialization.Xml.Tests.csproj @@ -15,10 +15,8 @@ - - + + @@ -29,6 +27,7 @@ + diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XmlSerializerTests.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XmlSerializerTests.cs new file mode 100644 index 000000000000..089e45de1131 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XmlSerializerTests.cs @@ -0,0 +1,66 @@ +// 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.IO; +using System.Xml; +using System.Xml.Serialization; +using Xunit; + +public static class XmlSerializerTests +{ + + public const string FakeNS = "http://example.com/XmlSerializerTests"; + + [Fact] + public static void FlagEnums_With_Different_Namespaces() + { + StringWriter sw = new StringWriter(); + XmlTextWriter xml = new XmlTextWriter(sw); + + TwoClasses twoClasses = new TwoClasses + { + First = new FirstClass { TestingEnumValues = TestEnum.First }, + Second = new SecondClass { TestingEnumValues = TestEnum.Second } + }; + + // 43675 - This line throws with inconsistent Flag.type/namespace usage + XmlSerializer ser = new XmlSerializer(typeof(TwoClasses)); + + ser.Serialize(xml, twoClasses); + string s = sw.ToString(); + + Assert.Contains("enumtest", s); + } + + [Flags] + public enum TestEnum + { + First = 1, + Second = 2 + } + + public class FirstClass + { + [XmlAttribute("enumtest")] + public TestEnum TestingEnumValues; + } + + public class SecondClass + { + [XmlAttribute("enumtest", Namespace = XmlSerializerTests.FakeNS)] + public TestEnum TestingEnumValues; + } + + public class TwoClasses + { + public TwoClasses() { } + + [XmlElement("first")] + public FirstClass First; + + [XmlElement("second", Namespace = XmlSerializerTests.FakeNS)] + public SecondClass Second; + } + +} From 857fe5b3f079b59d45cdf3d3a2050c35c4a9d370 Mon Sep 17 00:00:00 2001 From: Vladimir Sadov Date: Mon, 29 Mar 2021 16:16:14 -0700 Subject: [PATCH 28/98] Singlefile compression of native files (#49855) * versioning * no need to support 2.0 bundle version * enable compression * fixes after rebase * build fix for Unix * Fix build with GCC * disable a test temporarily to see what else breaks * Add EnableCompression option to Bundler + PR feedback * Couple more places should use version 6.0 * PR feedback (header versioning, more tests, fixed an assert) * Suggestion from PR review Co-authored-by: Vitek Karas * sorted usings * should be bundle_major_version in two more places. * More PR feedback Co-authored-by: Vitek Karas --- .../Bundle/BundleOptions.cs | 1 + .../Microsoft.NET.HostModel/Bundle/Bundler.cs | 78 ++++++++++++++++--- .../Bundle/FileEntry.cs | 16 +++- .../Bundle/Manifest.cs | 28 +++---- .../Bundle/TargetInfo.cs | 18 +++-- .../BundleExtractToSpecificPath.cs | 16 ++-- .../AppHost.Bundle.Tests/BundleTestBase.cs | 22 +++++- .../BundledAppWithSubDirs.cs | 62 ++++++++++++++- .../NetCoreApp3CompatModeTests.cs | 4 +- .../SingleFileApiTests.cs | 4 +- .../Helpers/BundleHelper.cs | 2 +- .../corehost/apphost/static/CMakeLists.txt | 4 + src/native/corehost/bundle/extractor.cpp | 71 ++++++++++++++++- src/native/corehost/bundle/file_entry.cpp | 19 ++++- src/native/corehost/bundle/file_entry.h | 7 +- src/native/corehost/bundle/header.cpp | 6 +- src/native/corehost/bundle/header.h | 12 ++- src/native/corehost/bundle/info.cpp | 5 +- src/native/corehost/bundle/manifest.cpp | 2 +- src/native/corehost/hostmisc/utils.h | 5 +- 20 files changed, 309 insertions(+), 73 deletions(-) diff --git a/src/installer/managed/Microsoft.NET.HostModel/Bundle/BundleOptions.cs b/src/installer/managed/Microsoft.NET.HostModel/Bundle/BundleOptions.cs index 646e1b094310..22030386d267 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/Bundle/BundleOptions.cs +++ b/src/installer/managed/Microsoft.NET.HostModel/Bundle/BundleOptions.cs @@ -17,5 +17,6 @@ public enum BundleOptions BundleOtherFiles = 2, BundleSymbolFiles = 4, BundleAllContent = BundleNativeBinaries | BundleOtherFiles, + EnableCompression = 8, }; } diff --git a/src/installer/managed/Microsoft.NET.HostModel/Bundle/Bundler.cs b/src/installer/managed/Microsoft.NET.HostModel/Bundle/Bundler.cs index 0505d2dcefbf..bee21ddc065d 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/Bundle/Bundler.cs +++ b/src/installer/managed/Microsoft.NET.HostModel/Bundle/Bundler.cs @@ -1,14 +1,15 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.NET.HostModel.AppHost; using System; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; using System.IO; +using System.IO.Compression; +using System.Linq; using System.Reflection.PortableExecutable; using System.Runtime.InteropServices; +using Microsoft.NET.HostModel.AppHost; namespace Microsoft.NET.HostModel.Bundle { @@ -18,6 +19,9 @@ namespace Microsoft.NET.HostModel.Bundle /// public class Bundler { + public const uint BundlerMajorVersion = 6; + public const uint BundlerMinorVersion = 0; + private readonly string HostName; private readonly string OutputDir; private readonly string DepsJson; @@ -44,22 +48,72 @@ public Bundler(string hostName, OutputDir = Path.GetFullPath(string.IsNullOrEmpty(outputDir) ? Environment.CurrentDirectory : outputDir); Target = new TargetInfo(targetOS, targetArch, targetFrameworkVersion); + if (Target.BundleMajorVersion < 6 && + (options & BundleOptions.EnableCompression) != 0) + { + throw new ArgumentException("Compression requires framework version 6.0 or above", nameof(options)); + } + appAssemblyName ??= Target.GetAssemblyName(hostName); DepsJson = appAssemblyName + ".deps.json"; RuntimeConfigJson = appAssemblyName + ".runtimeconfig.json"; RuntimeConfigDevJson = appAssemblyName + ".runtimeconfig.dev.json"; - BundleManifest = new Manifest(Target.BundleVersion, netcoreapp3CompatMode: options.HasFlag(BundleOptions.BundleAllContent)); + BundleManifest = new Manifest(Target.BundleMajorVersion, netcoreapp3CompatMode: options.HasFlag(BundleOptions.BundleAllContent)); Options = Target.DefaultOptions | options; } + private bool ShouldCompress(FileType type) + { + if (!Options.HasFlag(BundleOptions.EnableCompression)) + { + return false; + } + + switch (type) + { + case FileType.Symbols: + case FileType.NativeBinary: + return true; + + default: + return false; + } + } + /// /// Embed 'file' into 'bundle' /// - /// Returns the offset of the start 'file' within 'bundle' - - private long AddToBundle(Stream bundle, Stream file, FileType type) + /// + /// startOffset: offset of the start 'file' within 'bundle' + /// compressedSize: size of the compressed data, if entry was compressed, otherwise 0 + /// + private (long startOffset, long compressedSize) AddToBundle(Stream bundle, Stream file, FileType type) { + long startOffset = bundle.Position; + if (ShouldCompress(type)) + { + long fileLength = file.Length; + file.Position = 0; + + // We use DeflateStream here. + // It uses GZip algorithm, but with a trivial header that does not contain file info. + using (DeflateStream compressionStream = new DeflateStream(bundle, CompressionLevel.Optimal, leaveOpen: true)) + { + file.CopyTo(compressionStream); + } + + long compressedSize = bundle.Position - startOffset; + if (compressedSize < fileLength * 0.75) + { + return (startOffset, compressedSize); + } + + // compression rate was not good enough + // roll back the bundle offset and let the uncompressed code path take care of the entry. + bundle.Seek(startOffset, SeekOrigin.Begin); + } + if (type == FileType.Assembly) { long misalignment = (bundle.Position % Target.AssemblyAlignment); @@ -72,10 +126,10 @@ private long AddToBundle(Stream bundle, Stream file, FileType type) } file.Position = 0; - long startOffset = bundle.Position; + startOffset = bundle.Position; file.CopyTo(bundle); - return startOffset; + return (startOffset, 0); } private bool IsHost(string fileRelativePath) @@ -186,8 +240,8 @@ private FileType InferType(FileSpec fileSpec) /// public string GenerateBundle(IReadOnlyList fileSpecs) { - Tracer.Log($"Bundler version: {Manifest.CurrentVersion}"); - Tracer.Log($"Bundler Header: {BundleManifest.DesiredVersion}"); + Tracer.Log($"Bundler Version: {BundlerMajorVersion}.{BundlerMinorVersion}"); + Tracer.Log($"Bundle Version: {BundleManifest.BundleVersion}"); Tracer.Log($"Target Runtime: {Target}"); Tracer.Log($"Bundler Options: {Options}"); @@ -254,8 +308,8 @@ public string GenerateBundle(IReadOnlyList fileSpecs) using (FileStream file = File.OpenRead(fileSpec.SourcePath)) { FileType targetType = Target.TargetSpecificFileType(type); - long startOffset = AddToBundle(bundle, file, targetType); - FileEntry entry = BundleManifest.AddEntry(targetType, relativePath, startOffset, file.Length); + (long startOffset, long compressedSize) = AddToBundle(bundle, file, targetType); + FileEntry entry = BundleManifest.AddEntry(targetType, relativePath, startOffset, file.Length, compressedSize, Target.BundleMajorVersion); Tracer.Log($"Embed: {entry}"); } } diff --git a/src/installer/managed/Microsoft.NET.HostModel/Bundle/FileEntry.cs b/src/installer/managed/Microsoft.NET.HostModel/Bundle/FileEntry.cs index 7315c2a02631..e71a7faaa457 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/Bundle/FileEntry.cs +++ b/src/installer/managed/Microsoft.NET.HostModel/Bundle/FileEntry.cs @@ -15,32 +15,44 @@ namespace Microsoft.NET.HostModel.Bundle /// * Name ("NameLength" Bytes) /// * Offset (Int64) /// * Size (Int64) + /// === present only in bundle version 3+ + /// * CompressedSize (Int64) 0 indicates No Compression /// public class FileEntry { + public readonly uint BundleMajorVersion; + public readonly long Offset; public readonly long Size; + public readonly long CompressedSize; public readonly FileType Type; public readonly string RelativePath; // Path of an embedded file, relative to the Bundle source-directory. public const char DirectorySeparatorChar = '/'; - public FileEntry(FileType fileType, string relativePath, long offset, long size) + public FileEntry(FileType fileType, string relativePath, long offset, long size, long compressedSize, uint bundleMajorVersion) { + BundleMajorVersion = bundleMajorVersion; Type = fileType; RelativePath = relativePath.Replace('\\', DirectorySeparatorChar); Offset = offset; Size = size; + CompressedSize = compressedSize; } public void Write(BinaryWriter writer) { writer.Write(Offset); writer.Write(Size); + // compression is used only in version 6.0+ + if (BundleMajorVersion >= 6) + { + writer.Write(CompressedSize); + } writer.Write((byte)Type); writer.Write(RelativePath); } - public override string ToString() => $"{RelativePath} [{Type}] @{Offset} Sz={Size}"; + public override string ToString() => $"{RelativePath} [{Type}] @{Offset} Sz={Size} CompressedSz={CompressedSize}"; } } diff --git a/src/installer/managed/Microsoft.NET.HostModel/Bundle/Manifest.cs b/src/installer/managed/Microsoft.NET.HostModel/Bundle/Manifest.cs index 0beddb2b8cac..73e7b3b10fd2 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/Bundle/Manifest.cs +++ b/src/installer/managed/Microsoft.NET.HostModel/Bundle/Manifest.cs @@ -66,32 +66,26 @@ private enum HeaderFlags : ulong // with path-names so that the AppHost can use it in // extraction path. public readonly string BundleID; - - public const uint CurrentMajorVersion = 2; - public readonly uint DesiredMajorVersion; + public readonly uint BundleMajorVersion; // The Minor version is currently unused, and is always zero - public const uint MinorVersion = 0; - - public static string CurrentVersion => $"{CurrentMajorVersion}.{MinorVersion}"; - public string DesiredVersion => $"{DesiredMajorVersion}.{MinorVersion}"; - + public const uint BundleMinorVersion = 0; private FileEntry DepsJsonEntry; private FileEntry RuntimeConfigJsonEntry; private HeaderFlags Flags; - public List Files; + public string BundleVersion => $"{BundleMajorVersion}.{BundleMinorVersion}"; - public Manifest(uint desiredVersion, bool netcoreapp3CompatMode = false) + public Manifest(uint bundleMajorVersion, bool netcoreapp3CompatMode = false) { - DesiredMajorVersion = desiredVersion; + BundleMajorVersion = bundleMajorVersion; Files = new List(); BundleID = Path.GetRandomFileName(); - Flags = (netcoreapp3CompatMode) ? HeaderFlags.NetcoreApp3CompatMode: HeaderFlags.None; + Flags = (netcoreapp3CompatMode) ? HeaderFlags.NetcoreApp3CompatMode : HeaderFlags.None; } - public FileEntry AddEntry(FileType type, string relativePath, long offset, long size) + public FileEntry AddEntry(FileType type, string relativePath, long offset, long size, long compressedSize, uint bundleMajorVersion) { - FileEntry entry = new FileEntry(type, relativePath, offset, size); + FileEntry entry = new FileEntry(type, relativePath, offset, size, compressedSize, bundleMajorVersion); Files.Add(entry); switch (entry.Type) @@ -118,12 +112,12 @@ public long Write(BinaryWriter writer) long startOffset = writer.BaseStream.Position; // Write the bundle header - writer.Write(DesiredMajorVersion); - writer.Write(MinorVersion); + writer.Write(BundleMajorVersion); + writer.Write(BundleMinorVersion); writer.Write(Files.Count); writer.Write(BundleID); - if (DesiredMajorVersion == 2) + if (BundleMajorVersion >= 2) { writer.Write((DepsJsonEntry != null) ? DepsJsonEntry.Offset : 0); writer.Write((DepsJsonEntry != null) ? DepsJsonEntry.Size : 0); diff --git a/src/installer/managed/Microsoft.NET.HostModel/Bundle/TargetInfo.cs b/src/installer/managed/Microsoft.NET.HostModel/Bundle/TargetInfo.cs index 05d5ff9d89ee..2d3f6566a5ad 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/Bundle/TargetInfo.cs +++ b/src/installer/managed/Microsoft.NET.HostModel/Bundle/TargetInfo.cs @@ -25,7 +25,7 @@ public class TargetInfo public readonly OSPlatform OS; public readonly Architecture Arch; public readonly Version FrameworkVersion; - public readonly uint BundleVersion; + public readonly uint BundleMajorVersion; public readonly BundleOptions DefaultOptions; public readonly int AssemblyAlignment; @@ -33,18 +33,23 @@ public TargetInfo(OSPlatform? os, Architecture? arch, Version targetFrameworkVer { OS = os ?? HostOS; Arch = arch ?? RuntimeInformation.OSArchitecture; - FrameworkVersion = targetFrameworkVersion ?? net50; + FrameworkVersion = targetFrameworkVersion ?? net60; Debug.Assert(IsLinux || IsOSX || IsWindows); - if (FrameworkVersion.CompareTo(net50) >= 0) + if (FrameworkVersion.CompareTo(net60) >= 0) { - BundleVersion = 2u; + BundleMajorVersion = 6u; + DefaultOptions = BundleOptions.None; + } + else if (FrameworkVersion.CompareTo(net50) >= 0) + { + BundleMajorVersion = 2u; DefaultOptions = BundleOptions.None; } else if (FrameworkVersion.Major == 3 && (FrameworkVersion.Minor == 0 || FrameworkVersion.Minor == 1)) { - BundleVersion = 1u; + BundleMajorVersion = 1u; DefaultOptions = BundleOptions.BundleAllContent; } else @@ -94,7 +99,7 @@ public override string ToString() // The .net core 3 apphost doesn't care about semantics of FileType -- all files are extracted at startup. // However, the apphost checks that the FileType value is within expected bounds, so set it to the first enumeration. - public FileType TargetSpecificFileType(FileType fileType) => (BundleVersion == 1) ? FileType.Unknown : fileType; + public FileType TargetSpecificFileType(FileType fileType) => (BundleMajorVersion == 1) ? FileType.Unknown : fileType; // In .net core 3.x, bundle processing happens within the AppHost. // Therefore HostFxr and HostPolicy can be bundled within the single-file app. @@ -105,6 +110,7 @@ public override string ToString() public bool ShouldExclude(string relativePath) => (FrameworkVersion.Major != 3) && (relativePath.Equals(HostFxr) || relativePath.Equals(HostPolicy)); + private readonly Version net60 = new Version(6, 0); private readonly Version net50 = new Version(5, 0); private string HostFxr => IsWindows ? "hostfxr.dll" : IsLinux ? "libhostfxr.so" : "libhostfxr.dylib"; private string HostPolicy => IsWindows ? "hostpolicy.dll" : IsLinux ? "libhostpolicy.so" : "libhostpolicy.dylib"; diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleExtractToSpecificPath.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleExtractToSpecificPath.cs index 1741c9584b32..33c895548b21 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleExtractToSpecificPath.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleExtractToSpecificPath.cs @@ -31,8 +31,8 @@ private void Bundle_Extraction_To_Specific_Path_Succeeds() var hostName = BundleHelper.GetHostName(fixture); // Publish the bundle - UseSingleFileSelfContainedHost(fixture); - Bundler bundler = BundleHelper.BundleApp(fixture, out string singleFile, options: BundleOptions.BundleNativeBinaries); + BundleOptions options = BundleOptions.BundleNativeBinaries; + Bundler bundler = BundleSelfContainedApp(fixture, out string singleFile, options); // Verify expected files in the bundle directory var bundleDir = BundleHelper.GetBundleDir(fixture); @@ -80,8 +80,7 @@ private void Bundle_Extraction_To_Relative_Path_Succeeds(string relativePath, Bu return; var fixture = sharedTestState.TestFixture.Copy(); - UseSingleFileSelfContainedHost(fixture); - var bundler = BundleHelper.BundleApp(fixture, out var singleFile, bundleOptions); + var bundler = BundleSelfContainedApp(fixture, out var singleFile, bundleOptions); // Run the bundled app (extract files to ) var cmd = Command.Create(singleFile); @@ -110,8 +109,8 @@ private void Bundle_extraction_is_reused() var fixture = sharedTestState.TestFixture.Copy(); // Publish the bundle - UseSingleFileSelfContainedHost(fixture); - Bundler bundler = BundleHelper.BundleApp(fixture, out string singleFile, BundleOptions.BundleNativeBinaries); + BundleOptions options = BundleOptions.BundleNativeBinaries; + Bundler bundler = BundleSelfContainedApp(fixture, out string singleFile, options); // Create a directory for extraction. var extractBaseDir = BundleHelper.GetExtractionRootDir(fixture); @@ -160,13 +159,12 @@ private void Bundle_extraction_can_recover_missing_files() var appName = Path.GetFileNameWithoutExtension(hostName); // Publish the bundle - UseSingleFileSelfContainedHost(fixture); - Bundler bundler = BundleHelper.BundleApp(fixture, out string singleFile, BundleOptions.BundleNativeBinaries); + BundleOptions options = BundleOptions.BundleNativeBinaries; + Bundler bundler = BundleSelfContainedApp(fixture, out string singleFile, options); // Create a directory for extraction. var extractBaseDir = BundleHelper.GetExtractionRootDir(fixture); - // Run the bunded app for the first time, and extract files to // $DOTNET_BUNDLE_EXTRACT_BASE_DIR//bundle-id Command.Create(singleFile) diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleTestBase.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleTestBase.cs index c71aaa35e5bd..18aba06f6d97 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleTestBase.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleTestBase.cs @@ -44,10 +44,28 @@ public static string UseFrameworkDependentHost(TestProjectFixture testFixture) public static string BundleSelfContainedApp( TestProjectFixture testFixture, BundleOptions options = BundleOptions.None, - Version targetFrameworkVersion = null) + Version targetFrameworkVersion = null, + bool disableCompression = false) + { + string singleFile; + BundleSelfContainedApp(testFixture, out singleFile, options, targetFrameworkVersion); + return singleFile; + } + + public static Bundler BundleSelfContainedApp( + TestProjectFixture testFixture, + out string singleFile, + BundleOptions options = BundleOptions.None, + Version targetFrameworkVersion = null, + bool disableCompression = false) { UseSingleFileSelfContainedHost(testFixture); - return BundleHelper.BundleApp(testFixture, options, targetFrameworkVersion); + if (targetFrameworkVersion == null || targetFrameworkVersion >= new Version(6, 0)) + { + options |= BundleOptions.EnableCompression; + } + + return BundleHelper.BundleApp(testFixture, out singleFile, options, targetFrameworkVersion); } public abstract class SharedTestStateBase diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs index 2a0dc9ad537b..9ede20c516e3 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs @@ -59,7 +59,54 @@ public void Bundled_Framework_dependent_App_Run_Succeeds(BundleOptions options) public void Bundled_Self_Contained_App_Run_Succeeds(BundleOptions options) { var fixture = sharedTestState.TestSelfContainedFixture.Copy(); - var singleFile = BundleHelper.BundleApp(fixture, options); + var singleFile = BundleSelfContainedApp(fixture, options); + + // Run the bundled app (extract files) + RunTheApp(singleFile, fixture); + + // Run the bundled app again (reuse extracted files) + RunTheApp(singleFile, fixture); + } + + [InlineData(BundleOptions.None)] + [InlineData(BundleOptions.BundleNativeBinaries)] + [InlineData(BundleOptions.BundleAllContent)] + [Theory] + public void Bundled_Self_Contained_NoCompression_App_Run_Succeeds(BundleOptions options) + { + var fixture = sharedTestState.TestSelfContainedFixture.Copy(); + var singleFile = BundleSelfContainedApp(fixture, options, disableCompression: true); + + // Run the bundled app (extract files) + RunTheApp(singleFile, fixture); + + // Run the bundled app again (reuse extracted files) + RunTheApp(singleFile, fixture); + } + + [InlineData(BundleOptions.None)] + [InlineData(BundleOptions.BundleNativeBinaries)] + [InlineData(BundleOptions.BundleAllContent)] + [Theory] + public void Bundled_Self_Contained_Targeting50_App_Run_Succeeds(BundleOptions options) + { + var fixture = sharedTestState.TestSelfContainedFixture.Copy(); + var singleFile = BundleSelfContainedApp(fixture, options, new Version(5, 0)); + + // Run the bundled app (extract files) + RunTheApp(singleFile, fixture); + + // Run the bundled app again (reuse extracted files) + RunTheApp(singleFile, fixture); + } + + [InlineData(BundleOptions.BundleAllContent)] + [Theory] + public void Bundled_Framework_dependent_Targeting50_App_Run_Succeeds(BundleOptions options) + { + var fixture = sharedTestState.TestSelfContainedFixture.Copy(); + UseFrameworkDependentHost(fixture); + var singleFile = BundleHelper.BundleApp(fixture, options, new Version(5, 0)); // Run the bundled app (extract files) RunTheApp(singleFile, fixture); @@ -68,6 +115,17 @@ public void Bundled_Self_Contained_App_Run_Succeeds(BundleOptions options) RunTheApp(singleFile, fixture); } + [Fact] + public void Bundled_Self_Contained_Targeting50_WithCompression_Throws() + { + var fixture = sharedTestState.TestSelfContainedFixture.Copy(); + UseSingleFileSelfContainedHost(fixture); + // compression must be off when targeting 5.0 + var options = BundleOptions.EnableCompression; + + Assert.Throws(()=>BundleHelper.BundleApp(fixture, options, new Version(5, 0))); + } + [InlineData(BundleOptions.None)] [InlineData(BundleOptions.BundleNativeBinaries)] [InlineData(BundleOptions.BundleAllContent)] @@ -75,7 +133,7 @@ public void Bundled_Self_Contained_App_Run_Succeeds(BundleOptions options) public void Bundled_With_Empty_File_Succeeds(BundleOptions options) { var fixture = sharedTestState.TestAppWithEmptyFileFixture.Copy(); - var singleFile = BundleHelper.BundleApp(fixture, options); + var singleFile = BundleSelfContainedApp(fixture, options); // Run the app RunTheApp(singleFile, fixture); diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/NetCoreApp3CompatModeTests.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/NetCoreApp3CompatModeTests.cs index 95e1061f20c2..cb8bdf9d44a8 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/NetCoreApp3CompatModeTests.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/NetCoreApp3CompatModeTests.cs @@ -26,8 +26,8 @@ public NetCoreApp3CompatModeTests(SingleFileSharedState fixture) public void Bundle_Is_Extracted() { var fixture = sharedTestState.TestFixture.Copy(); - UseSingleFileSelfContainedHost(fixture); - Bundler bundler = BundleHelper.BundleApp(fixture, out string singleFile, BundleOptions.BundleAllContent); + BundleOptions options = BundleOptions.BundleAllContent; + Bundler bundler = BundleSelfContainedApp(fixture, out string singleFile, options); var extractionBaseDir = BundleHelper.GetExtractionRootDir(fixture); Command.Create(singleFile, "executing_assembly_location trusted_platform_assemblies assembly_location System.Console") diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs index 60425b9c7192..0efa9a781918 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs @@ -91,7 +91,7 @@ public void GetCommandLineArgs_0_Non_Bundled_App() public void AppContext_Native_Search_Dirs_Contains_Bundle_Dir() { var fixture = sharedTestState.TestFixture.Copy(); - Bundler bundler = BundleHelper.BundleApp(fixture, out string singleFile); + Bundler bundler = BundleSelfContainedApp(fixture, out string singleFile); string extractionDir = BundleHelper.GetExtractionDir(fixture, bundler).Name; string bundleDir = BundleHelper.GetBundleDir(fixture).FullName; @@ -110,7 +110,7 @@ public void AppContext_Native_Search_Dirs_Contains_Bundle_Dir() public void AppContext_Native_Search_Dirs_Contains_Bundle_And_Extraction_Dirs() { var fixture = sharedTestState.TestFixture.Copy(); - Bundler bundler = BundleHelper.BundleApp(fixture, out string singleFile, BundleOptions.BundleNativeBinaries); + Bundler bundler = BundleSelfContainedApp(fixture, out string singleFile, BundleOptions.BundleNativeBinaries); string extractionDir = BundleHelper.GetExtractionDir(fixture, bundler).Name; string bundleDir = BundleHelper.GetBundleDir(fixture).FullName; diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/Helpers/BundleHelper.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/Helpers/BundleHelper.cs index 6a102ddade96..ecd00f75c6fd 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/Helpers/BundleHelper.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/Helpers/BundleHelper.cs @@ -60,7 +60,7 @@ public static string[] GetBundledFiles(TestProjectFixture fixture) public static string[] GetExtractedFiles(TestProjectFixture fixture, BundleOptions bundleOptions) { - switch (bundleOptions) + switch (bundleOptions & ~BundleOptions.EnableCompression) { case BundleOptions.None: case BundleOptions.BundleOtherFiles: diff --git a/src/native/corehost/apphost/static/CMakeLists.txt b/src/native/corehost/apphost/static/CMakeLists.txt index c9222058ffdb..d7e129c5d206 100644 --- a/src/native/corehost/apphost/static/CMakeLists.txt +++ b/src/native/corehost/apphost/static/CMakeLists.txt @@ -19,6 +19,10 @@ set(SKIP_VERSIONING 1) include_directories(..) include_directories(../../json) +if(NOT CLR_CMAKE_TARGET_WIN32) + include_directories(../../../../libraries/Native/Unix/Common) +endif() + set(SOURCES ../bundle_marker.cpp ./hostfxr_resolver.cpp diff --git a/src/native/corehost/bundle/extractor.cpp b/src/native/corehost/bundle/extractor.cpp index be3f2077a378..e33daf8e70ce 100644 --- a/src/native/corehost/bundle/extractor.cpp +++ b/src/native/corehost/bundle/extractor.cpp @@ -7,6 +7,16 @@ #include "pal.h" #include "utils.h" +#if defined(NATIVE_LIBS_EMBEDDED) +extern "C" +{ +#include "../../../libraries/Native/AnyOS/zlib/pal_zlib.h" +} +#endif + +// Suppress prefast warning #6255: alloca indicates failure by raising a stack overflow exception +#pragma warning(disable:6255) + using namespace bundle; pal::string_t& extractor_t::extraction_dir() @@ -105,9 +115,66 @@ void extractor_t::extract(const file_entry_t &entry, reader_t &reader) reader.set_offset(entry.offset()); int64_t size = entry.size(); size_t cast_size = to_size_t_dbgchecked(size); - if (fwrite(reader, 1, cast_size, file) != cast_size) + size_t extracted_size = 0; + + if (entry.compressedSize() != 0) + { +#if defined(NATIVE_LIBS_EMBEDDED) + PAL_ZStream zStream; + zStream.nextIn = (uint8_t*)(const void*)reader; + zStream.availIn = entry.compressedSize(); + + const int Deflate_DefaultWindowBits = -15; // Legal values are 8..15 and -8..-15. 15 is the window size, + // negative val causes deflate to produce raw deflate data (no zlib header). + + int ret = CompressionNative_InflateInit2_(&zStream, Deflate_DefaultWindowBits); + if (ret != PAL_Z_OK) + { + trace::error(_X("Failure initializing zLib stream.")); + throw StatusCode::BundleExtractionIOError; + } + + const int bufSize = 4096; + uint8_t* buf = (uint8_t*)alloca(bufSize); + + do + { + zStream.nextOut = buf; + zStream.availOut = bufSize; + + ret = CompressionNative_Inflate(&zStream, PAL_Z_NOFLUSH); + if (ret < 0) + { + CompressionNative_InflateEnd(&zStream); + trace::error(_X("Failure inflating zLib stream. %s"), zStream.msg); + throw StatusCode::BundleExtractionIOError; + } + + int produced = bufSize - zStream.availOut; + if (fwrite(buf, 1, produced, file) != (size_t)produced) + { + CompressionNative_InflateEnd(&zStream); + trace::error(_X("I/O failure when writing decompressed file.")); + throw StatusCode::BundleExtractionIOError; + } + + extracted_size += produced; + } while (zStream.availOut == 0); + + CompressionNative_InflateEnd(&zStream); +#else + trace::error(_X("Failure extracting contents of the application bundle. Compressed files used with a standalone (not singlefile) apphost.")); + throw StatusCode::BundleExtractionIOError; +#endif + } + else + { + extracted_size = fwrite(reader, 1, cast_size, file); + } + + if (extracted_size != cast_size) { - trace::error(_X("Failure extracting contents of the application bundle.")); + trace::error(_X("Failure extracting contents of the application bundle. Expected size:%d Actual size:%d"), size, extracted_size); trace::error(_X("I/O failure when writing extracted files.")); throw StatusCode::BundleExtractionIOError; } diff --git a/src/native/corehost/bundle/file_entry.cpp b/src/native/corehost/bundle/file_entry.cpp index e33b2cfa32dc..ace0ef514927 100644 --- a/src/native/corehost/bundle/file_entry.cpp +++ b/src/native/corehost/bundle/file_entry.cpp @@ -10,15 +10,26 @@ using namespace bundle; bool file_entry_t::is_valid() const { - return m_offset > 0 && m_size >= 0 && + return m_offset > 0 && m_size >= 0 && m_compressedSize >= 0 && static_cast(m_type) < file_type_t::__last; } -file_entry_t file_entry_t::read(reader_t &reader, bool force_extraction) +file_entry_t file_entry_t::read(reader_t &reader, uint32_t bundle_major_version, bool force_extraction) { // First read the fixed-sized portion of file-entry - const file_entry_fixed_t* fixed_data = reinterpret_cast(reader.read_direct(sizeof(file_entry_fixed_t))); - file_entry_t entry(fixed_data, force_extraction); + file_entry_fixed_t fixed_data; + + fixed_data.offset = *(int64_t*)reader.read_direct(sizeof(int64_t)); + fixed_data.size = *(int64_t*)reader.read_direct(sizeof(int64_t)); + + // compressedSize is present only in v6+ headers + fixed_data.compressedSize = bundle_major_version >= 6 ? + *(int64_t*)reader.read_direct(sizeof(int64_t)) : + 0; + + fixed_data.type = *(file_type_t*)reader.read_direct(sizeof(file_type_t)); + + file_entry_t entry(&fixed_data, force_extraction); if (!entry.is_valid()) { diff --git a/src/native/corehost/bundle/file_entry.h b/src/native/corehost/bundle/file_entry.h index eb122efe8683..225cf225bcde 100644 --- a/src/native/corehost/bundle/file_entry.h +++ b/src/native/corehost/bundle/file_entry.h @@ -16,6 +16,7 @@ namespace bundle // Fixed size portion (file_entry_fixed_t) // - Offset // - Size + // - CompressedSize - only in bundleVersion 6+ // - File Entry Type // Variable Size portion // - relative path (7-bit extension encoded length prefixed string) @@ -25,6 +26,7 @@ namespace bundle { int64_t offset; int64_t size; + int64_t compressedSize; file_type_t type; }; #pragma pack(pop) @@ -56,23 +58,26 @@ namespace bundle m_offset = fixed_data->offset; m_size = fixed_data->size; + m_compressedSize = fixed_data->compressedSize; m_type = fixed_data->type; } const pal::string_t relative_path() const { return m_relative_path; } int64_t offset() const { return m_offset; } int64_t size() const { return m_size; } + int64_t compressedSize() const { return m_compressedSize; } file_type_t type() const { return m_type; } void disable() { m_disabled = true; } bool is_disabled() const { return m_disabled; } bool needs_extraction() const; bool matches(const pal::string_t& path) const { return (pal::pathcmp(relative_path(), path) == 0) && !is_disabled(); } - static file_entry_t read(reader_t &reader, bool force_extraction); + static file_entry_t read(reader_t &reader, uint32_t bundle_major_version, bool force_extraction); private: int64_t m_offset; int64_t m_size; + int64_t m_compressedSize; file_type_t m_type; pal::string_t m_relative_path; // Path of an embedded file, relative to the extraction directory. // If the file represented by this entry is also found in a servicing location, the servicing location must take precedence. diff --git a/src/native/corehost/bundle/header.cpp b/src/native/corehost/bundle/header.cpp index 05aa736c175b..268ef867146a 100644 --- a/src/native/corehost/bundle/header.cpp +++ b/src/native/corehost/bundle/header.cpp @@ -15,9 +15,11 @@ bool header_fixed_t::is_valid() const return false; } + // .net 6 host expects the version information to be 6.0 // .net 5 host expects the version information to be 2.0 // .net core 3 single-file bundles are handled within the netcoreapp3.x apphost, and are not processed here in the framework. - return (major_version == header_t::major_version) && (minor_version == header_t::minor_version); + return ((major_version == 6) && (minor_version == 0)) || + ((major_version == 2) && (minor_version == 0)); } header_t header_t::read(reader_t& reader) @@ -32,7 +34,7 @@ header_t header_t::read(reader_t& reader) throw StatusCode::BundleExtractionFailure; } - header_t header(fixed_header->num_embedded_files); + header_t header(fixed_header->major_version, fixed_header->minor_version, fixed_header->num_embedded_files); // bundle_id is a component of the extraction path reader.read_path_string(header.m_bundle_id); diff --git a/src/native/corehost/bundle/header.h b/src/native/corehost/bundle/header.h index f785203f38a0..1bc9241429c2 100644 --- a/src/native/corehost/bundle/header.h +++ b/src/native/corehost/bundle/header.h @@ -34,7 +34,7 @@ namespace bundle bool is_valid() const; }; - // netcoreapp3_compat_mode flag is set on a .net5 app, which chooses to build single-file apps in .netcore3.x compat mode, + // netcoreapp3_compat_mode flag is set on a .net5+ app, which chooses to build single-file apps in .netcore3.x compat mode, // This indicates that: // All published files are bundled into the app; some of them will be extracted to disk. // AppContext.BaseDirectory is set to the extraction directory (and not the AppHost directory). @@ -72,8 +72,10 @@ namespace bundle struct header_t { public: - header_t(int32_t num_embedded_files = 0) + header_t(uint32_t major_version, uint32_t minor_version, int32_t num_embedded_files) : m_num_embedded_files(num_embedded_files) + , m_major_version(major_version) + , m_minor_version(minor_version) , m_bundle_id() , m_v2_header() { @@ -87,11 +89,13 @@ namespace bundle const location_t& runtimeconfig_json_location() const { return m_v2_header.runtimeconfig_json_location; } bool is_netcoreapp3_compat_mode() const { return m_v2_header.is_netcoreapp3_compat_mode(); } - static const uint32_t major_version = 2; - static const uint32_t minor_version = 0; + const uint32_t major_version() const { return m_major_version; }; + const uint32_t minor_version() const { return m_minor_version; }; private: int32_t m_num_embedded_files; + uint32_t m_major_version; + uint32_t m_minor_version; pal::string_t m_bundle_id; header_fixed_v2_t m_v2_header; }; diff --git a/src/native/corehost/bundle/info.cpp b/src/native/corehost/bundle/info.cpp index afb1ee1381f5..5f9f0dadcd08 100644 --- a/src/native/corehost/bundle/info.cpp +++ b/src/native/corehost/bundle/info.cpp @@ -11,11 +11,12 @@ using namespace bundle; const info_t* info_t::the_app = nullptr; info_t::info_t(const pal::char_t* bundle_path, - const pal::char_t* app_path, - int64_t header_offset) + const pal::char_t* app_path, + int64_t header_offset) : m_bundle_path(bundle_path) , m_bundle_size(0) , m_header_offset(header_offset) + , m_header(0, 0, 0) { m_base_path = get_directory(m_bundle_path); diff --git a/src/native/corehost/bundle/manifest.cpp b/src/native/corehost/bundle/manifest.cpp index cf439952871c..ed7ced64f3aa 100644 --- a/src/native/corehost/bundle/manifest.cpp +++ b/src/native/corehost/bundle/manifest.cpp @@ -11,7 +11,7 @@ manifest_t manifest_t::read(reader_t& reader, const header_t& header) for (int32_t i = 0; i < header.num_embedded_files(); i++) { - file_entry_t entry = file_entry_t::read(reader, header.is_netcoreapp3_compat_mode()); + file_entry_t entry = file_entry_t::read(reader, header.major_version(), header.is_netcoreapp3_compat_mode()); manifest.files.push_back(std::move(entry)); manifest.m_files_need_extraction |= entry.needs_extraction(); } diff --git a/src/native/corehost/hostmisc/utils.h b/src/native/corehost/hostmisc/utils.h index 932cb8046752..d0dc381b69cc 100644 --- a/src/native/corehost/hostmisc/utils.h +++ b/src/native/corehost/hostmisc/utils.h @@ -120,8 +120,9 @@ template size_t to_size_t_dbgchecked(T value) { assert(value >= 0); - assert(value < static_cast(std::numeric_limits::max())); - return static_cast(value); + size_t result = static_cast(value); + assert(static_cast(result) == value); + return result; } #endif From 29e8a13cc5b8756378e016bf97ff0697deb1a639 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Mon, 29 Mar 2021 17:12:57 -0700 Subject: [PATCH 29/98] Fix SuperPMI handling of missing data for asm diffs (#50309) Adjust the error code determination for asm diffs to not report diffs if all the compilation failures were due to missing data in the MCH files. Update the SuperPMI end status line to report the count of MC compilation failures due to missing data (and adjust parallel SuperPMI to handle it as well). As a result, with `superpmi.py asmdiffs` you won't see the message "Asm diffs" if all the failures were due to missing data. --- .../superpmi/superpmi/parallelsuperpmi.cpp | 20 ++++++++++--------- .../ToolBox/superpmi/superpmi/superpmi.cpp | 10 +++++----- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/coreclr/ToolBox/superpmi/superpmi/parallelsuperpmi.cpp b/src/coreclr/ToolBox/superpmi/superpmi/parallelsuperpmi.cpp index ba8584617de2..1eb1a7c2f510 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi/parallelsuperpmi.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi/parallelsuperpmi.cpp @@ -210,6 +210,7 @@ void ProcessChildStdOut(const CommandLine::Options& o, int* jitted, int* failed, int* excluded, + int* missing, int* diffs, bool* usageError) { @@ -253,13 +254,13 @@ void ProcessChildStdOut(const CommandLine::Options& o, } else if (strncmp(buff, g_AllFormatStringFixedPrefix, strlen(g_AllFormatStringFixedPrefix)) == 0) { - int childLoaded = 0, childJitted = 0, childFailed = 0, childExcluded = 0; + int childLoaded = 0, childJitted = 0, childFailed = 0, childExcluded = 0, childMissing = 0; if (o.applyDiff) { int childDiffs = 0; int converted = sscanf_s(buff, g_AsmDiffsSummaryFormatString, &childLoaded, &childJitted, &childFailed, - &childExcluded, &childDiffs); - if (converted != 5) + &childExcluded, &childMissing, &childDiffs); + if (converted != 6) { LogError("Couldn't parse status message: \"%s\"", buff); continue; @@ -269,8 +270,8 @@ void ProcessChildStdOut(const CommandLine::Options& o, else { int converted = - sscanf_s(buff, g_SummaryFormatString, &childLoaded, &childJitted, &childFailed, &childExcluded); - if (converted != 4) + sscanf_s(buff, g_SummaryFormatString, &childLoaded, &childJitted, &childFailed, &childExcluded, &childMissing); + if (converted != 5) { LogError("Couldn't parse status message: \"%s\"", buff); continue; @@ -281,6 +282,7 @@ void ProcessChildStdOut(const CommandLine::Options& o, *jitted += childJitted; *failed += childFailed; *excluded += childExcluded; + *missing += childMissing; } } @@ -616,14 +618,14 @@ int doParallelSuperPMI(CommandLine::Options& o) bool usageError = false; // variable to flag if we hit a usage error in SuperPMI - int loaded = 0, jitted = 0, failed = 0, excluded = 0, diffs = 0; + int loaded = 0, jitted = 0, failed = 0, excluded = 0, missing = 0, diffs = 0; // Read the stderr files and log them as errors // Read the stdout files and parse them for counts and log any MISSING or ISSUE errors for (int i = 0; i < o.workerCount; i++) { ProcessChildStdErr(arrStdErrorPath[i]); - ProcessChildStdOut(o, arrStdOutputPath[i], &loaded, &jitted, &failed, &excluded, &diffs, &usageError); + ProcessChildStdOut(o, arrStdOutputPath[i], &loaded, &jitted, &failed, &excluded, &missing, &diffs, &usageError); if (usageError) break; } @@ -644,11 +646,11 @@ int doParallelSuperPMI(CommandLine::Options& o) { if (o.applyDiff) { - LogInfo(g_AsmDiffsSummaryFormatString, loaded, jitted, failed, excluded, diffs); + LogInfo(g_AsmDiffsSummaryFormatString, loaded, jitted, failed, excluded, missing, diffs); } else { - LogInfo(g_SummaryFormatString, loaded, jitted, failed, excluded); + LogInfo(g_SummaryFormatString, loaded, jitted, failed, excluded, missing); } } diff --git a/src/coreclr/ToolBox/superpmi/superpmi/superpmi.cpp b/src/coreclr/ToolBox/superpmi/superpmi/superpmi.cpp index 2b741a45f1ee..a3fa79dbc537 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi/superpmi.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi/superpmi.cpp @@ -25,8 +25,8 @@ extern int doParallelSuperPMI(CommandLine::Options& o); // There must be a single, fixed prefix common to all strings, to ease the determination of when // to parse the string fully. const char* const g_AllFormatStringFixedPrefix = "Loaded "; -const char* const g_SummaryFormatString = "Loaded %d Jitted %d FailedCompile %d Excluded %d"; -const char* const g_AsmDiffsSummaryFormatString = "Loaded %d Jitted %d FailedCompile %d Excluded %d Diffs %d"; +const char* const g_SummaryFormatString = "Loaded %d Jitted %d FailedCompile %d Excluded %d Missing %d"; +const char* const g_AsmDiffsSummaryFormatString = "Loaded %d Jitted %d FailedCompile %d Excluded %d Missing %d Diffs %d"; //#define SuperPMI_ChewMemory 0x7FFFFFFF //Amount of address space to consume on startup @@ -576,11 +576,11 @@ int __cdecl main(int argc, char* argv[]) if (o.applyDiff) { LogInfo(g_AsmDiffsSummaryFormatString, loadedCount, jittedCount, failToReplayCount, excludedCount, - jittedCount - failToReplayCount - matchCount); + missingCount, jittedCount - failToReplayCount - matchCount); } else { - LogInfo(g_SummaryFormatString, loadedCount, jittedCount, failToReplayCount, excludedCount); + LogInfo(g_SummaryFormatString, loadedCount, jittedCount, failToReplayCount, excludedCount, missingCount); } st2.Stop(); @@ -607,7 +607,7 @@ int __cdecl main(int argc, char* argv[]) { result = SpmiResult::Error; } - else if (o.applyDiff && matchCount != jittedCount) + else if (o.applyDiff && (matchCount != jittedCount - missingCount)) { result = SpmiResult::Diffs; } From a221324771e61a33ef81b319f75d30d7291d87e4 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 29 Mar 2021 20:40:33 -0400 Subject: [PATCH 30/98] Several WebProxy-related fixes (#50368) - Split browser from non-browser to avoid CA1416 suppressions, UnsupportedOSPlatform attributes, and PNSEs when used from browser - Cache domain name and local addresses for use in IsLocal, along with clearing of the caches upon network change detection - Removing redundant IPAddress.IsLoopback check that's covered by previous Uri.IsLoopback check - Change IsMatchInBypassList to use string interpolation, such that the non-default port case will get better implicitly with C# 10 interpolated string improvements --- .../src/System.Net.WebProxy.csproj | 5 +- .../src/System/Net/WebProxy.Browser.cs | 23 ++++++ .../src/System/Net/WebProxy.NonBrowser.cs | 76 ++++++++++++++++++ .../src/System/Net/WebProxy.cs | 80 +++---------------- 4 files changed, 114 insertions(+), 70 deletions(-) create mode 100644 src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.Browser.cs create mode 100644 src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.NonBrowser.cs diff --git a/src/libraries/System.Net.WebProxy/src/System.Net.WebProxy.csproj b/src/libraries/System.Net.WebProxy/src/System.Net.WebProxy.csproj index bd89d8a9ab77..5fd9532a47b3 100644 --- a/src/libraries/System.Net.WebProxy/src/System.Net.WebProxy.csproj +++ b/src/libraries/System.Net.WebProxy/src/System.Net.WebProxy.csproj @@ -1,12 +1,14 @@ true - $(NetCoreAppCurrent) + $(NetCoreAppCurrent);$(NetCoreAppCurrent)-Browser enable + + @@ -15,5 +17,6 @@ + diff --git a/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.Browser.cs b/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.Browser.cs new file mode 100644 index 000000000000..38fb0d0250be --- /dev/null +++ b/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.Browser.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Serialization; + +namespace System.Net +{ + public partial class WebProxy : IWebProxy, ISerializable + { + private bool IsLocal(Uri host) + { + if (host.IsLoopback) + { + return true; + } + + string hostString = host.Host; + return + !IPAddress.TryParse(hostString, out _) && + !hostString.Contains('.'); + } + } +} diff --git a/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.NonBrowser.cs b/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.NonBrowser.cs new file mode 100644 index 000000000000..33894f9145d4 --- /dev/null +++ b/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.NonBrowser.cs @@ -0,0 +1,76 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Net.NetworkInformation; +using System.Runtime.Serialization; +using System.Threading; + +namespace System.Net +{ + public partial class WebProxy : IWebProxy, ISerializable + { + private static volatile string? s_domainName; + private static volatile IPAddress[]? s_localAddresses; + private static int s_networkChangeRegistered; + + private bool IsLocal(Uri host) + { + if (host.IsLoopback) + { + return true; + } + + string hostString = host.Host; + + if (IPAddress.TryParse(hostString, out IPAddress? hostAddress)) + { + EnsureNetworkChangeRegistration(); + IPAddress[] localAddresses = s_localAddresses ??= Dns.GetHostEntry(Dns.GetHostName()).AddressList; + return Array.IndexOf(localAddresses, hostAddress) != -1; + } + + // No dot? Local. + int dot = hostString.IndexOf('.'); + if (dot == -1) + { + return true; + } + + // If it matches the primary domain, it's local (whether or not the hostname matches). + EnsureNetworkChangeRegistration(); + string local = s_domainName ??= "." + IPGlobalProperties.GetIPGlobalProperties().DomainName; + return + local.Length == (hostString.Length - dot) && + string.Compare(local, 0, hostString, dot, local.Length, StringComparison.OrdinalIgnoreCase) == 0; + } + + /// Ensures we've registered with NetworkChange to clear out statically-cached state upon a network change notification. + private static void EnsureNetworkChangeRegistration() + { + if (s_networkChangeRegistered == 0) + { + Register(); + + static void Register() + { + if (Interlocked.Exchange(ref s_networkChangeRegistered, 1) != 0) + { + return; + } + + // Clear out cached state when we get notification of a network-related change. + NetworkChange.NetworkAddressChanged += (s, e) => + { + s_domainName = null; + s_localAddresses = null; + }; + NetworkChange.NetworkAvailabilityChanged += (s, e) => + { + s_domainName = null; + s_localAddresses = null; + }; + } + } + } + } +} diff --git a/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.cs b/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.cs index a5db1a07205d..2a35d10674a8 100644 --- a/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.cs +++ b/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.cs @@ -4,14 +4,12 @@ using System.Collections; using System.Diagnostics.CodeAnalysis; using System.Globalization; -using System.Net.NetworkInformation; using System.Runtime.Serialization; -using System.Runtime.Versioning; using System.Text.RegularExpressions; namespace System.Net { - public class WebProxy : IWebProxy, ISerializable + public partial class WebProxy : IWebProxy, ISerializable { private ArrayList? _bypassList; private Regex[]? _regexBypassList; @@ -68,7 +66,7 @@ public WebProxy(string? Address, bool BypassOnLocal, string[]? BypassList, ICred [AllowNull] public string[] BypassList { - get { return _bypassList != null ? (string[])_bypassList.ToArray(typeof(string)) : Array.Empty(); } + get => _bypassList != null ? (string[])_bypassList.ToArray(typeof(string)) : Array.Empty(); set { _bypassList = value != null ? new ArrayList(value) : null; @@ -82,8 +80,8 @@ public string[] BypassList public bool UseDefaultCredentials { - get { return Credentials == CredentialCache.DefaultCredentials; } - set { Credentials = value ? CredentialCache.DefaultCredentials : null; } + get => Credentials == CredentialCache.DefaultCredentials; + set => Credentials = value ? CredentialCache.DefaultCredentials : null; } public Uri? GetProxy(Uri destination) @@ -132,13 +130,13 @@ private void UpdateRegexList(bool canThrow) private bool IsMatchInBypassList(Uri input) { - UpdateRegexList(false); + UpdateRegexList(canThrow: false); if (_regexBypassList != null) { string matchUriString = input.IsDefaultPort ? - input.Scheme + "://" + input.Host : - input.Scheme + "://" + input.Host + ":" + input.Port.ToString(); + $"{input.Scheme}://{input.Host}" : + $"{input.Scheme}://{input.Host}:{(uint)input.Port}"; foreach (Regex r in _regexBypassList) { @@ -152,54 +150,6 @@ private bool IsMatchInBypassList(Uri input) return false; } - private bool IsLocal(Uri host) - { - if (host.IsLoopback) - { - return true; - } - - string hostString = host.Host; - -#pragma warning disable CA1416 // Validate platform compatibility, issue: https://github.com/dotnet/runtime/issues/43751 - if (IPAddress.TryParse(hostString, out IPAddress? hostAddress)) - { - return IPAddress.IsLoopback(hostAddress) || IsAddressLocal(hostAddress); - } - - // No dot? Local. - int dot = hostString.IndexOf('.'); - if (dot == -1) - { - return true; - } - - // If it matches the primary domain, it's local. (Whether or not the hostname matches.) - string local = "." + IPGlobalProperties.GetIPGlobalProperties().DomainName; -#pragma warning restore CA1416 // Validate platform compatibility - return - local.Length == (hostString.Length - dot) && - string.Compare(local, 0, hostString, dot, local.Length, StringComparison.OrdinalIgnoreCase) == 0; - } - - [UnsupportedOSPlatform("browser")] - private static bool IsAddressLocal(IPAddress ipAddress) - { - // Perf note: The .NET Framework caches this and then uses network change notifications to track - // whether the set should be recomputed. We could consider doing the same if this is observed as - // a bottleneck, but that tracking has its own costs. - IPAddress[] localAddresses = Dns.GetHostEntry(Dns.GetHostName()).AddressList; - for (int i = 0; i < localAddresses.Length; i++) - { - if (ipAddress.Equals(localAddresses[i])) - { - return true; - } - } - - return false; - } - public bool IsBypassed(Uri host) { if (host == null) @@ -213,27 +163,19 @@ public bool IsBypassed(Uri host) IsMatchInBypassList(host); } - protected WebProxy(SerializationInfo serializationInfo, StreamingContext streamingContext) - { + protected WebProxy(SerializationInfo serializationInfo, StreamingContext streamingContext) => throw new PlatformNotSupportedException(); - } - void ISerializable.GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext) - { + void ISerializable.GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext) => throw new PlatformNotSupportedException(); - } - protected virtual void GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext) - { + protected virtual void GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext) => throw new PlatformNotSupportedException(); - } [Obsolete("This method has been deprecated. Please use the proxy selected for you by default. https://go.microsoft.com/fwlink/?linkid=14202")] - public static WebProxy GetDefaultProxy() - { + public static WebProxy GetDefaultProxy() => // The .NET Framework here returns a proxy that fetches IE settings and // executes JavaScript to determine the correct proxy. throw new PlatformNotSupportedException(); - } } } From e830f1006def0759fa3d2fa43629e34cdb150c3c Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 29 Mar 2021 20:44:25 -0400 Subject: [PATCH 31/98] Add CancellationTokenSource.TryReset (#50346) * Add CancellationTokenSource.TryReset * Update src/libraries/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs --- .../Threading/CancellationTokenSource.cs | 110 +++++++++++++++--- .../src/System/Threading/Timer.cs | 2 + .../src/System/ThrowHelper.cs | 3 + .../System.Runtime/ref/System.Runtime.cs | 1 + .../tests/CancellationTokenTests.cs | 49 ++++++++ 5 files changed, 149 insertions(+), 16 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs index ebd38dd1afa4..54a7dc691cc0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs @@ -66,7 +66,7 @@ private static void TimerCallback(object? state) => // separated out into a name /// canceled concurrently. /// /// - public bool IsCancellationRequested => _state >= NotifyingState; + public bool IsCancellationRequested => _state != NotCanceledState; /// A simple helper to determine whether cancellation has finished. internal bool IsCancellationCompleted => _state == NotifyingCompleteState; @@ -365,6 +365,54 @@ private void CancelAfter(uint millisecondsDelay) } } + /// + /// Attempts to reset the to be used for an unrelated operation. + /// + /// + /// true if the has not had cancellation requested and could + /// have its state reset to be reused for a subsequent operation; otherwise, false. + /// + /// + /// is intended to be used by the sole owner of the + /// when it is known that the operation with which the was used has + /// completed, no one else will be attempting to cancel it, and any registrations still remaining are erroneous. + /// Upon a successful reset, such registrations will no longer be notified for any subsequent cancellation of the + /// ; however, if any component still holds a reference to this + /// either directly or indirectly via a + /// handed out from it, polling via their reference will show the current state any time after the reset as + /// it's the same instance. Usage of concurrently with requesting cancellation is not + /// thread-safe and may result in TryReset returning true even if cancellation was already requested and may result + /// in registrations not being invoked as part of the concurrent cancellation request. + /// + public bool TryReset() + { + ThrowIfDisposed(); + + // We can only reset if cancellation has not yet been requested: we never want to allow a CancellationToken + // to transition from canceled to non-canceled. + if (_state == NotCanceledState) + { + // If there is no timer, then we're free to reset. If there is a timer, then we need to first try + // to reset it to be infinite so that it won't fire, and then recognize that it could have already + // fired by the time we successfully changed it, and so check to see whether that's possibly the case. + // If we successfully reset it and it never fired, then we can be sure it won't trigger cancellation. + bool reset = + _timer is not TimerQueueTimer timer || + (timer.Change(Timeout.UnsignedInfinite, Timeout.UnsignedInfinite) && !timer._everQueued); + + if (reset) + { + // We're not canceled and no timer will run to cancel us. + // Clear out all the registrations, and return that we've successfully reset. + Volatile.Read(ref _registrations)?.UnregisterAll(); + return true; + } + } + + // Failed to reset. + return false; + } + /// Releases the resources used by this . /// This method is not thread-safe for any other concurrent calls. public void Dispose() @@ -434,10 +482,7 @@ private void ThrowIfDisposed() { if (_disposed) { - ThrowObjectDisposedException(); - - [DoesNotReturn] - static void ThrowObjectDisposedException() => throw new ObjectDisposedException(null, SR.CancellationTokenSource_Disposed); + ThrowHelper.ThrowObjectDisposedException(ExceptionResource.CancellationTokenSource_Disposed); } } @@ -876,6 +921,25 @@ internal sealed class Registrations /// The associated source. public Registrations(CancellationTokenSource source) => Source = source; + [MethodImpl(MethodImplOptions.AggressiveInlining)] // used in only two places, one of which is a hot path + private void Recycle(CallbackNode node) + { + Debug.Assert(_lock == 1); + + // Clear out the unused node and put it on the singly-linked free list. + // The only field we don't clear out is the associated Registrations, as that's fixed + // throughout the node's lifetime. + node.Id = 0; + node.Callback = null; + node.CallbackState = null; + node.ExecutionContext = null; + node.SynchronizationContext = null; + + node.Prev = null; + node.Next = FreeNodeList; + FreeNodeList = node; + } + /// Unregisters a callback. /// The expected id of the registration. /// The callback node. @@ -925,17 +989,7 @@ public bool Unregister(long id, CallbackNode node) node.Next.Prev = node.Prev; } - // Clear out the now unused node and put it on the singly-linked free list. - // The only field we don't clear out is the associated Source, as that's fixed - // throughout the nodes lifetime. - node.Id = 0; - node.Callback = null; - node.CallbackState = null; - node.ExecutionContext = null; - node.SynchronizationContext = null; - node.Prev = null; - node.Next = FreeNodeList; - FreeNodeList = node; + Recycle(node); return true; } @@ -945,6 +999,30 @@ public bool Unregister(long id, CallbackNode node) } } + /// Moves all registrations to the free list. + public void UnregisterAll() + { + EnterLock(); + try + { + // Null out all callbacks. + CallbackNode? node = Callbacks; + Callbacks = null; + + // Reset and move each node that was in the callbacks list to the free list. + while (node != null) + { + CallbackNode? next = node.Next; + Recycle(node); + node = next; + } + } + finally + { + ExitLock(); + } + } + /// /// Wait for a single callback to complete (or, more specifically, to not be running). /// It is ok to call this method if the callback has already finished. diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.cs index 2ab4c1003e12..e2766f270c49 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.cs @@ -206,6 +206,7 @@ private void FireNextTimers() if (remaining <= 0) { // Timer is ready to fire. + timer._everQueued = true; if (timer._period != Timeout.UnsignedInfinite) { @@ -476,6 +477,7 @@ internal sealed partial class TimerQueueTimer : IThreadPoolWorkItem // instead of with a provided WaitHandle. private int _callbacksRunning; private bool _canceled; + internal bool _everQueued; private object? _notifyWhenNoCallbacksRunning; // may be either WaitHandle or Task internal TimerQueueTimer(TimerCallback timerCallback, object? state, uint dueTime, uint period, bool flowExecutionContext) diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs index 128b59d30594..117e60fdd061 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -911,6 +911,8 @@ private static string GetResourceString(ExceptionResource resource) return SR.Argument_SpansMustHaveSameLength; case ExceptionResource.Argument_InvalidFlag: return SR.Argument_InvalidFlag; + case ExceptionResource.CancellationTokenSource_Disposed: + return SR.CancellationTokenSource_Disposed; default: Debug.Fail("The enum value is not defined, please check the ExceptionResource Enum."); return ""; @@ -1090,5 +1092,6 @@ internal enum ExceptionResource Arg_TypeNotSupported, Argument_SpansMustHaveSameLength, Argument_InvalidFlag, + CancellationTokenSource_Disposed, } } diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index ef66619698d1..5e3c282aff8a 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -11020,6 +11020,7 @@ public void CancelAfter(System.TimeSpan delay) { } public static System.Threading.CancellationTokenSource CreateLinkedTokenSource(params System.Threading.CancellationToken[] tokens) { throw null; } public void Dispose() { } protected virtual void Dispose(bool disposing) { } + public bool TryReset() { throw null; } } public enum LazyThreadSafetyMode { diff --git a/src/libraries/System.Threading.Tasks/tests/CancellationTokenTests.cs b/src/libraries/System.Threading.Tasks/tests/CancellationTokenTests.cs index 8ba430cbbe5f..63a37468eb3d 100644 --- a/src/libraries/System.Threading.Tasks/tests/CancellationTokenTests.cs +++ b/src/libraries/System.Threading.Tasks/tests/CancellationTokenTests.cs @@ -1049,6 +1049,7 @@ public static void CancellationTokenSourceWithTimer() cts.Dispose(); } + [Fact] public static void CancellationTokenSourceWithTimer_Negative() { @@ -1076,6 +1077,54 @@ public static void CancellationTokenSourceWithTimer_Negative() Assert.Throws(() => { cts.CancelAfter(reasonableTimeSpan); }); } + [Fact] + public static void CancellationTokenSource_TryReset_ReturnsFalseIfAlreadyCanceled() + { + var cts = new CancellationTokenSource(); + cts.Cancel(); + Assert.False(cts.TryReset()); + Assert.True(cts.IsCancellationRequested); + } + + [Fact] + public static void CancellationTokenSource_TryReset_ReturnsTrueIfNotCanceledAndNoTimer() + { + var cts = new CancellationTokenSource(); + Assert.True(cts.TryReset()); + Assert.True(cts.TryReset()); + } + + [Fact] + public static void CancellationTokenSource_TryReset_ReturnsTrueIfNotCanceledAndTimerHasntFired() + { + var cts = new CancellationTokenSource(); + cts.CancelAfter(TimeSpan.FromDays(1)); + Assert.True(cts.TryReset()); + } + + [Fact] + public static void CancellationTokenSource_TryReset_UnregistersAll() + { + bool registration1Invoked = false; + bool registration2Invoked = false; + + var cts = new CancellationTokenSource(); + CancellationTokenRegistration ctr1 = cts.Token.Register(() => registration1Invoked = true); + Assert.True(cts.TryReset()); + CancellationTokenRegistration ctr2 = cts.Token.Register(() => registration2Invoked = true); + + cts.Cancel(); + + Assert.False(registration1Invoked); + Assert.True(registration2Invoked); + + Assert.False(ctr1.Unregister()); + Assert.False(ctr2.Unregister()); + + Assert.Equal(cts.Token, ctr1.Token); + Assert.Equal(cts.Token, ctr2.Token); + } + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public static void EnlistWithSyncContext_BeforeCancel() { From 260397c9c3d83d018b4faf2ba3413aec95ca11cf Mon Sep 17 00:00:00 2001 From: SingleAccretion <62474226+SingleAccretion@users.noreply.github.com> Date: Tue, 30 Mar 2021 04:14:17 +0300 Subject: [PATCH 32/98] Do not fold away double negation if the tree is a CSE candidate (#50373) * Do not morph away double negation if it is a CSE candidate * Added a test verifying that double negation is not removed for CSEs * Bump the test's priority to zero --- src/coreclr/jit/morph.cpp | 8 +++-- .../JIT/opt/CSE/CSEWithDoubleNegation.cs | 34 +++++++++++++++++++ .../JIT/opt/CSE/CSEWithDoubleNegation.csproj | 12 +++++++ 3 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 src/tests/JIT/opt/CSE/CSEWithDoubleNegation.cs create mode 100644 src/tests/JIT/opt/CSE/CSEWithDoubleNegation.csproj diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 433750b37f26..8fbce8a586e7 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -14411,8 +14411,12 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) case GT_NOT: case GT_NEG: - // Remove double negation/not - if (op1->OperIs(oper) && opts.OptimizationEnabled()) + // Remove double negation/not. + // Note: this is not a safe tranformation if "tree" is a CSE candidate. + // Consider for example the following expression: NEG(NEG(OP)), where the top-level + // NEG is a CSE candidate. Were we to morph this to just OP, CSE would fail to find + // the original NEG in the statement. + if (op1->OperIs(oper) && opts.OptimizationEnabled() && !gtIsActiveCSE_Candidate(tree)) { GenTree* child = op1->AsOp()->gtGetOp1(); return child; diff --git a/src/tests/JIT/opt/CSE/CSEWithDoubleNegation.cs b/src/tests/JIT/opt/CSE/CSEWithDoubleNegation.cs new file mode 100644 index 000000000000..d157d14a282c --- /dev/null +++ b/src/tests/JIT/opt/CSE/CSEWithDoubleNegation.cs @@ -0,0 +1,34 @@ +// 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.Runtime.CompilerServices; + +namespace CSEWithDoubleNegation +{ + class DoNotMorphAwayCSEThatRepresentsDoubleNegation + { + private static int _static = 0; + + static int Main(string[] args) + { + if (DoubleNeg() != 22) + { + Console.WriteLine("DoubleNeg() failed to return the expected value of 22"); + return -1; + } + + return 100; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static int DoubleNeg() + { + var a = 21; + _static = 42; + + return 43 - (0 - (a - _static)); + } + } +} + diff --git a/src/tests/JIT/opt/CSE/CSEWithDoubleNegation.csproj b/src/tests/JIT/opt/CSE/CSEWithDoubleNegation.csproj new file mode 100644 index 000000000000..075001135d54 --- /dev/null +++ b/src/tests/JIT/opt/CSE/CSEWithDoubleNegation.csproj @@ -0,0 +1,12 @@ + + + Exe + + + None + True + + + + + From ec0bb49776bd03330356556de88bb8456b4f667c Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Mon, 29 Mar 2021 21:12:29 -0500 Subject: [PATCH 33/98] Annotate CreateInstanceForAnotherGenericParameter as PublicParameterlessConstructor (#50390) This allows private constructors on the Types to be trimmed. Fix #50353 --- .../src/System/RuntimeHandles.cs | 15 +++++++++++++-- .../src/System/Collections/Generic/Comparer.cs | 2 +- .../Collections/Generic/EqualityComparer.cs | 2 +- .../src/System/RuntimeType.Mono.cs | 8 ++++++-- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs index f5eb863c158e..6e99db321a05 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -209,8 +209,13 @@ internal static bool HasElementType(RuntimeType type) return outHandles; } - internal static object CreateInstanceForAnotherGenericParameter([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType type, RuntimeType genericParameter) + internal static object CreateInstanceForAnotherGenericParameter( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] RuntimeType type, + RuntimeType genericParameter) { + Debug.Assert(type.GetConstructor(Type.EmptyTypes) is ConstructorInfo c && c.IsPublic, + $"CreateInstanceForAnotherGenericParameter requires {nameof(type)} to have a public parameterless constructor so it can be annotated for trimming without preserving private constructors."); + object? instantiatedObject = null; IntPtr typeHandle = genericParameter.GetTypeHandleInternal().Value; @@ -224,8 +229,14 @@ internal static object CreateInstanceForAnotherGenericParameter([DynamicallyAcce return instantiatedObject!; } - internal static object CreateInstanceForAnotherGenericParameter([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType type, RuntimeType genericParameter1, RuntimeType genericParameter2) + internal static object CreateInstanceForAnotherGenericParameter( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] RuntimeType type, + RuntimeType genericParameter1, + RuntimeType genericParameter2) { + Debug.Assert(type.GetConstructor(Type.EmptyTypes) is ConstructorInfo c && c.IsPublic, + $"CreateInstanceForAnotherGenericParameter requires {nameof(type)} to have a public parameterless constructor so it can be annotated for trimming without preserving private constructors."); + object? instantiatedObject = null; IntPtr* pTypeHandles = stackalloc IntPtr[] diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Comparer.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Comparer.cs index c21928a7381f..cdfc29a7a3e4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Comparer.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Comparer.cs @@ -120,7 +120,7 @@ public override int GetHashCode() => [Serializable] internal sealed partial class EnumComparer : Comparer, ISerializable where T : struct, Enum { - internal EnumComparer() { } + public EnumComparer() { } // Used by the serialization engine. private EnumComparer(SerializationInfo info, StreamingContext context) { } diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs index 0af18e7f7dd1..e7f23a80ab5f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs @@ -180,7 +180,7 @@ public override int GetHashCode() => // Needs to be public to support binary serialization compatibility public sealed partial class EnumEqualityComparer : EqualityComparer, ISerializable where T : struct, Enum { - internal EnumEqualityComparer() { } + public EnumEqualityComparer() { } // This is used by the serialization engine. private EnumEqualityComparer(SerializationInfo information, StreamingContext context) { } diff --git a/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs b/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs index 7e54e26fe994..f58b2d5bd4cc 100644 --- a/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs @@ -1835,11 +1835,15 @@ public override Type[] GetGenericParameterConstraints() return constraints ?? Type.EmptyTypes; } - internal static object CreateInstanceForAnotherGenericParameter([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.NonPublicConstructors)] Type genericType, RuntimeType genericArgument) + internal static object CreateInstanceForAnotherGenericParameter( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type genericType, + RuntimeType genericArgument) { var gt = (RuntimeType)MakeGenericType(genericType, new Type[] { genericArgument }); RuntimeConstructorInfo? ctor = gt.GetDefaultConstructor(); - if (ctor is null) + + // CreateInstanceForAnotherGenericParameter requires type to have a public parameterless constructor so it can be annotated for trimming without preserving private constructors. + if (ctor is null || !ctor.IsPublic) throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, gt)); return ctor.InternalInvoke(null, null, wrapExceptions: true)!; From 0b9dcc5bd0c63103bcd08639363bb8ef3697b76c Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Tue, 30 Mar 2021 00:01:14 -0700 Subject: [PATCH 34/98] ignore empty domain for digets auth (#50348) * ignore empty domain for digets auth * style cleanup --- .../Net/Http/HttpClientHandlerTest.Authentication.cs | 8 +++++++- .../SocketsHttpHandler/AuthenticationHelper.Digest.cs | 9 +++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Authentication.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Authentication.cs index 3dd6e7ed9ba7..4df4100a8ed4 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Authentication.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Authentication.cs @@ -33,7 +33,6 @@ public abstract class HttpClientHandler_Authentication_Test : HttpClientHandlerT private async Task CreateAndValidateRequest(HttpClientHandler handler, Uri url, HttpStatusCode expectedStatusCode, ICredentials credentials) { handler.Credentials = credentials; - using (HttpClient client = CreateHttpClient(handler)) using (HttpResponseMessage response = await client.GetAsync(url)) { @@ -94,6 +93,13 @@ public static IEnumerable Authentication_SocketsHttpHandler_TestData() yield return new object[] { $"Digest realm=\"testrealm\", algorithm=sha-256, nonce=\"testnonce\"", true }; yield return new object[] { $"Digest realm=\"testrealm\", algorithm=sha-256-SESS, nonce=\"testnonce\", qop=\"auth\"", true }; } + + // Add tests cases for empty values that are not mandatory + if (!IsWinHttpHandler) + { + yield return new object[] { "Digest realm=\"testrealm\",nonce=\"6afd170437eb5144258b308f7c491d96\",opaque=\"\",stale=FALSE,algorithm=MD5,qop=\"auth\"", true }; + yield return new object[] { "Digest realm=\"testrealm\", domain=\"\", nonce=\"NA42+vpOFQd1GwCyVRZuhhy+jDn4BMRl\", algorithm=MD5, qop=\"auth\", stale=false", true }; + } } [Theory] diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.Digest.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.Digest.cs index c9d9c7a8a02e..c44dac794eee 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.Digest.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.Digest.cs @@ -18,6 +18,7 @@ internal static partial class AuthenticationHelper private const string Qop = "qop"; private const string Auth = "auth"; private const string AuthInt = "auth-int"; + private const string Domain = "domain"; private const string Nonce = "nonce"; private const string NC = "nc"; private const string Realm = "realm"; @@ -402,9 +403,13 @@ private void Parse(string challenge) // Get the value. string? value = GetNextValue(challenge, parsedIndex, MustValueBeQuoted(key), out parsedIndex); + if (value == null) + break; + // Ensure value is valid. - if (string.IsNullOrEmpty(value) - && (value == null || !key.Equals(Opaque, StringComparison.OrdinalIgnoreCase))) + // Opaque and Domain can have empty string + if (value == string.Empty && + (!key.Equals(Opaque, StringComparison.OrdinalIgnoreCase) && !key.Equals(Domain, StringComparison.OrdinalIgnoreCase))) break; // Add the key-value pair to Parameters. From 82c705102a34c36c5395eb8804109f54c9750d90 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Tue, 30 Mar 2021 00:24:50 -0700 Subject: [PATCH 35/98] [WASM] Trim TimeZoneInfo to reduce size (#50266) * Trim TimeZoneInfo to reduce wasm size * Use partial class files instead of ifdefs --- .../CMakeLists.txt | 6 +- .../System.Private.CoreLib.Shared.projitems | 8 +- .../TimeZoneInfo.FullGlobalizationData.cs | 259 ++++++++++++++++++ .../src/System/TimeZoneInfo.GetDisplayName.cs | 66 ----- .../TimeZoneInfo.MinimalGlobalizationData.cs | 25 ++ .../src/System/TimeZoneInfo.Unix.cs | 208 +------------- 6 files changed, 300 insertions(+), 272 deletions(-) create mode 100644 src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.FullGlobalizationData.cs delete mode 100644 src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.GetDisplayName.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.MinimalGlobalizationData.cs diff --git a/src/libraries/Native/Unix/System.Globalization.Native/CMakeLists.txt b/src/libraries/Native/Unix/System.Globalization.Native/CMakeLists.txt index c399629400f4..797ae3ceb200 100644 --- a/src/libraries/Native/Unix/System.Globalization.Native/CMakeLists.txt +++ b/src/libraries/Native/Unix/System.Globalization.Native/CMakeLists.txt @@ -61,10 +61,14 @@ set(NATIVEGLOBALIZATION_SOURCES pal_localeNumberData.c pal_localeStringData.c pal_normalization.c - pal_timeZoneInfo.c pal_icushim.c ) +# time zone names are filtered out of icu data for the browser and associated functionality is disabled +if (NOT CLR_CMAKE_TARGET_BROWSER) + set(NATIVEGLOBALIZATION_SOURCES ${NATIVEGLOBALIZATION_SOURCES} pal_timeZoneInfo.c) +endif() + if (NOT GEN_SHARED_LIB AND NOT CLR_CMAKE_TARGET_MACCATALYST AND NOT CLR_CMAKE_TARGET_IOS AND NOT CLR_CMAKE_TARGET_TVOS AND NOT CLR_CMAKE_TARGET_ANDROID AND NOT CLR_CMAKE_TARGET_BROWSER) set(NATIVEGLOBALIZATION_SOURCES ${NATIVEGLOBALIZATION_SOURCES} entrypoints.c) endif() diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index d6c316b2fb8f..b294ebe1ce10 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1097,10 +1097,10 @@ Common\Interop\Interop.ResultCode.cs - + Common\Interop\Interop.TimeZoneDisplayNameType.cs - + Common\Interop\Interop.TimeZoneInfo.cs @@ -1900,7 +1900,7 @@ - + @@ -1909,7 +1909,7 @@ - + diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.FullGlobalizationData.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.FullGlobalizationData.cs new file mode 100644 index 000000000000..34e1f5adf691 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.FullGlobalizationData.cs @@ -0,0 +1,259 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; + +namespace System +{ + public sealed partial class TimeZoneInfo + { + private const string FallbackCultureName = "en-US"; + private const string GmtId = "GMT"; + + // Some time zones may give better display names using their location names rather than their generic name. + // We can update this list as need arises. + private static readonly string[] s_ZonesThatUseLocationName = new[] { + "Europe/Minsk", // Prefer "Belarus Time" over "Moscow Standard Time (Minsk)" + "Europe/Moscow", // Prefer "Moscow Time" over "Moscow Standard Time" + "Europe/Simferopol", // Prefer "Simferopol Time" over "Moscow Standard Time (Simferopol)" + "Pacific/Apia", // Prefer "Samoa Time" over "Apia Time" + "Pacific/Pitcairn" // Prefer "Pitcairn Islands Time" over "Pitcairn Time" + }; + + // Main function that is called during construction to populate the three display names + private static void TryPopulateTimeZoneDisplayNamesFromGlobalizationData(string timeZoneId, TimeSpan baseUtcOffset, ref string? standardDisplayName, ref string? daylightDisplayName, ref string? displayName) + { + // Determine the culture to use + CultureInfo uiCulture = CultureInfo.CurrentUICulture; + if (uiCulture.Name.Length == 0) + uiCulture = CultureInfo.GetCultureInfo(FallbackCultureName); // ICU doesn't work nicely with InvariantCulture + + // Attempt to populate the fields backing the StandardName, DaylightName, and DisplayName from globalization data. + GetDisplayName(timeZoneId, Interop.Globalization.TimeZoneDisplayNameType.Standard, uiCulture.Name, ref standardDisplayName); + GetDisplayName(timeZoneId, Interop.Globalization.TimeZoneDisplayNameType.DaylightSavings, uiCulture.Name, ref daylightDisplayName); + GetFullValueForDisplayNameField(timeZoneId, baseUtcOffset, uiCulture, ref displayName); + } + + // Helper function to get the standard display name for the UTC static time zone instance + private static string GetUtcStandardDisplayName() + { + // Don't bother looking up the name for invariant or English cultures + CultureInfo uiCulture = CultureInfo.CurrentUICulture; + if (GlobalizationMode.Invariant || uiCulture.Name.Length == 0 || uiCulture.TwoLetterISOLanguageName == "en") + return InvariantUtcStandardDisplayName; + + // Try to get a localized version of "Coordinated Universal Time" from the globalization data + string? standardDisplayName = null; + GetDisplayName(UtcId, Interop.Globalization.TimeZoneDisplayNameType.Standard, uiCulture.Name, ref standardDisplayName); + + // Final safety check. Don't allow null or abbreviations + if (standardDisplayName == null || standardDisplayName == "GMT" || standardDisplayName == "UTC") + standardDisplayName = InvariantUtcStandardDisplayName; + + return standardDisplayName; + } + + // Helper function that retrieves various forms of time zone display names from ICU + private static unsafe void GetDisplayName(string timeZoneId, Interop.Globalization.TimeZoneDisplayNameType nameType, string uiCulture, ref string? displayName) + { + if (GlobalizationMode.Invariant) + { + return; + } + + string? timeZoneDisplayName; + bool result = Interop.CallStringMethod( + (buffer, locale, id, type) => + { + fixed (char* bufferPtr = buffer) + { + return Interop.Globalization.GetTimeZoneDisplayName(locale, id, type, bufferPtr, buffer.Length); + } + }, + uiCulture, + timeZoneId, + nameType, + out timeZoneDisplayName); + + if (!result && uiCulture != FallbackCultureName) + { + // Try to fallback using FallbackCultureName just in case we can make it work. + result = Interop.CallStringMethod( + (buffer, locale, id, type) => + { + fixed (char* bufferPtr = buffer) + { + return Interop.Globalization.GetTimeZoneDisplayName(locale, id, type, bufferPtr, buffer.Length); + } + }, + FallbackCultureName, + timeZoneId, + nameType, + out timeZoneDisplayName); + } + + // If there is an unknown error, don't set the displayName field. + // It will be set to the abbreviation that was read out of the tzfile. + if (result && !string.IsNullOrEmpty(timeZoneDisplayName)) + { + displayName = timeZoneDisplayName; + } + } + + // Helper function that builds the value backing the DisplayName field from globalization data. + private static void GetFullValueForDisplayNameField(string timeZoneId, TimeSpan baseUtcOffset, CultureInfo uiCulture, ref string? displayName) + { + // There are a few diffent ways we might show the display name depending on the data. + // The algorithm used below should avoid duplicating the same words while still achieving the + // goal of providing a unique, discoverable, and intuitive name. + + // Try to get the generic name for this time zone. + string? genericName = null; + GetDisplayName(timeZoneId, Interop.Globalization.TimeZoneDisplayNameType.Generic, uiCulture.Name, ref genericName); + if (genericName == null) + { + // We'll use the fallback display name value already set. + return; + } + + // Get the base offset to prefix in front of the time zone. + // Only UTC and its aliases have "(UTC)", handled earlier. All other zones include an offset, even if it's zero. + string baseOffsetText = $"(UTC{(baseUtcOffset >= TimeSpan.Zero ? '+' : '-')}{baseUtcOffset:hh\\:mm})"; + + // Get the generic location name. + string? genericLocationName = null; + GetDisplayName(timeZoneId, Interop.Globalization.TimeZoneDisplayNameType.GenericLocation, uiCulture.Name, ref genericLocationName); + + // Some edge cases only apply when the offset is +00:00. + if (baseUtcOffset == TimeSpan.Zero) + { + // GMT and its aliases will just use the equivalent of "Greenwich Mean Time". + string? gmtLocationName = null; + GetDisplayName(GmtId, Interop.Globalization.TimeZoneDisplayNameType.GenericLocation, uiCulture.Name, ref gmtLocationName); + if (genericLocationName == gmtLocationName) + { + displayName = $"{baseOffsetText} {genericName}"; + return; + } + + // Other zones with a zero offset and the equivalent of "Greenwich Mean Time" should only use the location name. + // For example, prefer "Iceland Time" over "Greenwich Mean Time (Reykjavik)". + string? gmtGenericName = null; + GetDisplayName(GmtId, Interop.Globalization.TimeZoneDisplayNameType.Generic, uiCulture.Name, ref gmtGenericName); + if (genericName == gmtGenericName) + { + displayName = $"{baseOffsetText} {genericLocationName}"; + return; + } + } + + if (genericLocationName == genericName) + { + // When the location name is the same as the generic name, + // then it is generally good enough to show by itself. + + // *** Example (en-US) *** + // id = "America/Havana" + // baseOffsetText = "(UTC-05:00)" + // standardName = "Cuba Standard Time" + // genericName = "Cuba Time" + // genericLocationName = "Cuba Time" + // exemplarCityName = "Havana" + // displayName = "(UTC-05:00) Cuba Time" + + displayName = $"{baseOffsetText} {genericLocationName}"; + return; + } + + // Prefer location names in some special cases. + if (StringArrayContains(timeZoneId, s_ZonesThatUseLocationName, StringComparison.OrdinalIgnoreCase)) + { + displayName = $"{baseOffsetText} {genericLocationName}"; + return; + } + + // See if we should include the exemplar city name. + string exemplarCityName = GetExemplarCityName(timeZoneId, uiCulture.Name); + if (uiCulture.CompareInfo.IndexOf(genericName, exemplarCityName, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace) >= 0 && genericLocationName != null) + { + // When an exemplar city is already part of the generic name, + // there's no need to repeat it again so just use the generic name. + + // *** Example (fr-FR) *** + // id = "Australia/Lord_Howe" + // baseOffsetText = "(UTC+10:30)" + // standardName = "heure normale de Lord Howe" + // genericName = "heure de Lord Howe" + // genericLocationName = "heure : Lord Howe" + // exemplarCityName = "Lord Howe" + // displayName = "(UTC+10:30) heure de Lord Howe" + + displayName = $"{baseOffsetText} {genericName}"; + } + else + { + // Finally, use the generic name and the exemplar city together. + // This provides an intuitive name and still disambiguates. + + // *** Example (en-US) *** + // id = "Europe/Rome" + // baseOffsetText = "(UTC+01:00)" + // standardName = "Central European Standard Time" + // genericName = "Central European Time" + // genericLocationName = "Italy Time" + // exemplarCityName = "Rome" + // displayName = "(UTC+01:00) Central European Time (Rome)" + + displayName = $"{baseOffsetText} {genericName} ({exemplarCityName})"; + } + } + + // Helper function that gets an exmplar city name either from ICU or from the IANA time zone ID itself + private static string GetExemplarCityName(string timeZoneId, string uiCultureName) + { + // First try to get the name through the localization data. + string? exemplarCityName = null; + GetDisplayName(timeZoneId, Interop.Globalization.TimeZoneDisplayNameType.ExemplarCity, uiCultureName, ref exemplarCityName); + if (!string.IsNullOrEmpty(exemplarCityName)) + return exemplarCityName; + + // Support for getting exemplar city names was added in ICU 51. + // We may have an older version. For example, in Helix we test on RHEL 7.5 which uses ICU 50.1.2. + // We'll fallback to using an English name generated from the time zone ID. + int i = timeZoneId.LastIndexOf('/'); + return timeZoneId.Substring(i + 1).Replace('_', ' '); + } + + // Helper function that returns an alternative ID using ICU data. Used primarily for converting from Windows IDs. + private static unsafe string? GetAlternativeId(string id) + { + if (!GlobalizationMode.Invariant) + { + if (id.Equals("utc", StringComparison.OrdinalIgnoreCase)) + { + // Special case UTC, as previously ICU would convert it to "Etc/GMT" which is incorrect name for UTC. + return "Etc/UTC"; + } + + foreach (char c in id) + { + // ICU uses some characters as a separator and trim the id at that character. + // while we should fail if the Id contained one of these characters. + if (c == '\\' || c == '\n' || c == '\r') + { + return null; + } + } + + char* buffer = stackalloc char[100]; + int length = Interop.Globalization.WindowsIdToIanaId(id, buffer, 100); + if (length > 0) + { + return new string(buffer, 0, length); + } + } + + return null; + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.GetDisplayName.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.GetDisplayName.cs deleted file mode 100644 index ba54330cf891..000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.GetDisplayName.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Buffers; -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.IO; -using System.Text; -using System.Threading; -using System.Security; - -using Internal.IO; - -namespace System -{ - public sealed partial class TimeZoneInfo - { - private static unsafe void GetDisplayName(string timeZoneId, Interop.Globalization.TimeZoneDisplayNameType nameType, string uiCulture, ref string? displayName) - { - if (GlobalizationMode.Invariant) - { - return; - } - - string? timeZoneDisplayName; - bool result = Interop.CallStringMethod( - (buffer, locale, id, type) => - { - fixed (char* bufferPtr = buffer) - { - return Interop.Globalization.GetTimeZoneDisplayName(locale, id, type, bufferPtr, buffer.Length); - } - }, - uiCulture, - timeZoneId, - nameType, - out timeZoneDisplayName); - - if (!result && uiCulture != FallbackCultureName) - { - // Try to fallback using FallbackCultureName just in case we can make it work. - result = Interop.CallStringMethod( - (buffer, locale, id, type) => - { - fixed (char* bufferPtr = buffer) - { - return Interop.Globalization.GetTimeZoneDisplayName(locale, id, type, bufferPtr, buffer.Length); - } - }, - FallbackCultureName, - timeZoneId, - nameType, - out timeZoneDisplayName); - } - - // If there is an unknown error, don't set the displayName field. - // It will be set to the abbreviation that was read out of the tzfile. - if (result && !string.IsNullOrEmpty(timeZoneDisplayName)) - { - displayName = timeZoneDisplayName; - } - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.MinimalGlobalizationData.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.MinimalGlobalizationData.cs new file mode 100644 index 000000000000..9da75786bed1 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.MinimalGlobalizationData.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System +{ + public sealed partial class TimeZoneInfo + { + private static void TryPopulateTimeZoneDisplayNamesFromGlobalizationData(string timeZoneId, TimeSpan baseUtcOffset, ref string? standardDisplayName, ref string? daylightDisplayName, ref string? displayName) + { + // Do nothing. We'll use the fallback values already set. + } + + private static string GetUtcStandardDisplayName() + { + // Just use the invariant display name. + return InvariantUtcStandardDisplayName; + } + + private static string? GetAlternativeId(string id) + { + // No alternative IDs in this target. + return null; + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs index 5c8a9a755516..3bd7e2f569b2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs @@ -22,12 +22,10 @@ public sealed partial class TimeZoneInfo private const string ZoneTabFileName = "zone.tab"; private const string TimeZoneEnvironmentVariable = "TZ"; private const string TimeZoneDirectoryEnvironmentVariable = "TZDIR"; - private const string FallbackCultureName = "en-US"; - private const string GmtId = "GMT"; // UTC aliases per https://github.com/unicode-org/cldr/blob/master/common/bcp47/timezone.xml - // Hard-coded because we need to treat all aliases of UTC the same even when ICU is not available, - // or when we get "GMT" returned from older ICU versions. (This list is not likely to change.) + // Hard-coded because we need to treat all aliases of UTC the same even when globalization data is not available. + // (This list is not likely to change.) private static readonly string[] s_UtcAliases = new[] { "Etc/UTC", "Etc/UCT", @@ -39,16 +37,6 @@ public sealed partial class TimeZoneInfo "Zulu" }; - // Some time zones may give better display names using their location names rather than their generic name. - // We can update this list as need arises. - private static readonly string[] s_ZonesThatUseLocationName = new[] { - "Europe/Minsk", // Prefer "Belarus Time" over "Moscow Standard Time (Minsk)" - "Europe/Moscow", // Prefer "Moscow Time" over "Moscow Standard Time" - "Europe/Simferopol", // Prefer "Simferopol Time" over "Moscow Standard Time (Simferopol)" - "Pacific/Apia", // Prefer "Samoa Time" over "Apia Time" - "Pacific/Pitcairn" // Prefer "Pitcairn Islands Time" over "Pitcairn Time" - }; - private TimeZoneInfo(byte[] data, string id, bool dstDisabled) { _id = id; @@ -114,20 +102,14 @@ private TimeZoneInfo(byte[] data, string id, bool dstDisabled) } } - // Use abbrev as the fallback + // Set fallback values using abbreviations, base offset, and id + // These are expected in environments without time zone globalization data _standardDisplayName = standardAbbrevName; _daylightDisplayName = daylightAbbrevName ?? standardAbbrevName; - _displayName = _standardDisplayName; - - // Determine the culture to use - CultureInfo uiCulture = CultureInfo.CurrentUICulture; - if (uiCulture.Name.Length == 0) - uiCulture = CultureInfo.GetCultureInfo(FallbackCultureName); // ICU doesn't work nicely with InvariantCulture + _displayName = $"(UTC{(_baseUtcOffset >= TimeSpan.Zero ? '+' : '-')}{_baseUtcOffset:hh\\:mm}) {_id}"; - // Attempt to populate the fields backing the StandardName, DaylightName, and DisplayName from globalization data. - GetDisplayName(_id, Interop.Globalization.TimeZoneDisplayNameType.Standard, uiCulture.Name, ref _standardDisplayName); - GetDisplayName(_id, Interop.Globalization.TimeZoneDisplayNameType.DaylightSavings, uiCulture.Name, ref _daylightDisplayName); - GetFullValueForDisplayNameField(_id, _baseUtcOffset, uiCulture, ref _displayName); + // Try to populate the display names from the globalization data + TryPopulateTimeZoneDisplayNamesFromGlobalizationData(_id, _baseUtcOffset, ref _standardDisplayName, ref _daylightDisplayName, ref _displayName); // TZif supports seconds-level granularity with offsets but TimeZoneInfo only supports minutes since it aligns // with DateTimeOffset, SQL Server, and the W3C XML Specification @@ -145,133 +127,6 @@ private TimeZoneInfo(byte[] data, string id, bool dstDisabled) ValidateTimeZoneInfo(_id, _baseUtcOffset, _adjustmentRules, out _supportsDaylightSavingTime); } - // Helper function that builds the value backing the DisplayName field from gloablization data. - private static void GetFullValueForDisplayNameField(string timeZoneId, TimeSpan baseUtcOffset, CultureInfo uiCulture, ref string? displayName) - { - // There are a few diffent ways we might show the display name depending on the data. - // The algorithm used below should avoid duplicating the same words while still achieving the - // goal of providing a unique, discoverable, and intuitive name. - - // Get the base offset to prefix in front of the time zone. - // Only UTC and its aliases have "(UTC)", handled earlier. All other zones include an offset, even if it's zero. - string baseOffsetText = $"(UTC{(baseUtcOffset >= TimeSpan.Zero ? '+' : '-')}{baseUtcOffset:hh\\:mm})"; - - // Try to get the generic name for this time zone. - string? genericName = null; - GetDisplayName(timeZoneId, Interop.Globalization.TimeZoneDisplayNameType.Generic, uiCulture.Name, ref genericName); - - if (genericName == null) - { - // When we can't get a generic name, use the offset and the ID. - // It is not ideal, but at least it is non-ambiguous. - // (Note, UTC was handled already above.) - displayName = $"{baseOffsetText} {timeZoneId}"; - return; - } - - // Get the generic location name. - string? genericLocationName = null; - GetDisplayName(timeZoneId, Interop.Globalization.TimeZoneDisplayNameType.GenericLocation, uiCulture.Name, ref genericLocationName); - - // Some edge cases only apply when the offset is +00:00. - if (baseUtcOffset == TimeSpan.Zero) - { - // GMT and its aliases will just use the equivalent of "Greenwich Mean Time". - string? gmtLocationName = null; - GetDisplayName(GmtId, Interop.Globalization.TimeZoneDisplayNameType.GenericLocation, uiCulture.Name, ref gmtLocationName); - if (genericLocationName == gmtLocationName) - { - displayName = $"{baseOffsetText} {genericName}"; - return; - } - - // Other zones with a zero offset and the equivalent of "Greenwich Mean Time" should only use the location name. - // For example, prefer "Iceland Time" over "Greenwich Mean Time (Reykjavik)". - string? gmtGenericName = null; - GetDisplayName(GmtId, Interop.Globalization.TimeZoneDisplayNameType.Generic, uiCulture.Name, ref gmtGenericName); - if (genericName == gmtGenericName) - { - displayName = $"{baseOffsetText} {genericLocationName}"; - return; - } - } - - if (genericLocationName == genericName) - { - // When the location name is the same as the generic name, - // then it is generally good enough to show by itself. - - // *** Example (en-US) *** - // id = "America/Havana" - // baseOffsetText = "(UTC-05:00)" - // standardName = "Cuba Standard Time" - // genericName = "Cuba Time" - // genericLocationName = "Cuba Time" - // exemplarCityName = "Havana" - // displayName = "(UTC-05:00) Cuba Time" - - displayName = $"{baseOffsetText} {genericLocationName}"; - return; - } - - // Prefer location names in some special cases. - if (StringArrayContains(timeZoneId, s_ZonesThatUseLocationName, StringComparison.OrdinalIgnoreCase)) - { - displayName = $"{baseOffsetText} {genericLocationName}"; - return; - } - - // See if we should include the exemplar city name. - string exemplarCityName = GetExemplarCityName(timeZoneId, uiCulture.Name); - if (uiCulture.CompareInfo.IndexOf(genericName, exemplarCityName, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace) >= 0 && genericLocationName != null) - { - // When an exemplar city is already part of the generic name, - // there's no need to repeat it again so just use the generic name. - - // *** Example (fr-FR) *** - // id = "Australia/Lord_Howe" - // baseOffsetText = "(UTC+10:30)" - // standardName = "heure normale de Lord Howe" - // genericName = "heure de Lord Howe" - // genericLocationName = "heure : Lord Howe" - // exemplarCityName = "Lord Howe" - // displayName = "(UTC+10:30) heure de Lord Howe" - - displayName = $"{baseOffsetText} {genericName}"; - } - else - { - // Finally, use the generic name and the exemplar city together. - // This provides an intuitive name and still disambiguates. - - // *** Example (en-US) *** - // id = "Europe/Rome" - // baseOffsetText = "(UTC+01:00)" - // standardName = "Central European Standard Time" - // genericName = "Central European Time" - // genericLocationName = "Italy Time" - // exemplarCityName = "Rome" - // displayName = "(UTC+01:00) Central European Time (Rome)" - - displayName = $"{baseOffsetText} {genericName} ({exemplarCityName})"; - } - } - - private static string GetExemplarCityName(string timeZoneId, string uiCultureName) - { - // First try to get the name through the localization data. - string? exemplarCityName = null; - GetDisplayName(timeZoneId, Interop.Globalization.TimeZoneDisplayNameType.ExemplarCity, uiCultureName, ref exemplarCityName); - if (!string.IsNullOrEmpty(exemplarCityName)) - return exemplarCityName; - - // Support for getting exemplar city names was added in ICU 51. - // We may have an older version. For example, in Helix we test on RHEL 7.5 which uses ICU 50.1.2. - // We'll fallback to using an English name generated from the time zone ID. - int i = timeZoneId.LastIndexOf('/'); - return timeZoneId.Substring(i + 1).Replace('_', ' '); - } - // The TransitionTime fields are not used when AdjustmentRule.NoDaylightTransitions == true. // However, there are some cases in the past where DST = true, and the daylight savings offset // now equals what the current BaseUtcOffset is. In that case, the AdjustmentRule.DaylightOffset @@ -380,36 +235,6 @@ private static void PopulateAllSystemTimeZones(CachedData cachedData) } } - private static unsafe string? GetAlternativeId(string id) - { - if (!GlobalizationMode.Invariant) - { - if (id.Equals("utc", StringComparison.OrdinalIgnoreCase)) - { - //special case UTC as ICU will convert it to "Etc/GMT" which is incorrect name for UTC. - return "Etc/UTC"; - } - foreach (char c in id) - { - // ICU uses some characters as a separator and trim the id at that character. - // while we should fail if the Id contained one of these characters. - if (c == '\\' || c == '\n' || c == '\r') - { - return null; - } - } - - char* buffer = stackalloc char[100]; - int length = Interop.Globalization.WindowsIdToIanaId(id, buffer, 100); - if (length > 0) - { - return new string(buffer, 0, length); - } - } - - return null; - } - /// /// Helper function for retrieving the local system time zone. /// May throw COMException, TimeZoneNotFoundException, InvalidTimeZoneException. @@ -1979,24 +1804,5 @@ private static bool StringArrayContains(string value, string[] source, StringCom return false; } - - // Helper function to get the standard display name for the UTC static time zone instance - private static string GetUtcStandardDisplayName() - { - // Don't bother looking up the name for invariant or English cultures - CultureInfo uiCulture = CultureInfo.CurrentUICulture; - if (GlobalizationMode.Invariant || uiCulture.Name.Length == 0 || uiCulture.TwoLetterISOLanguageName == "en") - return InvariantUtcStandardDisplayName; - - // Try to get a localized version of "Coordinated Universal Time" from the globalization data - string? standardDisplayName = null; - GetDisplayName(UtcId, Interop.Globalization.TimeZoneDisplayNameType.Standard, uiCulture.Name, ref standardDisplayName); - - // Final safety check. Don't allow null or abbreviations - if (standardDisplayName == null || standardDisplayName == "GMT" || standardDisplayName == "UTC") - standardDisplayName = InvariantUtcStandardDisplayName; - - return standardDisplayName; - } } } From ee0c5a2d58f07b44d593c82346964b20e2dde58d Mon Sep 17 00:00:00 2001 From: Lukas Lansky Date: Tue, 30 Mar 2021 11:48:34 +0200 Subject: [PATCH 36/98] Move publishing off hosted ADO machines, again (#50405) Co-authored-by: Matt Galbraith --- eng/common/post-build/publish-using-darc.ps1 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/eng/common/post-build/publish-using-darc.ps1 b/eng/common/post-build/publish-using-darc.ps1 index 2427ca6b6aec..b6e55f5e74a7 100644 --- a/eng/common/post-build/publish-using-darc.ps1 +++ b/eng/common/post-build/publish-using-darc.ps1 @@ -55,12 +55,15 @@ try { $optionalParams.Add($SigningValidationAdditionalParameters) | Out-Null } } - + # note the custom branch to work around https://github.com/dotnet/arcade/issues/6987 . Try to keep this rebased off master if publishing changes + + Write-Host "Using Arcade branch master-workaround-publishing-issue to work around disk space issues with alternate build pool. Will be lost on Arcade updates as this is in eng common. Contact dnceng for questions." + & $darc add-build-to-channel ` --id $buildId ` --publishing-infra-version $PublishingInfraVersion ` --default-channels ` - --source-branch main ` + --source-branch master-workaround-publishing-issue ` --azdev-pat $AzdoToken ` --bar-uri $MaestroApiEndPoint ` --password $MaestroToken ` From ff27f8299253af1e6cfbe3fb184a055d047a9247 Mon Sep 17 00:00:00 2001 From: Ulrich Weigand Date: Tue, 30 Mar 2021 11:51:36 +0200 Subject: [PATCH 37/98] More Mono unwind fixes for s390x (#49986) * mono_arch_unwind_frame: Handle FPRs during unwind. (This is important since GCC will spill GPRs to FPRs instead of the stack when beneficial.) * map_hw_reg_to_dwarf_reg: Implement DWARF register number mapping for FPRs. (https://github.com/IBM/s390x-abi/releases/download/v1.5/lzsabi_s390x.pdf) * mono_arch_create_generic_trampoline: Fix incorrect start address. --- src/mono/mono/mini/exceptions-s390x.c | 10 ++++++---- src/mono/mono/mini/tramp-s390x.c | 2 +- src/mono/mono/mini/unwind.c | 7 ++++--- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/mono/mono/mini/exceptions-s390x.c b/src/mono/mono/mini/exceptions-s390x.c index 1ba005b5cfab..0df2507cc8bb 100644 --- a/src/mono/mono/mini/exceptions-s390x.c +++ b/src/mono/mono/mini/exceptions-s390x.c @@ -521,7 +521,7 @@ mono_arch_unwind_frame (MonoJitTlsData *jit_tls, guint8 *cfa; guint32 unwind_info_len; guint8 *unwind_info; - host_mgreg_t regs[16]; + host_mgreg_t regs[32]; if (ji->is_trampoline) frame->type = FRAME_TYPE_TRAMPOLINE; @@ -535,16 +535,18 @@ mono_arch_unwind_frame (MonoJitTlsData *jit_tls, if (ji->has_arch_eh_info) epilog = (guint8*)ji->code_start + ji->code_size - mono_jinfo_get_epilog_size (ji); - memcpy(®s, &ctx->uc_mcontext.gregs, sizeof(regs)); + memcpy (®s[0], &ctx->uc_mcontext.gregs, 16 * sizeof(host_mgreg_t)); + memcpy (®s[16], &ctx->uc_mcontext.fpregs.fprs, 16 * sizeof(host_mgreg_t)); gboolean success = mono_unwind_frame (unwind_info, unwind_info_len, ji->code_start, (guint8 *) ji->code_start + ji->code_size, - ip, epilog ? &epilog : NULL, regs, 16, save_locations, + ip, epilog ? &epilog : NULL, regs, 32, save_locations, MONO_MAX_IREGS, &cfa); if (!success) return FALSE; - memcpy (&new_ctx->uc_mcontext.gregs, ®s, sizeof(regs)); + memcpy (&new_ctx->uc_mcontext.gregs, ®s[0], 16 * sizeof(host_mgreg_t)); + memcpy (&new_ctx->uc_mcontext.fpregs.fprs, ®s[16], 16 * sizeof(host_mgreg_t)); MONO_CONTEXT_SET_IP(new_ctx, regs[14] - 2); MONO_CONTEXT_SET_BP(new_ctx, regs[15]); MONO_CONTEXT_SET_SP(new_ctx, regs[15]); diff --git a/src/mono/mono/mini/tramp-s390x.c b/src/mono/mono/mini/tramp-s390x.c index 92553ce209b1..d5d69093444d 100644 --- a/src/mono/mono/mini/tramp-s390x.c +++ b/src/mono/mono/mini/tramp-s390x.c @@ -510,7 +510,7 @@ mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInf g_assert (info); tramp_name = mono_get_generic_trampoline_name (tramp_type); - *info = mono_tramp_info_create (tramp_name, buf, buf - code, ji, unwind_ops); + *info = mono_tramp_info_create (tramp_name, code, buf - code, ji, unwind_ops); /* Sanity check */ g_assert ((buf - code) <= 512); diff --git a/src/mono/mono/mini/unwind.c b/src/mono/mono/mini/unwind.c index 4c27edc45313..bcf9bd4682ef 100644 --- a/src/mono/mono/mini/unwind.c +++ b/src/mono/mono/mini/unwind.c @@ -94,12 +94,13 @@ static int map_hw_reg_to_dwarf_reg [ppc_lr + 1] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, #elif defined (TARGET_S390X) /* * 0-15 = GR0-15 - * 16-31 = FP0-15 + * 16-31 = FP0-15 (f0, f2, f4, f6, f1, f3, f5, f7, f8, f10, f12, f14, f9, f11, f13, f15) */ static int map_hw_reg_to_dwarf_reg [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31}; + 16, 20, 17, 21, 18, 22, 19, 23, + 24, 28, 25, 29, 26, 30, 27, 31}; + #define NUM_DWARF_REGS 32 #define DWARF_DATA_ALIGN (-8) #define DWARF_PC_REG (mono_hw_reg_to_dwarf_reg (14)) From ac21254184f5a6cec884d8d4049c80d905170b38 Mon Sep 17 00:00:00 2001 From: Neale Ferguson Date: Tue, 30 Mar 2021 20:22:26 +1000 Subject: [PATCH 38/98] s390x: Fixes to tailcall and array parameter fix (#49466) ### Small s390x fixes - Add another tailcall disqualifying condition - Minor formatting change ### New Array Parameter fix We have been seeing msbuild tasks fail due to an OverflowException being raised in `(wrapper_alloc)_object:AllocVector` because the number of elements appears to be negative (0xffffffff00000088). However, this is just the result of a missing type conversion somehow. The original C# code is simply: ``` _metadataStringTable = new string[_reader.ReadInt32()]; ``` The IL corresponding to that line reads: ``` converting (in B2: stack: 2) IL_0021: callvirt 0x0a00035e cmethod = int System.IO.BinaryReader:ReadInt32 () Call requires: 1 parameters converting (in B2: stack: 2) IL_0026: newarr 0x010000b6 ``` Note that the return from the `ReadInt32` call is of type `int` (i.e. 32 bits) and is passed unmodified as argument to `newarr`. This remains the same in the initial stages of Mono JIT compilation: ``` call_membase R43 <- [R45 + 0xe8] [int System.IO.BinaryReader:ReadInt32 ()] [s390_r2 <- R44] clobbers: c il_seq_point il: 0x26, nonempty-stack newarr R46 <- R43 ``` But later the `newarr` is implemented in terms of a function call: ``` call_membase R43 <- [R45 + 0xe8] [int System.IO.BinaryReader:ReadInt32 ()] [s390_r2 <- R44] clobbers: c il_seq_point il: 0x26, nonempty-stack i8const R81 <- [2929315620864] move R83 <- R81 move R84 <- R43 call R46 <- [(wrapper alloc) object object:AllocVector (intptr,intptr)] [s390_r2 <- R83] [s390_r3 <- R84] clobbers: c ``` And here the argument is of type `intptr` (i.e. 64 bits). But the return value of ReadInt32 is still passed through directly to `AllocVector` without any conversion, and this remains true in the final assembler code: ``` b2: 0d e1 basr %r14,%r1 // ReadInt32 b4: b9 04 00 32 lgr %r3,%r2 // <<- simple move of the return value b8: c0 28 00 00 02 aa iihf %r2,682 be: c0 29 08 d1 28 00 iilf %r2,147924992 c4: c0 e8 00 00 03 ff iihf %r14,1023 ca: c0 e9 9d ec 97 08 iilf %r14,2649528072 d0: 0d ee basr %r14,%r14 // AllocVector ``` This becomes a problem because the implementation of `AllocVector` does indeed expect a 64-bit input, and throws an exception if that is negative. In addition, the implementation of `ReadInt32` only computes a 32-bit result in the low 32 bits of the register, and leaves the upper half undefined. Due to the particular code sequence we get a return with the upper half nonzero even though the lower half is a positive integer: ``` e6: e3 20 f0 e0 00 14 lgf %r2,224(%r15) <<- if this is a 32-bit value with the top bit set, then the upper half of %r2 is now 0xffffffff ec: e3 20 f0 b8 00 50 sty %r2,184(%r15) f2: b9 04 00 32 lgr %r3,%r2 <<- and so is %r3 f6: c0 01 00 ff 00 ff lgfi %r0,16711935 fc: b9 e4 00 43 ngrk %r4,%r3,%r0 100: b9 14 00 24 lgfr %r2,%r4 104: 88 20 00 08 srl %r2,8 108: 89 40 00 18 sll %r4,24 10c: b9 e6 40 22 ogrk %r2,%r2,%r4 110: c0 01 ff 00 ff 00 lgfi %r0,-16711936 <<-- the upper half of %r0 is also 0xffffffff 116: b9 e4 00 43 ngrk %r4,%r3,%r0 <<-- and so is %r4 11a: b9 14 00 34 lgfr %r3,%r4 11e: 89 30 00 08 sll %r3,8 122: 88 40 00 18 srl %r4,24 126: b9 e6 40 33 ogrk %r3,%r3,%r4 <<- and now %r3 12a: b9 08 00 23 agr %r2,%r3 <<- and therefore %r2, where the upper half for 0 before 12e: e3 20 f0 b8 00 50 sty %r2,184(%r15) 134: a7 fb 00 f8 aghi %r15,248 138: eb 6e f0 30 00 04 lmg %r6,%r14,48(%r15) 13e: 07 fe br %r14 <<- and here it is returned unchanged ``` --- src/mono/mono/mini/method-to-ir.c | 15 ++++++++++++++- src/mono/mono/mini/mini-s390x.c | 9 ++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/mono/mono/mini/method-to-ir.c b/src/mono/mono/mini/method-to-ir.c index ed3c96ec246b..88148513cdd5 100644 --- a/src/mono/mono/mini/method-to-ir.c +++ b/src/mono/mono/mini/method-to-ir.c @@ -9798,7 +9798,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b context_used = mini_class_check_context_used (cfg, klass); - if (sp [0]->type == STACK_I8 || (TARGET_SIZEOF_VOID_P == 8 && sp [0]->type == STACK_PTR)) { +#ifndef TARGET_S390X + if (sp [0]->type == STACK_I8 && TARGET_SIZEOF_VOID_P == 4) { MONO_INST_NEW (cfg, ins, OP_LCONV_TO_OVF_U4); ins->sreg1 = sp [0]->dreg; ins->type = STACK_I4; @@ -9806,6 +9807,18 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b MONO_ADD_INS (cfg->cbb, ins); *sp = mono_decompose_opcode (cfg, ins); } +#else + /* The array allocator expects a 64-bit input, and we cannot rely + on the high bits of a 32-bit result, so we have to extend. */ + if (sp [0]->type == STACK_I4 && TARGET_SIZEOF_VOID_P == 8) { + MONO_INST_NEW (cfg, ins, OP_ICONV_TO_I8); + ins->sreg1 = sp [0]->dreg; + ins->type = STACK_I8; + ins->dreg = alloc_ireg (cfg); + MONO_ADD_INS (cfg->cbb, ins); + *sp = mono_decompose_opcode (cfg, ins); + } +#endif if (context_used) { MonoInst *args [3]; diff --git a/src/mono/mono/mini/mini-s390x.c b/src/mono/mono/mini/mini-s390x.c index 835e9486ae22..1cc8f83e2b72 100644 --- a/src/mono/mono/mini/mini-s390x.c +++ b/src/mono/mono/mini/mini-s390x.c @@ -3471,7 +3471,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) if (!cfg->r4fp) s390_ledbr (code, ins->dreg, ins->sreg1); break; - case OP_TLS_GET: { + case OP_TLS_GET: { if (s390_is_imm16 (ins->inst_offset)) { s390_lghi (code, s390_r13, ins->inst_offset); } else if (s390_is_imm32 (ins->inst_offset)) { @@ -5349,8 +5349,7 @@ mono_arch_register_lowlevel_calls (void) */ void -mono_arch_patch_code_new (MonoCompile *cfg, - guint8 *code, MonoJumpInfo *ji, gpointer target) +mono_arch_patch_code_new (MonoCompile *cfg, guint8 *code, MonoJumpInfo *ji, gpointer target) { unsigned char *ip = ji->ip.i + code; gint64 displace; @@ -6956,13 +6955,13 @@ mono_arch_tailcall_supported (MonoCompile *cfg, MonoMethodSignature *caller_sig, res = FALSE; break; case RegTypeStructByAddr : - if (ainfo[i].reg == STK_BASE) + if (ainfo[i].reg == STK_BASE || ainfo[i].reg == S390_LAST_ARG_REG) res = FALSE; else res = TRUE; break; case RegTypeStructByVal : - if (ainfo[i].reg == STK_BASE) + if (ainfo[i].reg == STK_BASE || ainfo[i].reg == S390_LAST_ARG_REG) res = FALSE; else { switch(ainfo[i].size) { From 06ac81accee958128e2833b34ab502d235311c36 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 30 Mar 2021 12:19:31 +0100 Subject: [PATCH 39/98] Add "Help Wanted" badge (#50352) * Add "Help Wanted" badge * Move help wanted second --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 861ff943990d..1444df567a8e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # .NET Runtime - [![Build Status](https://dnceng.visualstudio.com/public/_apis/build/status/dotnet/runtime/runtime?branchName=main)](https://dnceng.visualstudio.com/public/_build/latest?definitionId=686&branchName=main) +[![Help Wanted](https://img.shields.io/github/issues/dotnet/runtime/up-for-grabs?style=flat-square&color=%232EA043&label=help%20wanted)](https://github.com/dotnet/runtime/issues?q=is%3Aissue+is%3Aopen+label%3A%22up-for-grabs%22) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/dotnet/runtime) [![Discord](https://img.shields.io/discord/732297728826277939?style=flat-square&label=Discord&logo=discord&logoColor=white&color=7289DA)](https://aka.ms/dotnet-discord) From 75d8aaed9dcdc2b118c78c45d35c479bde444090 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 30 Mar 2021 07:21:28 -0400 Subject: [PATCH 40/98] [main] Update dependencies from dotnet/runtime dotnet/xharness (#50358) * Update dependencies from https://github.com/dotnet/runtime build 20210328.6 Microsoft.NETCore.DotNetHost , Microsoft.NETCore.DotNetHostPolicy , Microsoft.NETCore.ILAsm , runtime.native.System.IO.Ports , Microsoft.NET.Sdk.IL , System.Runtime.CompilerServices.Unsafe , System.Text.Json From Version 6.0.0-preview.3.21169.6 -> To Version 6.0.0-preview.4.21178.6 * Update dependencies from https://github.com/dotnet/xharness build 20210329.2 Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Xunit From Version 1.0.0-prerelease.21176.1 -> To Version 1.0.0-prerelease.21179.2 Co-authored-by: dotnet-maestro[bot] --- .config/dotnet-tools.json | 2 +- eng/Version.Details.xml | 36 ++++++++++++++++++------------------ eng/Versions.props | 16 ++++++++-------- global.json | 2 +- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 2baea1807598..691e8e80af85 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "1.0.0-prerelease.21176.1", + "version": "1.0.0-prerelease.21179.2", "commands": [ "xharness" ] diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index abea99b10f5f..6f899814ba01 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -166,45 +166,45 @@ https://github.com/dotnet/runtime 38017c3935de95d0335bac04f4901ddfc2718656 - + https://github.com/dotnet/runtime - 76954b4ed7b5cd48ace16fefb1209fe56779b247 + 102d1e856c7e0e553abeec937783da5debed73ad - + https://github.com/dotnet/runtime - 76954b4ed7b5cd48ace16fefb1209fe56779b247 + 102d1e856c7e0e553abeec937783da5debed73ad - + https://github.com/dotnet/runtime - 76954b4ed7b5cd48ace16fefb1209fe56779b247 + 102d1e856c7e0e553abeec937783da5debed73ad - + https://github.com/dotnet/runtime - 76954b4ed7b5cd48ace16fefb1209fe56779b247 + 102d1e856c7e0e553abeec937783da5debed73ad - + https://github.com/dotnet/runtime - 76954b4ed7b5cd48ace16fefb1209fe56779b247 + 102d1e856c7e0e553abeec937783da5debed73ad - + https://github.com/dotnet/runtime - 76954b4ed7b5cd48ace16fefb1209fe56779b247 + 102d1e856c7e0e553abeec937783da5debed73ad - + https://github.com/dotnet/runtime - 76954b4ed7b5cd48ace16fefb1209fe56779b247 + 102d1e856c7e0e553abeec937783da5debed73ad https://github.com/mono/linker 318105ce4619c651d26caeed4cb32c63eefdf3ee - + https://github.com/dotnet/xharness - 4b4b63fe858a81329d3e6c3f781ce30425ce5dd7 + ae0f235b8c5110f94a21826de6faf9499a4e0c9b - + https://github.com/dotnet/xharness - 4b4b63fe858a81329d3e6c3f781ce30425ce5dd7 + ae0f235b8c5110f94a21826de6faf9499a4e0c9b https://github.com/dotnet/arcade diff --git a/eng/Versions.props b/eng/Versions.props index 320a69968a16..072cc67acfcd 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -66,11 +66,11 @@ 5.9.0-preview.2 6.0.0-alpha.1.20612.4 - 6.0.0-preview.3.21169.6 - 6.0.0-preview.3.21169.6 + 6.0.0-preview.4.21178.6 + 6.0.0-preview.4.21178.6 3.1.0 - 6.0.0-preview.3.21169.6 + 6.0.0-preview.4.21178.6 1.2.0-beta.304 4.5.1 @@ -98,14 +98,14 @@ 4.7.0 4.7.0 4.7.0 - 6.0.0-preview.3.21169.6 - 6.0.0-preview.3.21169.6 + 6.0.0-preview.4.21178.6 + 6.0.0-preview.4.21178.6 4.3.0 4.5.4 4.5.0 1.1.1 4.3.0 - 6.0.0-preview.3.21169.6 + 6.0.0-preview.4.21178.6 6.0.0-beta.21174.2 6.0.0-beta.21174.2 @@ -148,8 +148,8 @@ 1.0.1-prerelease-00006 16.9.0-preview-20201201-01 - 1.0.0-prerelease.21176.1 - 1.0.0-prerelease.21176.1 + 1.0.0-prerelease.21179.2 + 1.0.0-prerelease.21179.2 2.4.1 2.4.2 1.3.0 diff --git a/global.json b/global.json index b3a6c7ce6100..a6e3436c1f79 100644 --- a/global.json +++ b/global.json @@ -18,6 +18,6 @@ "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21176.2", "Microsoft.Build.NoTargets": "2.0.17", "Microsoft.Build.Traversal": "2.1.1", - "Microsoft.NET.Sdk.IL": "6.0.0-preview.3.21169.6" + "Microsoft.NET.Sdk.IL": "6.0.0-preview.4.21178.6" } } From ddb88a44049d2b8b3bed39431729fcd67d4ca789 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Tue, 30 Mar 2021 14:23:03 +0300 Subject: [PATCH 41/98] [interp] Add a few super instructions (#50361) * [interp] Fix PROFILE_INTERP build * [interp] Add ldc.i8.0 opcode, replacing ldnull on 64bit We still need to use this opcode in more places, instead of the generic ldc.i8 * [interp] Add a few super instructions We depend on the cprop pass to init local_ref_count. We only add a super instruction if the definition of the an intermediary result is done in the same basic block (the dreg is local var) and it is not used anywhere else, otherwise we can't clear the instruction. * [interp] Improve the marvin block intrinsic By removing also 2 additional ldloca instructions, and enabling cprop for these vars that no longer have their address taken. --- src/mono/mono/mini/interp/interp.c | 50 +++++- src/mono/mono/mini/interp/mintops.def | 22 ++- src/mono/mono/mini/interp/mintops.h | 2 + src/mono/mono/mini/interp/transform.c | 243 +++++++++++++++++++++++--- src/mono/mono/mini/interp/transform.h | 7 +- 5 files changed, 293 insertions(+), 31 deletions(-) diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 9c0dd1d4ecbd..1fa5e2660d54 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -3205,10 +3205,6 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs ++ip; mono_break (); MINT_IN_BREAK; - MINT_IN_CASE(MINT_LDNULL) - LOCAL_VAR (ip [1], gpointer) = NULL; - ip += 2; - MINT_IN_BREAK; MINT_IN_CASE(MINT_INIT_ARGLIST) { const guint16 *call_ip = frame->parent->state.ip - 6; g_assert_checked (*call_ip == MINT_CALL_VARARG); @@ -3266,6 +3262,10 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs LOCAL_VAR (ip [1], gint32) = READ32 (ip + 2); ip += 4; MINT_IN_BREAK; + MINT_IN_CASE(MINT_LDC_I8_0) + LOCAL_VAR (ip [1], gint64) = 0; + ip += 2; + MINT_IN_BREAK; MINT_IN_CASE(MINT_LDC_I8) LOCAL_VAR (ip [1], gint64) = READ64 (ip + 2); ip += 6; @@ -3624,6 +3624,12 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs MINT_IN_CASE(MINT_RET) frame->retval [0] = LOCAL_VAR (ip [1], stackval); goto exit_frame; + MINT_IN_CASE(MINT_RET_I4_IMM) + frame->retval [0].data.i = (gint16)ip [1]; + goto exit_frame; + MINT_IN_CASE(MINT_RET_I8_IMM) + frame->retval [0].data.l = (gint16)ip [1]; + goto exit_frame; MINT_IN_CASE(MINT_RET_VOID) goto exit_frame; MINT_IN_CASE(MINT_RET_VT) { @@ -4324,10 +4330,18 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs LOCAL_VAR (ip [1], gint32) = LOCAL_VAR (ip [2], gint32) + 1; ip += 3; MINT_IN_BREAK; + MINT_IN_CASE(MINT_ADD_I4_IMM) + LOCAL_VAR (ip [1], gint32) = LOCAL_VAR (ip [2], gint32) + (gint16)ip [3]; + ip += 4; + MINT_IN_BREAK; MINT_IN_CASE(MINT_ADD1_I8) LOCAL_VAR (ip [1], gint64) = LOCAL_VAR (ip [2], gint64) + 1; ip += 3; MINT_IN_BREAK; + MINT_IN_CASE(MINT_ADD_I8_IMM) + LOCAL_VAR (ip [1], gint64) = LOCAL_VAR (ip [2], gint64) + (gint16)ip [3]; + ip += 4; + MINT_IN_BREAK; MINT_IN_CASE(MINT_SUB_I4) BINOP(gint32, -); MINT_IN_BREAK; @@ -4491,6 +4505,30 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs MINT_IN_CASE(MINT_SHR_UN_I8) SHIFTOP(guint64, >>); MINT_IN_BREAK; + MINT_IN_CASE(MINT_SHL_I4_IMM) + LOCAL_VAR (ip [1], gint32) = LOCAL_VAR (ip [2], gint32) << ip [3]; + ip += 4; + MINT_IN_BREAK; + MINT_IN_CASE(MINT_SHL_I8_IMM) + LOCAL_VAR (ip [1], gint64) = LOCAL_VAR (ip [2], gint64) << ip [3]; + ip += 4; + MINT_IN_BREAK; + MINT_IN_CASE(MINT_SHR_I4_IMM) + LOCAL_VAR (ip [1], gint32) = LOCAL_VAR (ip [2], gint32) >> ip [3]; + ip += 4; + MINT_IN_BREAK; + MINT_IN_CASE(MINT_SHR_I8_IMM) + LOCAL_VAR (ip [1], gint64) = LOCAL_VAR (ip [2], gint64) >> ip [3]; + ip += 4; + MINT_IN_BREAK; + MINT_IN_CASE(MINT_SHR_UN_I4_IMM) + LOCAL_VAR (ip [1], guint32) = LOCAL_VAR (ip [2], guint32) >> ip [3]; + ip += 4; + MINT_IN_BREAK; + MINT_IN_CASE(MINT_SHR_UN_I8_IMM) + LOCAL_VAR (ip [1], guint64) = LOCAL_VAR (ip [2], guint64) >> ip [3]; + ip += 4; + MINT_IN_BREAK; MINT_IN_CASE(MINT_NEG_I4) LOCAL_VAR (ip [1], gint32) = - LOCAL_VAR (ip [2], gint32); ip += 3; @@ -4858,7 +4896,7 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs MINT_IN_BREAK; } MINT_IN_CASE(MINT_INTRINS_MARVIN_BLOCK) { - interp_intrins_marvin_block (LOCAL_VAR (ip [1], guint32*), LOCAL_VAR (ip [2], guint32*)); + interp_intrins_marvin_block ((guint32*)(locals + ip [1]), (guint32*)(locals + ip [2])); ip += 3; MINT_IN_BREAK; } @@ -7184,7 +7222,7 @@ imethod_opcount_comparer (gconstpointer m1, gconstpointer m2) static void interp_print_method_counts (void) { - MonoJitMemoryManager *jit_mm = jit_mm_for_method (method); + MonoJitMemoryManager *jit_mm = get_default_jit_mm (); jit_mm_lock (jit_mm); imethods = (InterpMethod**) malloc (jit_mm->interp_code_hash.num_entries * sizeof (InterpMethod*)); diff --git a/src/mono/mono/mini/interp/mintops.def b/src/mono/mono/mini/interp/mintops.def index 03f5184c294b..b14bd646520d 100644 --- a/src/mono/mono/mini/interp/mintops.def +++ b/src/mono/mono/mini/interp/mintops.def @@ -14,7 +14,6 @@ OPDEF(MINT_DEF, "def", 2, 1, 0, MintOpNoArgs) OPDEF(MINT_DUMMY_USE, "dummy_use", 2, 0, 1, MintOpNoArgs) OPDEF(MINT_BREAK, "break", 1, 0, 0, MintOpNoArgs) OPDEF(MINT_BREAKPOINT, "breakpoint", 1, 0, 0, MintOpNoArgs) -OPDEF(MINT_LDNULL, "ldnull", 2, 1, 0, MintOpNoArgs) OPDEF(MINT_RET, "ret", 2, 0, 1, MintOpNoArgs) OPDEF(MINT_RET_VOID, "ret.void", 1, 0, 0, MintOpNoArgs) @@ -33,11 +32,12 @@ OPDEF(MINT_LDC_I4_5, "ldc.i4.5", 2, 1, 0, MintOpNoArgs) OPDEF(MINT_LDC_I4_6, "ldc.i4.6", 2, 1, 0, MintOpNoArgs) OPDEF(MINT_LDC_I4_7, "ldc.i4.7", 2, 1, 0, MintOpNoArgs) OPDEF(MINT_LDC_I4_8, "ldc.i4.8", 2, 1, 0, MintOpNoArgs) - OPDEF(MINT_LDC_I4_S, "ldc.i4.s", 3, 1, 0, MintOpShortInt) OPDEF(MINT_LDC_I4, "ldc.i4", 4, 1, 0, MintOpInt) -OPDEF(MINT_LDC_I8, "ldc.i8", 6, 1, 0, MintOpLongInt) + +OPDEF(MINT_LDC_I8_0, "ldc.i8.0", 2, 1, 0, MintOpNoArgs) OPDEF(MINT_LDC_I8_S, "ldc.i8.s", 3, 1, 0, MintOpShortInt) +OPDEF(MINT_LDC_I8, "ldc.i8", 6, 1, 0, MintOpLongInt) OPDEF(MINT_LDC_R4, "ldc.r4", 4, 1, 0, MintOpFloat) OPDEF(MINT_LDC_R8, "ldc.r8", 6, 1, 0, MintOpDouble) @@ -422,6 +422,7 @@ OPDEF(MINT_REM_R8, "rem.r8", 4, 1, 2, MintOpNoArgs) OPDEF(MINT_REM_UN_I4, "rem.un.i4", 4, 1, 2, MintOpNoArgs) OPDEF(MINT_REM_UN_I8, "rem.un.i8", 4, 1, 2, MintOpNoArgs) +// Shifts, keep in order with imm versions OPDEF(MINT_SHR_UN_I4, "shr.un.i4", 4, 1, 2, MintOpNoArgs) OPDEF(MINT_SHR_UN_I8, "shr.un.i8", 4, 1, 2, MintOpNoArgs) OPDEF(MINT_SHL_I4, "shl.i4", 4, 1, 2, MintOpNoArgs) @@ -595,6 +596,21 @@ OPDEF(MINT_CONV_OVF_U8_R8, "conv.ovf.u8.r8", 3, 1, 1, MintOpNoArgs) OPDEF(MINT_CEQ0_I4, "ceq0.i4", 3, 1, 1, MintOpNoArgs) /* unops end */ +/* super instructions */ +OPDEF(MINT_RET_I4_IMM, "ret.i4.imm", 2, 0, 0, MintOpShortInt) +OPDEF(MINT_RET_I8_IMM, "ret.i8.imm", 2, 0, 0, MintOpShortInt) + +OPDEF(MINT_ADD_I4_IMM, "add.i4.imm", 4, 1, 1, MintOpShortInt) +OPDEF(MINT_ADD_I8_IMM, "add.i8.imm", 4, 1, 1, MintOpShortInt) + +OPDEF(MINT_SHR_UN_I4_IMM, "shr.un.i4.imm", 4, 1, 1, MintOpShortInt) +OPDEF(MINT_SHR_UN_I8_IMM, "shr.un.i8.imm", 4, 1, 1, MintOpShortInt) +OPDEF(MINT_SHL_I4_IMM, "shl.i4.imm", 4, 1, 1, MintOpShortInt) +OPDEF(MINT_SHL_I8_IMM, "shl.i8.imm", 4, 1, 1, MintOpShortInt) +OPDEF(MINT_SHR_I4_IMM, "shr.i4.imm", 4, 1, 1, MintOpShortInt) +OPDEF(MINT_SHR_I8_IMM, "shr.i8.imm", 4, 1, 1, MintOpShortInt) + + OPDEF(MINT_CKFINITE, "ckfinite", 3, 1, 1, MintOpNoArgs) OPDEF(MINT_MKREFANY, "mkrefany", 4, 1, 1, MintOpClassToken) OPDEF(MINT_REFANYTYPE, "refanytype", 3, 1, 1, MintOpNoArgs) diff --git a/src/mono/mono/mini/interp/mintops.h b/src/mono/mono/mini/interp/mintops.h index f2ae6dfc096f..15254ba66c0e 100644 --- a/src/mono/mono/mini/interp/mintops.h +++ b/src/mono/mono/mini/interp/mintops.h @@ -61,8 +61,10 @@ typedef enum { #define MINT_IS_CALL(op) ((op) >= MINT_CALL && (op) <= MINT_JIT_CALL) #define MINT_IS_PATCHABLE_CALL(op) ((op) >= MINT_CALL && (op) <= MINT_VCALL) #define MINT_IS_LDC_I4(op) ((op) >= MINT_LDC_I4_M1 && (op) <= MINT_LDC_I4) +#define MINT_IS_LDC_I8(op) ((op) >= MINT_LDC_I8_0 && (op) <= MINT_LDC_I8) #define MINT_IS_UNOP(op) ((op) >= MINT_ADD1_I4 && (op) <= MINT_CEQ0_I4) #define MINT_IS_BINOP(op) ((op) >= MINT_ADD_I4 && (op) <= MINT_CLT_UN_R8) +#define MINT_IS_BINOP_SHIFT(op) ((op) >= MINT_SHR_UN_I4 && (op) <= MINT_SHR_I8) #define MINT_IS_LDFLD(op) ((op) >= MINT_LDFLD_I1 && (op) <= MINT_LDFLD_O) #define MINT_IS_STFLD(op) ((op) >= MINT_STFLD_I1 && (op) <= MINT_STFLD_O) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 31dc24e235c6..0501d06cba07 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -136,6 +136,12 @@ MonoInterpStats mono_interp_stats; #define MINT_MOV_P MINT_MOV_4 #endif +#if SIZEOF_VOID_P == 8 +#define MINT_LDNULL MINT_LDC_I8_0 +#else +#define MINT_LDNULL MINT_LDC_I4_0 +#endif + typedef struct { const gchar *op_name; guint16 insn [3]; @@ -425,6 +431,7 @@ create_interp_local_explicit (TransformData *td, MonoType *type, int size) td->locals [td->locals_size].size = size; td->locals [td->locals_size].live_start = -1; td->locals [td->locals_size].bb_index = -1; + td->locals [td->locals_size].def = NULL; td->locals_size++; return td->locals_size - 1; @@ -1580,6 +1587,18 @@ interp_get_const_from_ldc_i4 (InterpInst *ins) } } +static gint64 +interp_get_const_from_ldc_i8 (InterpInst *ins) +{ + switch (ins->opcode) { + case MINT_LDC_I8_0: return 0; + case MINT_LDC_I8_S: return (gint64)(gint16)ins->data [0]; + case MINT_LDC_I8: return READ64 (&ins->data [0]); + default: + g_assert_not_reached (); + } +} + /* If ins is not null, it will replace it with the ldc */ static InterpInst* interp_get_ldc_i4_from_const (TransformData *td, InterpInst *ins, gint32 ct, int dreg) @@ -2071,8 +2090,37 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas g_assert (!strcmp (tm, "get_Value")); *op = MINT_LDIND_I; } else if (in_corlib && !strcmp (klass_name_space, "System") && !strcmp (klass_name, "Marvin")) { - if (!strcmp (tm, "Block")) - *op = MINT_INTRINS_MARVIN_BLOCK; + if (!strcmp (tm, "Block")) { + InterpInst *ldloca2 = td->last_ins; + if (ldloca2 != NULL && ldloca2->opcode == MINT_LDLOCA_S) { + InterpInst *ldloca1 = interp_prev_ins (ldloca2); + if (ldloca1 != NULL && ldloca1->opcode == MINT_LDLOCA_S) { + interp_add_ins (td, MINT_INTRINS_MARVIN_BLOCK); + td->last_ins->sregs [0] = ldloca1->sregs [0]; + td->last_ins->sregs [1] = ldloca2->sregs [0]; + + // This intrinsic would normally receive two local refs, however, we try optimizing + // away both ldlocas for better codegen. This means that this intrinsic will instead + // modify the values of both sregs. In order to not overcomplicate the optimization + // passes and offset allocator with support for modifiable sregs or multi dregs, we + // just redefine both sregs after the intrinsic. + interp_add_ins (td, MINT_DEF); + td->last_ins->dreg = ldloca1->sregs [0]; + interp_add_ins (td, MINT_DEF); + td->last_ins->dreg = ldloca2->sregs [0]; + + // Remove the ldlocas + td->locals [ldloca1->sregs [0]].indirects--; + td->locals [ldloca2->sregs [0]].indirects--; + mono_interp_stats.ldlocas_removed += 2; + interp_clear_ins (ldloca1); + interp_clear_ins (ldloca2); + td->sp -= 2; + td->ip += 5; + return TRUE; + } + } + } } else if (in_corlib && !strcmp (klass_name_space, "System.Runtime.InteropServices") && !strcmp (klass_name, "MemoryMarshal")) { if (!strcmp (tm, "GetArrayDataReference")) *op = MINT_INTRINS_MEMORYMARSHAL_GETARRAYDATAREF; @@ -3796,6 +3844,7 @@ interp_method_compute_offsets (TransformData *td, InterpMethod *imethod, MonoMet td->locals [i].flags = INTERP_LOCAL_FLAG_GLOBAL; td->locals [i].indirects = 0; td->locals [i].mt = mt; + td->locals [i].def = NULL; if (mt == MINT_TYPE_VT) { size = mono_type_size (type, &align); td->locals [i].size = size; @@ -3824,6 +3873,7 @@ interp_method_compute_offsets (TransformData *td, InterpMethod *imethod, MonoMet td->locals [index].flags = INTERP_LOCAL_FLAG_GLOBAL; td->locals [index].indirects = 0; td->locals [index].mt = mint_type (header->locals [i]); + td->locals [index].def = NULL; if (td->locals [index].mt == MINT_TYPE_VT) td->locals [index].size = size; else @@ -4592,7 +4642,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->ip += 2; break; } - case CEE_LDNULL: + case CEE_LDNULL: interp_add_ins (td, MINT_LDNULL); push_type (td, STACK_TYPE_O, NULL); interp_ins_set_dreg (td->last_ins, td->sp [-1].local); @@ -7672,8 +7722,9 @@ interp_optimize_bblocks (TransformData *td) } static gboolean -interp_local_deadce (TransformData *td, int *local_ref_count) +interp_local_deadce (TransformData *td) { + int *local_ref_count = td->local_ref_count; gboolean needs_dce = FALSE; gboolean needs_cprop = FALSE; @@ -7697,7 +7748,7 @@ interp_local_deadce (TransformData *td, int *local_ref_count) for (InterpInst *ins = bb->first_ins; ins != NULL; ins = ins->next) { if (MINT_IS_MOV (ins->opcode) || MINT_IS_LDC_I4 (ins->opcode) || - ins->opcode == MINT_LDC_I8 || + MINT_IS_LDC_I8 (ins->opcode) || ins->opcode == MINT_MONO_LDPTR || ins->opcode == MINT_LDLOCA_S) { int dreg = ins->dreg; @@ -7749,8 +7800,9 @@ interp_local_deadce (TransformData *td, int *local_ref_count) break; static InterpInst* -interp_fold_unop (TransformData *td, LocalValue *local_defs, int *local_ref_count, InterpInst *ins) +interp_fold_unop (TransformData *td, LocalValue *local_defs, InterpInst *ins) { + int *local_ref_count = td->local_ref_count; // ins should be an unop, therefore it should have a single dreg and a single sreg int dreg = ins->dreg; int sreg = ins->sregs [0]; @@ -7861,8 +7913,9 @@ interp_fold_unop (TransformData *td, LocalValue *local_defs, int *local_ref_coun break; static InterpInst* -interp_fold_unop_cond_br (TransformData *td, InterpBasicBlock *cbb, LocalValue *local_defs, int *local_ref_count, InterpInst *ins) +interp_fold_unop_cond_br (TransformData *td, InterpBasicBlock *cbb, LocalValue *local_defs, InterpInst *ins) { + int *local_ref_count = td->local_ref_count; // ins should be an unop conditional branch, therefore it should have a single sreg int sreg = ins->sregs [0]; LocalValue *val = &local_defs [sreg]; @@ -7922,8 +7975,9 @@ interp_fold_unop_cond_br (TransformData *td, InterpBasicBlock *cbb, LocalValue * static InterpInst* -interp_fold_binop (TransformData *td, LocalValue *local_defs, int *local_ref_count, InterpInst *ins) +interp_fold_binop (TransformData *td, LocalValue *local_defs, InterpInst *ins) { + int *local_ref_count = td->local_ref_count; // ins should be a binop, therefore it should have a single dreg and two sregs int dreg = ins->dreg; int sreg1 = ins->sregs [0]; @@ -8043,8 +8097,9 @@ interp_fold_binop (TransformData *td, LocalValue *local_defs, int *local_ref_cou break; static InterpInst* -interp_fold_binop_cond_br (TransformData *td, InterpBasicBlock *cbb, LocalValue *local_defs, int *local_ref_count, InterpInst *ins) +interp_fold_binop_cond_br (TransformData *td, InterpBasicBlock *cbb, LocalValue *local_defs, InterpInst *ins) { + int *local_ref_count = td->local_ref_count; // ins should be a conditional binop, therefore it should have only two sregs int sreg1 = ins->sregs [0]; int sreg2 = ins->sregs [1]; @@ -8094,8 +8149,9 @@ interp_fold_binop_cond_br (TransformData *td, InterpBasicBlock *cbb, LocalValue } static void -cprop_sreg (TransformData *td, InterpInst *ins, int *psreg, int *local_ref_count, LocalValue *local_defs) +cprop_sreg (TransformData *td, InterpInst *ins, int *psreg, LocalValue *local_defs) { + int *local_ref_count = td->local_ref_count; int sreg = *psreg; local_ref_count [sreg]++; @@ -8126,6 +8182,7 @@ interp_cprop (TransformData *td) gboolean needs_retry; int ins_index; + td->local_ref_count = local_ref_count; retry: memset (local_ref_count, 0, td->locals_size * sizeof (int)); @@ -8167,12 +8224,12 @@ interp_cprop (TransformData *td) int *call_args = ins->info.call_args; if (call_args) { while (*call_args != -1) { - cprop_sreg (td, ins, call_args, local_ref_count, local_defs); + cprop_sreg (td, ins, call_args, local_defs); call_args++; } } } else { - cprop_sreg (td, ins, &sregs [i], local_ref_count, local_defs); + cprop_sreg (td, ins, &sregs [i], local_defs); // This var is used as a source to a normal instruction. In case this var will // also be used as source to a call, make sure the offset allocator will create // a new temporary call arg var and not use this one. Call arg vars have special @@ -8261,9 +8318,9 @@ interp_cprop (TransformData *td) } else if (MINT_IS_LDC_I4 (opcode)) { local_defs [dreg].type = LOCAL_VALUE_I4; local_defs [dreg].i = interp_get_const_from_ldc_i4 (ins); - } else if (opcode == MINT_LDC_I8) { + } else if (MINT_IS_LDC_I8 (opcode)) { local_defs [dreg].type = LOCAL_VALUE_I8; - local_defs [dreg].l = READ64 (&ins->data [0]); + local_defs [dreg].l = interp_get_const_from_ldc_i8 (ins); } else if (ins->opcode == MINT_MONO_LDPTR) { #if SIZEOF_VOID_P == 8 local_defs [dreg].type = LOCAL_VALUE_I8; @@ -8273,13 +8330,13 @@ interp_cprop (TransformData *td) local_defs [dreg].i = (gint32)td->data_items [ins->data [0]]; #endif } else if (MINT_IS_UNOP (opcode) || (opcode >= MINT_MOV_I1 && opcode <= MINT_MOV_U2)) { - ins = interp_fold_unop (td, local_defs, local_ref_count, ins); + ins = interp_fold_unop (td, local_defs, ins); } else if (MINT_IS_UNOP_CONDITIONAL_BRANCH (opcode)) { - ins = interp_fold_unop_cond_br (td, bb, local_defs, local_ref_count, ins); + ins = interp_fold_unop_cond_br (td, bb, local_defs, ins); } else if (MINT_IS_BINOP (opcode)) { - ins = interp_fold_binop (td, local_defs, local_ref_count, ins); + ins = interp_fold_binop (td, local_defs, ins); } else if (MINT_IS_BINOP_CONDITIONAL_BRANCH (opcode)) { - ins = interp_fold_binop_cond_br (td, bb, local_defs, local_ref_count, ins); + ins = interp_fold_binop_cond_br (td, bb, local_defs, ins); } else if (MINT_IS_LDFLD (opcode) && ins->data [0] == 0) { InterpInst *ldloca = local_defs [sregs [0]].ins; if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S && @@ -8322,7 +8379,7 @@ interp_cprop (TransformData *td) } } - needs_retry = interp_local_deadce (td, local_ref_count); + needs_retry = interp_local_deadce (td); if (mono_interp_opt & INTERP_OPT_BBLOCKS) needs_retry |= interp_optimize_bblocks (td); @@ -8330,7 +8387,6 @@ interp_cprop (TransformData *td) goto retry; g_free (local_defs); - g_free (local_ref_count); } void @@ -8339,12 +8395,150 @@ mono_test_interp_cprop (TransformData *td) interp_cprop (td); } +static gboolean +get_sreg_imm (TransformData *td, int sreg, gint16 *imm) +{ + InterpInst *def = td->locals [sreg].def; + if (def != NULL && td->local_ref_count [sreg] == 1) { + gint64 ct; + if (MINT_IS_LDC_I4 (def->opcode)) + ct = interp_get_const_from_ldc_i4 (def); + else if (MINT_IS_LDC_I8 (def->opcode)) + ct = interp_get_const_from_ldc_i8 (def); + else + return FALSE; + if (ct >= G_MININT16 && ct <= G_MAXINT16) { + *imm = (gint16)ct; + mono_interp_stats.super_instructions++; + return TRUE; + } + } + return FALSE; +} + static void interp_super_instructions (TransformData *td) { + InterpBasicBlock *bb; + int *local_ref_count = td->local_ref_count; // Add some actual super instructions + for (bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { + InterpInst *ins; + + // Set cbb since we do some instruction inserting below + td->cbb = bb; + + for (ins = bb->first_ins; ins != NULL; ins = ins->next) { + int opcode = ins->opcode; + if (opcode == MINT_NOP) + continue; + if (mono_interp_op_dregs [opcode] && !(td->locals [ins->dreg].flags & INTERP_LOCAL_FLAG_GLOBAL)) + td->locals [ins->dreg].def = ins; + + if (opcode == MINT_RET) { + // ldc + ret -> ret.imm + int sreg = ins->sregs [0]; + gint16 imm; + if (get_sreg_imm (td, sreg, &imm)) { + InterpInst *def = td->locals [sreg].def; + int ret_op = MINT_IS_LDC_I4 (def->opcode) ? MINT_RET_I4_IMM : MINT_RET_I8_IMM; + InterpInst *new_inst = interp_insert_ins (td, ins, ret_op); + new_inst->data [0] = imm; + interp_clear_ins (def); + interp_clear_ins (ins); + local_ref_count [sreg]--; + + if (td->verbose_level) { + g_print ("superins: "); + dump_interp_inst (new_inst); + } + } + } else if (opcode == MINT_ADD_I4 || opcode == MINT_ADD_I8) { + int sreg = -1; + int sreg_imm = -1; + gint16 imm; + if (get_sreg_imm (td, ins->sregs [0], &imm)) { + sreg = ins->sregs [1]; + sreg_imm = ins->sregs [0]; + } else if (get_sreg_imm (td, ins->sregs [1], &imm)) { + sreg = ins->sregs [0]; + sreg_imm = ins->sregs [1]; + } + if (sreg != -1) { + int add_op = opcode == MINT_ADD_I4 ? MINT_ADD_I4_IMM : MINT_ADD_I8_IMM; + InterpInst *new_inst = interp_insert_ins (td, ins, add_op); + new_inst->dreg = ins->dreg; + new_inst->sregs [0] = sreg; + new_inst->data [0] = imm; + interp_clear_ins (td->locals [sreg_imm].def); + interp_clear_ins (ins); + local_ref_count [sreg_imm]--; + if (td->verbose_level) { + g_print ("superins: "); + dump_interp_inst (new_inst); + } + } + } else if (opcode == MINT_SUB_I4 || opcode == MINT_SUB_I8) { + // ldc + sub -> add.-imm + gint16 imm; + int sreg_imm = ins->sregs [1]; + if (get_sreg_imm (td, sreg_imm, &imm) && imm != G_MININT16) { + int add_op = opcode == MINT_SUB_I4 ? MINT_ADD_I4_IMM : MINT_ADD_I8_IMM; + InterpInst *new_inst = interp_insert_ins (td, ins, add_op); + new_inst->dreg = ins->dreg; + new_inst->sregs [0] = ins->sregs [0]; + new_inst->data [0] = -imm; + interp_clear_ins (td->locals [sreg_imm].def); + interp_clear_ins (ins); + local_ref_count [sreg_imm]--; + if (td->verbose_level) { + g_print ("superins: "); + dump_interp_inst (new_inst); + } + } + } else if (MINT_IS_BINOP_SHIFT (opcode)) { + // ldc + sh -> sh.imm + gint16 imm; + int sreg_imm = ins->sregs [1]; + if (get_sreg_imm (td, sreg_imm, &imm)) { + int shift_op = MINT_SHR_UN_I4_IMM + (opcode - MINT_SHR_UN_I4); + InterpInst *new_inst = interp_insert_ins (td, ins, shift_op); + new_inst->dreg = ins->dreg; + new_inst->sregs [0] = ins->sregs [0]; + new_inst->data [0] = imm; + interp_clear_ins (td->locals [sreg_imm].def); + interp_clear_ins (ins); + local_ref_count [sreg_imm]--; + if (td->verbose_level) { + g_print ("superins: "); + dump_interp_inst (new_inst); + } + } + } else if (MINT_IS_LDFLD (opcode)) { + // cknull + ldfld -> ldfld + // FIXME This optimization is very limited, it is meant mainly to remove cknull + // when inlining property accessors. We should have more advanced cknull removal + // optimzations, so we can catch cases where instructions are not next to each other. + int obj_sreg = ins->sregs [0]; + InterpInst *def = td->locals [obj_sreg].def; + if (def != NULL && def->opcode == MINT_CKNULL && interp_prev_ins (ins) == def && + def->dreg == obj_sreg && local_ref_count [obj_sreg] == 1) { + if (td->verbose_level) { + g_print ("remove redundant cknull (%s): ", td->method->name); + dump_interp_inst (def); + } + ins->sregs [0] = def->sregs [0]; + interp_clear_ins (def); + local_ref_count [obj_sreg]--; + mono_interp_stats.super_instructions++; + } + } + } + } } +static void initialize_global_vars (TransformData *td); + static void interp_optimize_code (TransformData *td) { @@ -8354,7 +8548,13 @@ interp_optimize_code (TransformData *td) if (mono_interp_opt & INTERP_OPT_CPROP) MONO_TIME_TRACK (mono_interp_stats.cprop_time, interp_cprop (td)); - if (mono_interp_opt & INTERP_OPT_SUPER_INSTRUCTIONS) + // After this point control optimizations on control flow can no longer happen, so we can determine + // which vars are global. This helps speed up the super instructions pass, which only operates on + // single def, single use local vars. + initialize_global_vars (td); + + if ((mono_interp_opt & INTERP_OPT_SUPER_INSTRUCTIONS) && + (mono_interp_opt & INTERP_OPT_CPROP)) MONO_TIME_TRACK (mono_interp_stats.super_instructions_time, interp_super_instructions (td)); } @@ -8986,6 +9186,7 @@ generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoG g_free (td->data_items); g_free (td->stack); g_free (td->locals); + g_free (td->local_ref_count); g_hash_table_destroy (td->data_hash); #ifdef ENABLE_EXPERIMENT_TIERED g_hash_table_destroy (td->patchsite_hash); diff --git a/src/mono/mono/mini/interp/transform.h b/src/mono/mono/mini/interp/transform.h index 0609d202a82b..4d9aa9233283 100644 --- a/src/mono/mono/mini/interp/transform.h +++ b/src/mono/mono/mini/interp/transform.h @@ -145,8 +145,12 @@ typedef struct { // index of first basic block where this var is used int bb_index; union { - // If var is INTERP_LOCAL_FLAG_CALL_ARGS, this is the call instruction using it + // If var is INTERP_LOCAL_FLAG_CALL_ARGS, this is the call instruction using it. + // Only used during var offset allocator InterpInst *call; + // For local vars, this represents the instruction declaring it. + // Only used during super instruction pass. + InterpInst *def; }; } InterpLocal; @@ -173,6 +177,7 @@ typedef struct gint32 param_area_offset; gint32 total_locals_size; InterpLocal *locals; + int *local_ref_count; unsigned int il_locals_offset; unsigned int il_locals_size; unsigned int locals_size; From 6d707d78c87c6c619d48537dff0568696a5d65bd Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Tue, 30 Mar 2021 07:10:16 -0700 Subject: [PATCH 42/98] Revert "Fix release builds with latest VS dogfood (#49948)" (#50398) This reverts commit 3961d91812c997af18ff1ebbdac53bcc4d84fb02. This workaround is no longer needed. --- src/coreclr/EmptyProps.props | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/coreclr/EmptyProps.props b/src/coreclr/EmptyProps.props index f26de36e4f97..c062d8bc5662 100644 --- a/src/coreclr/EmptyProps.props +++ b/src/coreclr/EmptyProps.props @@ -5,13 +5,4 @@ in the root of the source tree. In particular this was necessary to compile DacTableGen, which is currently compiled against .NET Framework. --> - - - - - - - - - From 45fbba6a539fe2054d379faf697dab28d9be2828 Mon Sep 17 00:00:00 2001 From: Ryan Lucia Date: Tue, 30 Mar 2021 10:31:36 -0400 Subject: [PATCH 43/98] Fix waits on iOS (#50388) I don't understand why CLOCK_MONOTONIC doesn't work even in newer simulators here, but this will at least fix the problem for now. --- .../Native/Unix/System.Native/pal_threading.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/libraries/Native/Unix/System.Native/pal_threading.c b/src/libraries/Native/Unix/System.Native/pal_threading.c index 937edd1ce16e..cf739c845427 100644 --- a/src/libraries/Native/Unix/System.Native/pal_threading.c +++ b/src/libraries/Native/Unix/System.Native/pal_threading.c @@ -11,6 +11,7 @@ #include #include #include +#include #if defined(TARGET_OSX) // So we can use the declaration of pthread_cond_timedwait_relative_np @@ -169,13 +170,25 @@ int32_t SystemNative_LowLevelMonitor_TimedWait(LowLevelMonitor *monitor, int32_t #if HAVE_CLOCK_GETTIME_NSEC_NP timeoutTimeSpec.tv_sec = timeoutMilliseconds / 1000; timeoutTimeSpec.tv_nsec = (timeoutMilliseconds % 1000) * 1000 * 1000; + error = pthread_cond_timedwait_relative_np(&monitor->Condition, &monitor->Mutex, &timeoutTimeSpec); #else +#if HAVE_CLOCK_MONOTONIC error = clock_gettime(CLOCK_MONOTONIC, &timeoutTimeSpec); assert(error == 0); +#else + struct timeval tv; + + error = gettimeofday(&tv, NULL); + assert(error == 0); + + timeoutTimeSpec.tv_sec = tv.tv_sec; + timeoutTimeSpec.tv_nsec = tv.tv_usec * 1000; +#endif uint64_t nanoseconds = (uint64_t)timeoutMilliseconds * 1000 * 1000 + (uint64_t)timeoutTimeSpec.tv_nsec; timeoutTimeSpec.tv_sec += nanoseconds / (1000 * 1000 * 1000); timeoutTimeSpec.tv_nsec = nanoseconds % (1000 * 1000 * 1000); + error = pthread_cond_timedwait(&monitor->Condition, &monitor->Mutex, &timeoutTimeSpec); #endif assert(error == 0 || error == ETIMEDOUT); From 4f62f32fad638ed6b9fc771eae3039d567adc81d Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Tue, 30 Mar 2021 09:04:42 -0700 Subject: [PATCH 44/98] increase timout on ConnectTimeout_TimesOutSSLAuth_Throws test (#50351) --- .../tests/FunctionalTests/HttpClientHandlerTest.Cancellation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Cancellation.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Cancellation.cs index 969415dde425..5589de00d628 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Cancellation.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Cancellation.cs @@ -37,7 +37,7 @@ await Assert.ThrowsAnyAsync(() => new UriBuilder(uri) { Scheme = "https" }.ToString()) { Version = UseVersion }, default)); sw.Stop(); - Assert.InRange(sw.ElapsedMilliseconds, 500, 60_000); + Assert.InRange(sw.ElapsedMilliseconds, 500, 85_000); releaseServer.SetResult(); } }, server => releaseServer.Task); // doesn't establish SSL connection From 217599679c1679db41bfbf28a9969705e4761fa6 Mon Sep 17 00:00:00 2001 From: Omair Majid Date: Tue, 30 Mar 2021 12:08:02 -0400 Subject: [PATCH 45/98] Clean up allocated memory on error in CordbEval::NewParameterizedObject (#50371) pArgData is allocated in this piece of code; if we are failing and returning early, we should deallocate this memory to avoid a memory leak. --- src/coreclr/debug/di/rsthread.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/debug/di/rsthread.cpp b/src/coreclr/debug/di/rsthread.cpp index b6870e560665..04a7fa21a1db 100644 --- a/src/coreclr/debug/di/rsthread.cpp +++ b/src/coreclr/debug/di/rsthread.cpp @@ -9790,6 +9790,7 @@ HRESULT CordbEval::NewParameterizedObject(ICorDebugFunction * pConstructor, if (FAILED(hr)) { + delete [] pArgData; return hr; } } From 0eb48fed41043174e8cd858ff10cbdb0f1718eec Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 30 Mar 2021 11:25:00 -0500 Subject: [PATCH 46/98] Update dependencies from https://github.com/mono/linker build 20210330.1 (#50417) Microsoft.NET.ILLink.Tasks From Version 6.0.100-preview.2.21177.1 -> To Version 6.0.100-preview.2.21180.1 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 6f899814ba01..7daac0a01812 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -194,9 +194,9 @@ https://github.com/dotnet/runtime 102d1e856c7e0e553abeec937783da5debed73ad - + https://github.com/mono/linker - 318105ce4619c651d26caeed4cb32c63eefdf3ee + 26d9440e6683b44c4db5f45b25e9c857a6563bb8 https://github.com/dotnet/xharness diff --git a/eng/Versions.props b/eng/Versions.props index 072cc67acfcd..12105014900a 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -159,7 +159,7 @@ 5.0.0-preview-20201009.2 - 6.0.100-preview.2.21177.1 + 6.0.100-preview.2.21180.1 6.0.0-preview.4.21172.5 From 2652995b6e58d78ea9ae6d75ea4eabcf5756d557 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Tue, 30 Mar 2021 09:42:04 -0700 Subject: [PATCH 47/98] Fix issues with JitDump (#50395) 1. On 64 bit platforms, with "long" bitset types (> 64 bits), when converting to string format for display, we were only displaying the low 32 bits of every 64 bit chunk. 2. Add display of the `IGF_ALIGN` flag. --- src/coreclr/jit/bitsetasshortlong.h | 17 +++++++++-------- src/coreclr/jit/emit.cpp | 4 ++++ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/coreclr/jit/bitsetasshortlong.h b/src/coreclr/jit/bitsetasshortlong.h index 078cdc810e9d..0eda55e1e105 100644 --- a/src/coreclr/jit/bitsetasshortlong.h +++ b/src/coreclr/jit/bitsetasshortlong.h @@ -910,17 +910,18 @@ const char* BitSetOps> 16; - bits = bits >> 16; } } return res; diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 5713e748ba09..cd626cbd335d 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -3460,6 +3460,10 @@ void emitter::emitDispIGflags(unsigned flags) { printf(", extend"); } + if (flags & IGF_LOOP_ALIGN) + { + printf(", align"); + } } void emitter::emitDispIG(insGroup* ig, insGroup* igPrev, bool verbose) From 20864e71f52b39a6a9cbe35cbf63845445000725 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Cant=C3=BA?= Date: Tue, 30 Mar 2021 09:45:10 -0700 Subject: [PATCH 48/98] FileSystemGlobbing: Allow rootDir paths ending with separator to match files correctly (#45139) * Allow rootDir paths ending with separator to match files correctly * Avoid running tests with windows-like absolute paths on non-windows platforms * Address test suggestion --- .../src/InMemoryDirectoryInfo.cs | 10 +- .../tests/FunctionalTests.cs | 154 +++++++++++++++++- 2 files changed, 156 insertions(+), 8 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/InMemoryDirectoryInfo.cs b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/InMemoryDirectoryInfo.cs index d94f1982e1ea..55688a8baf4d 100644 --- a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/InMemoryDirectoryInfo.cs +++ b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/InMemoryDirectoryInfo.cs @@ -119,13 +119,11 @@ public override IEnumerable EnumerateFileSystemInfos() private bool IsRootDirectory(string rootDir, string filePath) { - if (!filePath.StartsWith(rootDir, StringComparison.Ordinal) || - filePath.IndexOf(Path.DirectorySeparatorChar, rootDir.Length) != rootDir.Length) - { - return false; - } + int rootDirLength = rootDir.Length; - return true; + return filePath.StartsWith(rootDir, StringComparison.Ordinal) && + (rootDir[rootDirLength - 1] == Path.DirectorySeparatorChar || + filePath.IndexOf(Path.DirectorySeparatorChar, rootDirLength) == rootDirLength); } /// diff --git a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/tests/FunctionalTests.cs b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/tests/FunctionalTests.cs index ce56c09bc1a7..602047b5e478 100644 --- a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/tests/FunctionalTests.cs +++ b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/tests/FunctionalTests.cs @@ -635,9 +635,118 @@ public void StemIncludesAllSegmentsFromPatternStartingAtWildcard_TwoDirectoriesD Assert.Equal(expectedStem, actualStem); } - private List GetFileList() + [Theory] + [InlineData("/", '/')] + public void RootDir_IsPathRoot_WithInMemory_AllOS(string rootDir, char separator) + { + RootDir_IsPathRoot_WithInMemory(rootDir, separator); + } + + [Theory] + [PlatformSpecific(TestPlatforms.Windows)] + [InlineData("C:\\", '\\')] + [InlineData("C:/", '/')] + public void RootDir_IsPathRoot_WithInMemory_WindowsOnly(string rootDir, char separator) { - return new List + RootDir_IsPathRoot_WithInMemory(rootDir, separator); + } + + private static void RootDir_IsPathRoot_WithInMemory(string rootDir, char separator) + { + var matcher = new Matcher(); + matcher.AddInclude($"**{separator}*.cs"); + + IEnumerable files = GetFileList(rootDir, separator); + PatternMatchingResult results = matcher.Match(rootDir, files); + + IEnumerable actual = results.Files.Select(match => match.Path); + IEnumerable expected = new string[] + { + "src/project/source1.cs", + "src/project/sub/source2.cs", + "src/project/sub/source3.cs", + "src/project/sub2/source4.cs", + "src/project/sub2/source5.cs", + "src/project/compiler/preprocess/preprocess-source1.cs", + "src/project/compiler/preprocess/sub/preprocess-source2.cs", + "src/project/compiler/preprocess/sub/sub/preprocess-source3.cs", + "src/project/compiler/shared/shared1.cs", + "src/project/compiler/shared/sub/shared2.cs", + "src/project/compiler/shared/sub/sub/sharedsub.cs", + "src/project2/source1.cs", + "src/project2/sub/source2.cs", + "src/project2/sub/source3.cs", + "src/project2/sub2/source4.cs", + "src/project2/sub2/source5.cs", + "src/project2/compiler/preprocess/preprocess-source1.cs", + "src/project2/compiler/preprocess/sub/preprocess-source2.cs", + "src/project2/compiler/preprocess/sub/sub/preprocess-source3.cs", + "src/project2/compiler/shared/shared1.cs", + "src/project2/compiler/shared/sub/shared2.cs", + "src/project2/compiler/shared/sub/sub/sharedsub.cs", + "lib/source6.cs", + "lib/sub3/source7.cs", + "lib/sub4/source8.cs", + }; + + Assert.Equal( + expected.OrderBy(e => e), + actual.OrderBy(e => e), + StringComparer.OrdinalIgnoreCase); + } + + [Theory] + [InlineData("/src/project", '/')] + [InlineData("/src/project/", '/')] + public void RootDir_IsAbsolutePath_WithInMemory_AllOS(string rootDir, char separator) + { + RootDir_IsAbsolutePath_WithInMemory(rootDir, separator); + } + + [Theory] + [PlatformSpecific(TestPlatforms.Windows)] + [InlineData("C:\\src\\project", '\\')] + [InlineData("C:\\src\\project\\", '\\')] + [InlineData("C:/src/project", '/')] + [InlineData("C:/src/project/", '/')] + public void RootDir_IsAbsolutePath_WithInMemory_WindowsOnly(string rootDir, char separator) + { + RootDir_IsAbsolutePath_WithInMemory(rootDir, separator); + } + + private static void RootDir_IsAbsolutePath_WithInMemory(string rootDir, char separator) + { + var matcher = new Matcher(); + matcher.AddInclude($"**{separator}*.cs"); + + IEnumerable files = GetFileList(Path.GetPathRoot(rootDir), separator); + PatternMatchingResult results = matcher.Match(rootDir, files); + + IEnumerable actual = results.Files.Select(match => match.Path); + IEnumerable expected = new string[] + { + "source1.cs", + "sub/source2.cs", + "sub/source3.cs", + "sub2/source4.cs", + "sub2/source5.cs", + "compiler/preprocess/preprocess-source1.cs", + "compiler/preprocess/sub/preprocess-source2.cs", + "compiler/preprocess/sub/sub/preprocess-source3.cs", + "compiler/shared/shared1.cs", + "compiler/shared/sub/shared2.cs", + "compiler/shared/sub/sub/sharedsub.cs" + }; + + Assert.Equal( + expected.OrderBy(e => e), + actual.OrderBy(e => e), + StringComparer.OrdinalIgnoreCase); + } + + private static IEnumerable GetFileList(string rootDir = "", char directorySeparator = '/') + { + var files = new List { "root/test.0", "root/dir1/test.1", @@ -693,6 +802,8 @@ private List GetFileList() ".hidden/file1.hid", ".hidden/sub/file2.hid" }; + + return files.Select(x => (rootDir + x).Replace('/', directorySeparator)); } private DisposableFileSystem CreateContext() @@ -713,5 +824,44 @@ private void ExecuteAndVerify(Matcher matcher, string directoryPath, params stri AssertExtensions.CollectionEqual(expected, actual, StringComparer.OrdinalIgnoreCase); } + + [Fact] // https://github.com/dotnet/runtime/issues/44767 + public void VerifyAbsolutePaths_HasMatches() + { + var fileMatcher = new Matcher(); + fileMatcher.AddInclude("**/*"); + + if (PlatformDetection.IsWindows) + { + // Windows-like absolute paths are not supported on Unix. + string fakeWindowsPath = "C:\\This\\is\\a\\nested\\windows-like\\path\\somefile.cs"; + Assert.True(fileMatcher.Match(Path.GetPathRoot(fakeWindowsPath), fakeWindowsPath).HasMatches); + } + + // Unix-like absolute paths are treated as relative paths on Windows. + string fakeUnixPath = "/This/is/a/nested/unix-like/path/somefile.cs"; + Assert.True(fileMatcher.Match(Path.GetPathRoot(fakeUnixPath), fakeUnixPath).HasMatches); + } + + [Fact] // https://github.com/dotnet/runtime/issues/36415 + public void VerifyInMemoryDirectoryInfo_IsNotEmpty() + { + IEnumerable files = new[] { @"pagefile.sys" }; + InMemoryDirectoryInfo directoryInfo; + IEnumerable fileSystemInfos; + + if (PlatformDetection.IsWindows) + { + directoryInfo = new InMemoryDirectoryInfo(@"C:\", files); + fileSystemInfos = directoryInfo.EnumerateFileSystemInfos(); + + Assert.Equal(1, fileSystemInfos.Count()); + } + + directoryInfo = new InMemoryDirectoryInfo("/", files); + fileSystemInfos = directoryInfo.EnumerateFileSystemInfos(); + + Assert.Equal(1, fileSystemInfos.Count()); + } } } From b23205f59ab3853e4b6b43f91a7ca3900c0cc7d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Tue, 30 Mar 2021 18:52:30 +0200 Subject: [PATCH 49/98] Use OptimizeForSize condition in SPC only on Browser (#50413) The size impact doesn't matter for tvOS. --- .../src/System.Private.CoreLib.Shared.projitems | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index b294ebe1ce10..4e86ee49effe 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -16,7 +16,7 @@ true $(MSBuildThisFileDirectory)ILLink\ true - true + true $(DefineConstants);TARGET_32BIT From 01081d62d5703e475b720277feac9b000bbcd1dd Mon Sep 17 00:00:00 2001 From: Maoni Stephens Date: Tue, 30 Mar 2021 15:29:29 -0700 Subject: [PATCH 50/98] expose alloc data on dbi (#50341) we are already exposing this data for sos, should make this available for debuggers to use as well. it's very lightweight and would be very useful for things like showing exactly how many bytes were allocated on the thread between 2 breakpoints. --- src/coreclr/debug/daccess/dacdbiimpl.cpp | 11 +++++++++++ src/coreclr/debug/daccess/dacdbiimpl.h | 3 +++ src/coreclr/debug/inc/dacdbiinterface.h | 11 +++++++++++ src/coreclr/debug/inc/dacdbistructures.h | 7 +++++++ 4 files changed, 32 insertions(+) diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index d38273244086..076662042084 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -4787,6 +4787,17 @@ VMPTR_OBJECTHANDLE DacDbiInterfaceImpl::GetThreadObject(VMPTR_Thread vmThread) } } +void DacDbiInterfaceImpl::GetThreadAllocInfo(VMPTR_Thread vmThread, + DacThreadAllocInfo* threadAllocInfo) +{ + DD_ENTER_MAY_THROW; + + Thread * pThread = vmThread.GetDacPtr(); + gc_alloc_context* allocContext = pThread->GetAllocContext(); + threadAllocInfo->m_allocBytesSOH = (ULONG)(allocContext->alloc_bytes - (allocContext->alloc_limit - allocContext->alloc_ptr)); + threadAllocInfo->m_allocBytesUOH = (ULONG)allocContext->alloc_bytes_uoh; +} + // Set and reset the TSNC_DebuggerUserSuspend bit on the state of the specified thread // according to the CorDebugThreadState. void DacDbiInterfaceImpl::SetDebugState(VMPTR_Thread vmThread, diff --git a/src/coreclr/debug/daccess/dacdbiimpl.h b/src/coreclr/debug/daccess/dacdbiimpl.h index 42028b59d46d..3088ed007a71 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.h +++ b/src/coreclr/debug/daccess/dacdbiimpl.h @@ -783,6 +783,9 @@ class DacDbiInterfaceImpl : // Return the object handle for the managed Thread object corresponding to the specified thread. VMPTR_OBJECTHANDLE GetThreadObject(VMPTR_Thread vmThread); + // Get the alocated bytes for this thread. + void GetThreadAllocInfo(VMPTR_Thread vmThread, DacThreadAllocInfo* threadAllocInfo); + // Set and reset the TSNC_DebuggerUserSuspend bit on the state of the specified thread // according to the CorDebugThreadState. void SetDebugState(VMPTR_Thread vmThread, diff --git a/src/coreclr/debug/inc/dacdbiinterface.h b/src/coreclr/debug/inc/dacdbiinterface.h index 5ca550ae0f1f..2dc0beca1c0a 100644 --- a/src/coreclr/debug/inc/dacdbiinterface.h +++ b/src/coreclr/debug/inc/dacdbiinterface.h @@ -1065,6 +1065,17 @@ class IDacDbiInterface virtual VMPTR_OBJECTHANDLE GetThreadObject(VMPTR_Thread vmThread) = 0; + + // + // Get the allocation info corresponding to the specified thread. + // + // Arguments: + // vmThread - the specified thread + // threadAllocInfo - the allocated bytes from SOH and UOH so far on this thread + // + + virtual + void GetThreadAllocInfo(VMPTR_Thread vmThread, DacThreadAllocInfo* threadAllocInfo) = 0; // // Set and reset the TSNC_DebuggerUserSuspend bit on the state of the specified thread diff --git a/src/coreclr/debug/inc/dacdbistructures.h b/src/coreclr/debug/inc/dacdbistructures.h index b515cd7f1f38..2dabaa48e2c2 100644 --- a/src/coreclr/debug/inc/dacdbistructures.h +++ b/src/coreclr/debug/inc/dacdbistructures.h @@ -784,5 +784,12 @@ struct MSLAYOUT DacSharedReJitInfo CORDB_ADDRESS m_rgInstrumentedMapEntries; }; +// These represent the allocated bytes so far on the thread. +struct MSLAYOUT DacThreadAllocInfo +{ + ULONG m_allocBytesSOH; + ULONG m_allocBytesUOH; +}; + #include "dacdbistructures.inl" #endif // DACDBISTRUCTURES_H_ From a5b829e9c7c6d672262c50a3780bbff426ec1266 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Tue, 30 Mar 2021 15:45:56 -0700 Subject: [PATCH 51/98] Rename the superpmi.py asmdiffs output directory (#50403) * Rename the superpmi.py asmdiffs output directory Previously, the directory name was `asm.os.arch.build_flavor`. Now, use the MCH file name instead. Since the MCH filename of our collections has the target os/arch/flavor, those components will still be there (but will be based on the MCH file, not the host; this is important if you are doing cross-compile asm diffs). It will also have the MCH base name, such as "benchmarks.run", to help distinguish different runs. Of course, if there is a conflict with a previous run, `.1`, `.2`, etc., is appended to the directory name. Also, add a new, optional `-tag` option to `superpmi.py asmdiffs` to allow specifying an additional word to prefix the artifacts folder base name. This might help more easily distinguish different experiments. So, `superpmi.py asmdiffs -filter benchmarks -tag test2` might give you dasm files in these directories: ``` c:\runtime\artifacts\spmi\asm.test2.benchmarks.run.windows.x64.checked.3\base c:\runtime\artifacts\spmi\asm.test2.benchmarks.run.windows.x64.checked.3\diff ``` * Review feedback Created `create_artifacts_base_name()` function. Fixed `run_and_log` to display command line of command being invoked at the specified log level. Currently only affects invoking `jit-analyze`, which makes the `jit-analyze` command line visible on the console, not just in the log file. --- src/coreclr/scripts/superpmi.py | 57 ++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/src/coreclr/scripts/superpmi.py b/src/coreclr/scripts/superpmi.py index f26b63bc6737..2f956b1214e7 100755 --- a/src/coreclr/scripts/superpmi.py +++ b/src/coreclr/scripts/superpmi.py @@ -287,7 +287,6 @@ # subparser for asmdiffs asm_diff_parser = subparsers.add_parser("asmdiffs", description=asm_diff_description, parents=[core_root_parser, target_parser, superpmi_common_parser, replay_common_parser]) -# Add required arguments asm_diff_parser.add_argument("-base_jit_path", help="Path to baseline clrjit. Defaults to baseline JIT from rolling build, by computing baseline git hash.") asm_diff_parser.add_argument("-diff_jit_path", help="Path to diff clrjit. Defaults to Core_Root JIT.") asm_diff_parser.add_argument("-git_hash", help="Use this git hash as the current hash for use to find a baseline JIT. Defaults to current git hash of source tree.") @@ -295,8 +294,9 @@ asm_diff_parser.add_argument("--diff_jit_dump", action="store_true", help="Generate JitDump output for diffs. Default: only generate asm, not JitDump.") asm_diff_parser.add_argument("-temp_dir", help="Specify a temporary directory used for a previous ASM diffs run (for which --skip_cleanup was used) to view the results. The replay command is skipped.") asm_diff_parser.add_argument("--gcinfo", action="store_true", help="Include GC info in disassembly (sets COMPlus_JitGCDump/COMPlus_NgenGCDump; requires instructions to be prefixed by offsets).") -asm_diff_parser.add_argument("-base_jit_option", action="append", help="Option to pass to the baselne JIT. Format is key=value, where key is the option name without leading COMPlus_...") +asm_diff_parser.add_argument("-base_jit_option", action="append", help="Option to pass to the baseline JIT. Format is key=value, where key is the option name without leading COMPlus_...") asm_diff_parser.add_argument("-diff_jit_option", action="append", help="Option to pass to the diff JIT. Format is key=value, where key is the option name without leading COMPlus_...") +asm_diff_parser.add_argument("-tag", help="Specify a word to add to the directory name where the asm diffs will be placed") # subparser for upload upload_parser = subparsers.add_parser("upload", description=upload_description, parents=[core_root_parser, target_parser]) @@ -648,7 +648,7 @@ def run_and_log(command, log_level=logging.DEBUG): Process return code """ - logging.debug("Invoking: %s", " ".join(command)) + logging.log(log_level, "Invoking: %s", " ".join(command)) proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout_output, _ = proc.communicate() for line in stdout_output.decode('utf-8', errors='replace').splitlines(): # There won't be any stderr output since it was piped to stdout @@ -697,6 +697,32 @@ def check_target_arch(coreclr_args, target_arch): def check_mch_arch(coreclr_args, mch_arch): return (mch_arch is not None) and (mch_arch in coreclr_args.valid_arches) + +def create_artifacts_base_name(coreclr_args, mch_file): + """ Create an appropriate "base" name for use creating a directory name related to MCH file playback. + This will later be prepended by "asm." or "jitdump.", for example, and + create_unique_directory_name() should be called on the final name to ensure it is unique. + + Use the MCH file base name as the main part of the directory name, removing + the trailing ".mch", if any. + + If there is a tag specified (for asm diffs), prepend the tag. + + Args: + coreclr_args : the parsed arguments + mch_file (str) : the MCH file name that is being replayed. + + Returns: + A directory name to be used. + """ + artifacts_base_name = os.path.basename(mch_file) + if artifacts_base_name.lower().endswith(".mch"): + artifacts_base_name = artifacts_base_name[:-4] + if hasattr(coreclr_args, "tag") and coreclr_args.tag is not None: + artifacts_base_name = "{}.{}".format(coreclr_args.tag, artifacts_base_name) + return artifacts_base_name + + ################################################################################ # Helper classes ################################################################################ @@ -1471,14 +1497,14 @@ def print_fail_mcl_file_method_numbers(fail_mcl_file): logging.debug(line) -def save_repro_mc_files(temp_location, coreclr_args, repro_base_command_line): +def save_repro_mc_files(temp_location, coreclr_args, artifacts_base_name, repro_base_command_line): """ For commands that use the superpmi "-r" option to create "repro" .mc files, copy these to a location where they are saved (and not in a "temp" directory) for easy use by the user. """ # If there are any .mc files, drop them into artifacts/repro/../*.mc mc_files = [os.path.join(temp_location, item) for item in os.listdir(temp_location) if item.endswith(".mc")] if len(mc_files) > 0: - repro_location = create_unique_directory_name(coreclr_args.spmi_location, "repro.{}.{}.{}".format(coreclr_args.host_os, coreclr_args.arch, coreclr_args.build_type)) + repro_location = create_unique_directory_name(coreclr_args.spmi_location, "repro.{}".format(artifacts_base_name)) repro_files = [] for item in mc_files: @@ -1623,7 +1649,8 @@ def replay(self): logging.warning("Warning: SuperPMI returned a zero exit code, but generated a non-zero-sized mcl file") print_fail_mcl_file_method_numbers(fail_mcl_file) repro_base_command_line = "{} {} {}".format(self.superpmi_path, " ".join(repro_flags), self.jit_path) - save_repro_mc_files(temp_location, self.coreclr_args, repro_base_command_line) + artifacts_base_name = create_artifacts_base_name(self.coreclr_args, mch_file) + save_repro_mc_files(temp_location, self.coreclr_args, artifacts_base_name, repro_base_command_line) if not self.coreclr_args.skip_cleanup: if os.path.isfile(fail_mcl_file): @@ -1824,13 +1851,15 @@ def replay_with_asm_diffs(self): files_with_replay_failures.append(mch_file) result = False + artifacts_base_name = create_artifacts_base_name(self.coreclr_args, mch_file) + if is_nonzero_length_file(fail_mcl_file): # Unclean replay. Examine the contents of the fail.mcl file to dig into failures. if return_code == 0: logging.warning("Warning: SuperPMI returned a zero exit code, but generated a non-zero-sized mcl file") print_fail_mcl_file_method_numbers(fail_mcl_file) repro_base_command_line = "{} {} {}".format(self.superpmi_path, " ".join(altjit_asm_diffs_flags), self.diff_jit_path) - save_repro_mc_files(temp_location, self.coreclr_args, repro_base_command_line) + save_repro_mc_files(temp_location, self.coreclr_args, artifacts_base_name, repro_base_command_line) # There were diffs. Go through each method that created diffs and # create a base/diff asm file with diffable asm. In addition, create @@ -1850,7 +1879,7 @@ def replay_with_asm_diffs(self): mcl_lines = [item.strip() for item in mcl_lines] self.diff_mcl_contents = mcl_lines - asm_root_dir = create_unique_directory_name(self.coreclr_args.spmi_location, "asm.{}.{}.{}".format(self.coreclr_args.host_os, self.coreclr_args.arch, self.coreclr_args.build_type)) + asm_root_dir = create_unique_directory_name(self.coreclr_args.spmi_location, "asm.{}".format(artifacts_base_name)) base_asm_location = os.path.join(asm_root_dir, "base") diff_asm_location = os.path.join(asm_root_dir, "diff") os.makedirs(base_asm_location) @@ -1858,7 +1887,7 @@ def replay_with_asm_diffs(self): if self.coreclr_args.diff_jit_dump: # If JIT dumps are requested, create a diff and baseline directory for JIT dumps - jitdump_root_dir = create_unique_directory_name(self.coreclr_args.spmi_location, "jitdump.{}.{}.{}".format(self.coreclr_args.host_os, self.coreclr_args.arch, self.coreclr_args.build_type)) + jitdump_root_dir = create_unique_directory_name(self.coreclr_args.spmi_location, "jitdump.{}".format(artifacts_base_name)) base_dump_location = os.path.join(jitdump_root_dir, "base") diff_dump_location = os.path.join(jitdump_root_dir, "diff") os.makedirs(base_dump_location) @@ -1907,12 +1936,12 @@ async def create_one_artifact(jit_path: str, location: str, flags: list[str]) -> for item in self.diff_mcl_contents: diff_items.append(item) - logging.info("Creating dasm files") + logging.info("Creating dasm files: %s %s", base_asm_location, diff_asm_location) subproc_helper = AsyncSubprocessHelper(diff_items, verbose=True) subproc_helper.run_to_completion(create_replay_artifacts, self, mch_file, asm_complus_vars_full_env, text_differences, base_asm_location, diff_asm_location, ".dasm") if self.coreclr_args.diff_jit_dump: - logging.info("Creating JitDump files") + logging.info("Creating JitDump files: %s %s", base_dump_location, diff_dump_location) subproc_helper.run_to_completion(create_replay_artifacts, self, mch_file, jit_dump_complus_vars_full_env, jit_dump_differences, base_dump_location, diff_dump_location, ".txt") logging.info("Differences found. To replay SuperPMI use:") @@ -3451,6 +3480,12 @@ def verify_replay_common_args(): lambda unused: True, "Unable to set diff_jit_option.") + coreclr_args.verify(args, + "tag", + lambda unused: True, + "Unable to set tag.", + modify_arg=lambda arg: make_safe_filename(arg) if arg is not None else arg) + process_base_jit_path_arg(coreclr_args) jit_in_product_location = False From 62ac429174fdc8f461c08a0e3ba51358ceb7773e Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Tue, 30 Mar 2021 19:17:23 -0400 Subject: [PATCH 52/98] [mono] Move the jit info table definitions into a separate jit-info.h file, it no longer depends on domains. (#50423) --- src/mono/mono/metadata/CMakeLists.txt | 1 + src/mono/mono/metadata/domain-internals.h | 261 ------------------- src/mono/mono/metadata/domain.c | 1 + src/mono/mono/metadata/exception.c | 1 + src/mono/mono/metadata/jit-info.c | 1 + src/mono/mono/metadata/jit-info.h | 290 ++++++++++++++++++++++ src/mono/mono/metadata/loader.c | 1 + src/mono/mono/metadata/threads.c | 1 + src/mono/mono/mini/mini.h | 1 + 9 files changed, 297 insertions(+), 261 deletions(-) create mode 100644 src/mono/mono/metadata/jit-info.h diff --git a/src/mono/mono/metadata/CMakeLists.txt b/src/mono/mono/metadata/CMakeLists.txt index f5281ab0bf7a..4b56d07c4405 100644 --- a/src/mono/mono/metadata/CMakeLists.txt +++ b/src/mono/mono/metadata/CMakeLists.txt @@ -94,6 +94,7 @@ set(metadata_common_sources icall-eventpipe.c image.c image-internals.h + jit-info.h jit-info.c loader.c lock-tracer.c diff --git a/src/mono/mono/metadata/domain-internals.h b/src/mono/mono/metadata/domain-internals.h index 05aafc20417e..c822b1426a15 100644 --- a/src/mono/mono/metadata/domain-internals.h +++ b/src/mono/mono/metadata/domain-internals.h @@ -32,214 +32,6 @@ G_BEGIN_DECLS */ extern gboolean mono_dont_free_domains; -typedef struct _MonoJitInfoTable MonoJitInfoTable; -typedef struct _MonoJitInfoTableChunk MonoJitInfoTableChunk; - -#define MONO_JIT_INFO_TABLE_CHUNK_SIZE 64 - -struct _MonoJitInfoTableChunk -{ - int refcount; - volatile int num_elements; - volatile gint8 *last_code_end; - MonoJitInfo *next_tombstone; - MonoJitInfo * volatile data [MONO_JIT_INFO_TABLE_CHUNK_SIZE]; -}; - -struct _MonoJitInfoTable -{ - MonoDomain *domain; - int num_chunks; - int num_valid; - MonoJitInfoTableChunk *chunks [MONO_ZERO_LEN_ARRAY]; -}; - -#define MONO_SIZEOF_JIT_INFO_TABLE (sizeof (struct _MonoJitInfoTable) - MONO_ZERO_LEN_ARRAY * SIZEOF_VOID_P) - -typedef GArray MonoAotModuleInfoTable; - -typedef struct { - guint32 flags; - gint32 exvar_offset; - gpointer try_start; - gpointer try_end; - gpointer handler_start; - /* - * For LLVM compiled code, this is the index of the il clause - * associated with this handler. - */ - int clause_index; - uint32_t try_offset; - uint32_t try_len; - uint32_t handler_offset; - uint32_t handler_len; - union { - MonoClass *catch_class; - gpointer filter; - gpointer handler_end; - } data; -} MonoJitExceptionInfo; - -/* - * Contains information about the type arguments for generic shared methods. - */ -typedef struct { - gboolean is_gsharedvt; -} MonoGenericSharingContext; - -/* Simplified DWARF location list entry */ -typedef struct { - /* Whenever the value is in a register */ - gboolean is_reg; - /* - * If is_reg is TRUE, the register which contains the value. Otherwise - * the base register. - */ - int reg; - /* - * If is_reg is FALSE, the offset of the stack location relative to 'reg'. - * Otherwise, 0. - */ - int offset; - /* - * Offsets of the PC interval where the value is in this location. - */ - int from, to; -} MonoDwarfLocListEntry; - -typedef struct -{ - MonoGenericSharingContext *generic_sharing_context; - int nlocs; - MonoDwarfLocListEntry *locations; - gint32 this_offset; - guint8 this_reg; - gboolean has_this:1; - gboolean this_in_reg:1; -} MonoGenericJitInfo; - -/* -A try block hole is used to represent a non-contiguous part of -of a segment of native code protected by a given .try block. -Usually, a try block is defined as a contiguous segment of code. -But in some cases it's needed to have some parts of it to not be protected. -For example, given "try {} finally {}", the code in the .try block to call -the finally part looks like: - -try { - ... - call finally_block - adjust stack - jump outside try block - ... -} finally { - ... -} - -The instructions between the call and the jump should not be under the try block since they happen -after the finally block executes, which means if an async exceptions happens at that point we would -execute the finally clause twice. So, to avoid this, we introduce a hole in the try block to signal -that those instructions are not protected. -*/ -typedef struct -{ - guint32 offset; - guint16 clause; - guint16 length; -} MonoTryBlockHoleJitInfo; - -typedef struct -{ - guint16 num_holes; - MonoTryBlockHoleJitInfo holes [MONO_ZERO_LEN_ARRAY]; -} MonoTryBlockHoleTableJitInfo; - -typedef struct -{ - guint32 stack_size; - guint32 epilog_size; -} MonoArchEHJitInfo; - -typedef struct { - /* Relative to code_start */ - int thunks_offset; - int thunks_size; -} MonoThunkJitInfo; - -typedef struct { - guint8 *unw_info; - int unw_info_len; -} MonoUnwindJitInfo; - -typedef enum { - JIT_INFO_NONE = 0, - JIT_INFO_HAS_GENERIC_JIT_INFO = (1 << 0), - JIT_INFO_HAS_TRY_BLOCK_HOLES = (1 << 1), - JIT_INFO_HAS_ARCH_EH_INFO = (1 << 2), - JIT_INFO_HAS_THUNK_INFO = (1 << 3), - /* - * If this is set, the unwind info is stored in the structure, instead of being pointed to by the - * 'unwind_info' field. - */ - JIT_INFO_HAS_UNWIND_INFO = (1 << 4) -} MonoJitInfoFlags; - -G_ENUM_FUNCTIONS (MonoJitInfoFlags) - -struct _MonoJitInfo { - /* NOTE: These first two elements (method and - next_jit_code_hash) must be in the same order and at the - same offset as in RuntimeMethod, because of the jit_code_hash - internal hash table in MonoDomain. */ - union { - MonoMethod *method; - MonoImage *image; - MonoAotModule *aot_info; - MonoTrampInfo *tramp_info; - } d; - union { - MonoJitInfo *next_jit_code_hash; - MonoJitInfo *next_tombstone; - } n; - gpointer code_start; - guint32 unwind_info; - int code_size; - guint32 num_clauses:15; - gboolean has_generic_jit_info:1; - gboolean has_try_block_holes:1; - gboolean has_arch_eh_info:1; - gboolean has_thunk_info:1; - gboolean has_unwind_info:1; - gboolean from_aot:1; - gboolean from_llvm:1; - gboolean dbg_attrs_inited:1; - gboolean dbg_hidden:1; - /* Whenever this jit info was loaded in async context */ - gboolean async:1; - gboolean dbg_step_through:1; - gboolean dbg_non_user_code:1; - /* - * Whenever this jit info refers to a trampoline. - * d.tramp_info contains additional data in this case. - */ - gboolean is_trampoline:1; - /* Whenever this jit info refers to an interpreter method */ - gboolean is_interp:1; - - /* FIXME: Embed this after the structure later*/ - gpointer gc_info; /* Currently only used by SGen */ - - gpointer seq_points; - - MonoJitExceptionInfo clauses [MONO_ZERO_LEN_ARRAY]; - /* There is an optional MonoGenericJitInfo after the clauses */ - /* There is an optional MonoTryBlockHoleTableJitInfo after MonoGenericJitInfo clauses*/ - /* There is an optional MonoArchEHJitInfo after MonoTryBlockHoleTableJitInfo */ - /* There is an optional MonoThunkJitInfo after MonoArchEHJitInfo */ -}; - -#define MONO_SIZEOF_JIT_INFO (offsetof (struct _MonoJitInfo, clauses)) - struct _MonoAppContext { MonoObject obj; gint32 domain_id; @@ -335,65 +127,12 @@ mono_runtime_quit_internal (void); void mono_close_exe_image (void); -void -mono_jit_info_tables_init (void); - -int -mono_jit_info_size (MonoJitInfoFlags flags, int num_clauses, int num_holes); - -void -mono_jit_info_init (MonoJitInfo *ji, MonoMethod *method, guint8 *code, int code_size, - MonoJitInfoFlags flags, int num_clauses, int num_holes); - -MonoJitInfoTable * -mono_jit_info_table_new (void); - -void -mono_jit_info_table_free (MonoJitInfoTable *table); - -void -mono_jit_info_table_add (MonoJitInfo *ji); - -void -mono_jit_info_table_remove (MonoJitInfo *ji); - -void -mono_jit_info_add_aot_module (MonoImage *image, gpointer start, gpointer end); - -MonoGenericJitInfo* -mono_jit_info_get_generic_jit_info (MonoJitInfo *ji); - -MonoGenericSharingContext* -mono_jit_info_get_generic_sharing_context (MonoJitInfo *ji); - -void -mono_jit_info_set_generic_sharing_context (MonoJitInfo *ji, MonoGenericSharingContext *gsctx); - void mono_domain_unset (void); void mono_domain_set_internal_with_options (MonoDomain *domain, gboolean migrate_exception); -MonoTryBlockHoleTableJitInfo* -mono_jit_info_get_try_block_hole_table_info (MonoJitInfo *ji); - -MonoArchEHJitInfo* -mono_jit_info_get_arch_eh_info (MonoJitInfo *ji); - -MonoThunkJitInfo* -mono_jit_info_get_thunk_info (MonoJitInfo *ji); - -MonoUnwindJitInfo* -mono_jit_info_get_unwind_info (MonoJitInfo *ji); - -/* - * Installs a new function which is used to return a MonoJitInfo for a method inside - * an AOT module. - */ -typedef MonoJitInfo *(*MonoJitInfoFindInAot) (MonoDomain *domain, MonoImage *image, gpointer addr); -void mono_install_jit_info_find_in_aot (MonoJitInfoFindInAot func); - void mono_jit_code_hash_init (MonoInternalHashTable *jit_code_hash); diff --git a/src/mono/mono/metadata/domain.c b/src/mono/mono/metadata/domain.c index 88e45e8bcd97..ba2ea791834d 100644 --- a/src/mono/mono/metadata/domain.c +++ b/src/mono/mono/metadata/domain.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include "external-only.h" diff --git a/src/mono/mono/metadata/exception.c b/src/mono/mono/metadata/exception.c index fd43f641b08a..5e57b278e83d 100644 --- a/src/mono/mono/metadata/exception.c +++ b/src/mono/mono/metadata/exception.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include diff --git a/src/mono/mono/metadata/jit-info.c b/src/mono/mono/metadata/jit-info.c index a2f37556d95c..84bac024bb55 100644 --- a/src/mono/mono/metadata/jit-info.c +++ b/src/mono/mono/metadata/jit-info.c @@ -17,6 +17,7 @@ #include #include +#include #include #include diff --git a/src/mono/mono/metadata/jit-info.h b/src/mono/mono/metadata/jit-info.h new file mode 100644 index 000000000000..2f54f453882a --- /dev/null +++ b/src/mono/mono/metadata/jit-info.h @@ -0,0 +1,290 @@ +/** + * \file + * MonoJitInfo table. + * Copyright 2012 Xamarin Inc (http://www.xamarin.com) + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ +#ifndef __MONO_METADATA_JIT_INFO_H__ +#define __MONO_METADATA_JIT_INFO_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +typedef struct _MonoJitInfoTable MonoJitInfoTable; +typedef struct _MonoJitInfoTableChunk MonoJitInfoTableChunk; + +#define MONO_JIT_INFO_TABLE_CHUNK_SIZE 64 + +struct _MonoJitInfoTableChunk +{ + int refcount; + volatile int num_elements; + volatile gint8 *last_code_end; + MonoJitInfo *next_tombstone; + MonoJitInfo * volatile data [MONO_JIT_INFO_TABLE_CHUNK_SIZE]; +}; + +struct _MonoJitInfoTable +{ + MonoDomain *domain; + int num_chunks; + int num_valid; + MonoJitInfoTableChunk *chunks [MONO_ZERO_LEN_ARRAY]; +}; + +#define MONO_SIZEOF_JIT_INFO_TABLE (sizeof (struct _MonoJitInfoTable) - MONO_ZERO_LEN_ARRAY * SIZEOF_VOID_P) + +typedef GArray MonoAotModuleInfoTable; + +typedef struct { + guint32 flags; + gint32 exvar_offset; + gpointer try_start; + gpointer try_end; + gpointer handler_start; + /* + * For LLVM compiled code, this is the index of the il clause + * associated with this handler. + */ + int clause_index; + uint32_t try_offset; + uint32_t try_len; + uint32_t handler_offset; + uint32_t handler_len; + union { + MonoClass *catch_class; + gpointer filter; + gpointer handler_end; + } data; +} MonoJitExceptionInfo; + +/* + * Contains information about the type arguments for generic shared methods. + */ +typedef struct { + gboolean is_gsharedvt; +} MonoGenericSharingContext; + +/* Simplified DWARF location list entry */ +typedef struct { + /* Whenever the value is in a register */ + gboolean is_reg; + /* + * If is_reg is TRUE, the register which contains the value. Otherwise + * the base register. + */ + int reg; + /* + * If is_reg is FALSE, the offset of the stack location relative to 'reg'. + * Otherwise, 0. + */ + int offset; + /* + * Offsets of the PC interval where the value is in this location. + */ + int from, to; +} MonoDwarfLocListEntry; + +typedef struct +{ + MonoGenericSharingContext *generic_sharing_context; + int nlocs; + MonoDwarfLocListEntry *locations; + gint32 this_offset; + guint8 this_reg; + gboolean has_this:1; + gboolean this_in_reg:1; +} MonoGenericJitInfo; + +/* +A try block hole is used to represent a non-contiguous part of +of a segment of native code protected by a given .try block. +Usually, a try block is defined as a contiguous segment of code. +But in some cases it's needed to have some parts of it to not be protected. +For example, given "try {} finally {}", the code in the .try block to call +the finally part looks like: + +try { + ... + call finally_block + adjust stack + jump outside try block + ... +} finally { + ... +} + +The instructions between the call and the jump should not be under the try block since they happen +after the finally block executes, which means if an async exceptions happens at that point we would +execute the finally clause twice. So, to avoid this, we introduce a hole in the try block to signal +that those instructions are not protected. +*/ +typedef struct +{ + guint32 offset; + guint16 clause; + guint16 length; +} MonoTryBlockHoleJitInfo; + +typedef struct +{ + guint16 num_holes; + MonoTryBlockHoleJitInfo holes [MONO_ZERO_LEN_ARRAY]; +} MonoTryBlockHoleTableJitInfo; + +typedef struct +{ + guint32 stack_size; + guint32 epilog_size; +} MonoArchEHJitInfo; + +typedef struct { + /* Relative to code_start */ + int thunks_offset; + int thunks_size; +} MonoThunkJitInfo; + +typedef struct { + guint8 *unw_info; + int unw_info_len; +} MonoUnwindJitInfo; + +typedef enum { + JIT_INFO_NONE = 0, + JIT_INFO_HAS_GENERIC_JIT_INFO = (1 << 0), + JIT_INFO_HAS_TRY_BLOCK_HOLES = (1 << 1), + JIT_INFO_HAS_ARCH_EH_INFO = (1 << 2), + JIT_INFO_HAS_THUNK_INFO = (1 << 3), + /* + * If this is set, the unwind info is stored in the structure, instead of being pointed to by the + * 'unwind_info' field. + */ + JIT_INFO_HAS_UNWIND_INFO = (1 << 4) +} MonoJitInfoFlags; + +G_ENUM_FUNCTIONS (MonoJitInfoFlags) + +struct _MonoJitInfo { + /* NOTE: These first two elements (method and + next_jit_code_hash) must be in the same order and at the + same offset as in RuntimeMethod, because of the jit_code_hash + internal hash table in MonoDomain. */ + union { + MonoMethod *method; + MonoImage *image; + MonoAotModule *aot_info; + MonoTrampInfo *tramp_info; + } d; + union { + MonoJitInfo *next_jit_code_hash; + MonoJitInfo *next_tombstone; + } n; + gpointer code_start; + guint32 unwind_info; + int code_size; + guint32 num_clauses:15; + gboolean has_generic_jit_info:1; + gboolean has_try_block_holes:1; + gboolean has_arch_eh_info:1; + gboolean has_thunk_info:1; + gboolean has_unwind_info:1; + gboolean from_aot:1; + gboolean from_llvm:1; + gboolean dbg_attrs_inited:1; + gboolean dbg_hidden:1; + /* Whenever this jit info was loaded in async context */ + gboolean async:1; + gboolean dbg_step_through:1; + gboolean dbg_non_user_code:1; + /* + * Whenever this jit info refers to a trampoline. + * d.tramp_info contains additional data in this case. + */ + gboolean is_trampoline:1; + /* Whenever this jit info refers to an interpreter method */ + gboolean is_interp:1; + + /* FIXME: Embed this after the structure later*/ + gpointer gc_info; /* Currently only used by SGen */ + + gpointer seq_points; + + MonoJitExceptionInfo clauses [MONO_ZERO_LEN_ARRAY]; + /* There is an optional MonoGenericJitInfo after the clauses */ + /* There is an optional MonoTryBlockHoleTableJitInfo after MonoGenericJitInfo clauses*/ + /* There is an optional MonoArchEHJitInfo after MonoTryBlockHoleTableJitInfo */ + /* There is an optional MonoThunkJitInfo after MonoArchEHJitInfo */ +}; + +#define MONO_SIZEOF_JIT_INFO (offsetof (struct _MonoJitInfo, clauses)) + +void +mono_jit_info_tables_init (void); + +int +mono_jit_info_size (MonoJitInfoFlags flags, int num_clauses, int num_holes); + +void +mono_jit_info_init (MonoJitInfo *ji, MonoMethod *method, guint8 *code, int code_size, + MonoJitInfoFlags flags, int num_clauses, int num_holes); + +MonoJitInfoTable * +mono_jit_info_table_new (void); + +void +mono_jit_info_table_free (MonoJitInfoTable *table); + +void +mono_jit_info_table_add (MonoJitInfo *ji); + +void +mono_jit_info_table_remove (MonoJitInfo *ji); + +void +mono_jit_info_add_aot_module (MonoImage *image, gpointer start, gpointer end); + +MonoGenericJitInfo* +mono_jit_info_get_generic_jit_info (MonoJitInfo *ji); + +MonoGenericSharingContext* +mono_jit_info_get_generic_sharing_context (MonoJitInfo *ji); + +void +mono_jit_info_set_generic_sharing_context (MonoJitInfo *ji, MonoGenericSharingContext *gsctx); + +MonoTryBlockHoleTableJitInfo* +mono_jit_info_get_try_block_hole_table_info (MonoJitInfo *ji); + +MonoArchEHJitInfo* +mono_jit_info_get_arch_eh_info (MonoJitInfo *ji); + +MonoThunkJitInfo* +mono_jit_info_get_thunk_info (MonoJitInfo *ji); + +MonoUnwindJitInfo* +mono_jit_info_get_unwind_info (MonoJitInfo *ji); + +/* + * Installs a new function which is used to return a MonoJitInfo for a method inside + * an AOT module. + */ +typedef MonoJitInfo *(*MonoJitInfoFindInAot) (MonoDomain *domain, MonoImage *image, gpointer addr); +void mono_install_jit_info_find_in_aot (MonoJitInfoFindInAot func); + +G_END_DECLS + +#endif /* __MONO_METADATA_JIT_INFO_H__ */ diff --git a/src/mono/mono/metadata/loader.c b/src/mono/mono/metadata/loader.c index 22aaca70e241..d58494d032f9 100644 --- a/src/mono/mono/metadata/loader.c +++ b/src/mono/mono/metadata/loader.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include diff --git a/src/mono/mono/metadata/threads.c b/src/mono/mono/metadata/threads.c index abb788944976..2e43fe2ea237 100644 --- a/src/mono/mono/metadata/threads.c +++ b/src/mono/mono/metadata/threads.c @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include diff --git a/src/mono/mono/mini/mini.h b/src/mono/mono/mini/mini.h index d6e6ab422ddb..ac00a7223755 100644 --- a/src/mono/mono/mini/mini.h +++ b/src/mono/mono/mini/mini.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include From db7e04d23e3026b691c24e2b21224b5fa8ee3f43 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Tue, 30 Mar 2021 19:18:03 -0400 Subject: [PATCH 53/98] [mono] Remove domain lock (#50328) * [mono] Reduce domain lock usage. * Avoid double locking the loader+domain lock, its not needed. * Use the mem manager lock and the loader lock in a few places instead of the domain lock. * Use lock free code in mono_jit_runtime_invoke (). * Avoid locking around mono_jit_info_table_add (), it does locking itself. * [mono] Internalize access to MonoMemoryManager.mp to memory-manager.c. Also add a separate non-coop lock to protect it, since its accessed very frequently. * Use a separate lock for each memory manager. * Remove the domain lock. --- src/mono/mono/metadata/appdomain.c | 8 +-- src/mono/mono/metadata/domain-internals.h | 10 --- src/mono/mono/metadata/domain.c | 14 ----- src/mono/mono/metadata/loader-internals.h | 17 ++--- src/mono/mono/metadata/lock-tracer.h | 1 - src/mono/mono/metadata/memory-manager.c | 77 +++++++++++++---------- src/mono/mono/metadata/object.c | 25 ++++---- src/mono/mono/metadata/sre.c | 10 +-- src/mono/mono/mini/interp/interp.c | 24 ++++++- src/mono/mono/mini/mini-generic-sharing.c | 11 ++-- src/mono/mono/mini/mini-runtime.c | 9 +-- src/mono/mono/mini/mini.c | 8 +-- 12 files changed, 99 insertions(+), 115 deletions(-) diff --git a/src/mono/mono/metadata/appdomain.c b/src/mono/mono/metadata/appdomain.c index bbb920968126..11b20d7fc84f 100644 --- a/src/mono/mono/metadata/appdomain.c +++ b/src/mono/mono/metadata/appdomain.c @@ -605,13 +605,7 @@ mono_domain_try_type_resolve_name (MonoDomain *domain, MonoAssembly *assembly, M gboolean mono_domain_owns_vtable_slot (MonoDomain *domain, gpointer vtable_slot) { - gboolean res; - MonoMemoryManager *memory_manager = mono_mem_manager_get_ambient (); - - mono_mem_manager_lock (memory_manager); - res = mono_mempool_contains_addr (memory_manager->mp, vtable_slot); - mono_mem_manager_unlock (memory_manager); - return res; + return mono_mem_manager_mp_contains_addr (mono_mem_manager_get_ambient (), vtable_slot); } gboolean diff --git a/src/mono/mono/metadata/domain-internals.h b/src/mono/mono/metadata/domain-internals.h index c822b1426a15..9465be28d7b6 100644 --- a/src/mono/mono/metadata/domain-internals.h +++ b/src/mono/mono/metadata/domain-internals.h @@ -47,13 +47,6 @@ typedef struct _MonoThunkFreeList { typedef struct _MonoJitCodeHash MonoJitCodeHash; struct _MonoDomain { - /* - * This lock must never be taken before the loader lock, - * i.e. if both are taken by the same thread, the loader lock - * must taken first. - */ - MonoCoopMutex lock; - /* * keep all the managed objects close to each other for the precise GC * For the Boehm GC we additionally keep close also other GC-tracked pointers. @@ -112,9 +105,6 @@ mono_domain_assemblies_unlock (MonoDomain *domain) typedef MonoDomain* (*MonoLoadFunc) (const char *filename, const char *runtime_version); -void mono_domain_lock (MonoDomain *domain); -void mono_domain_unlock (MonoDomain *domain); - void mono_install_runtime_load (MonoLoadFunc func); diff --git a/src/mono/mono/metadata/domain.c b/src/mono/mono/metadata/domain.c index ba2ea791834d..c887af37f1f8 100644 --- a/src/mono/mono/metadata/domain.c +++ b/src/mono/mono/metadata/domain.c @@ -316,8 +316,6 @@ mono_domain_create (void) domain->domain_assemblies = NULL; - mono_coop_mutex_init_recursive (&domain->lock); - mono_coop_mutex_init_recursive (&domain->assemblies_lock); mono_appdomains_lock (); @@ -1257,18 +1255,6 @@ mono_get_runtime_info (void) return current_runtime; } -void -mono_domain_lock (MonoDomain *domain) -{ - mono_locks_coop_acquire (&domain->lock, DomainLock); -} - -void -mono_domain_unlock (MonoDomain *domain) -{ - mono_locks_coop_release (&domain->lock, DomainLock); -} - GPtrArray* mono_domain_get_assemblies (MonoDomain *domain) { diff --git a/src/mono/mono/metadata/loader-internals.h b/src/mono/mono/metadata/loader-internals.h index 051b11aa43ae..34f55769d7ca 100644 --- a/src/mono/mono/metadata/loader-internals.h +++ b/src/mono/mono/metadata/loader-internals.h @@ -86,7 +86,6 @@ struct _MonoAssemblyLoadContext { }; struct _MonoMemoryManager { - MonoDomain *domain; // Whether the MemoryManager can be unloaded on netcore; should only be set at creation gboolean collectible; // Whether this is a singleton or generic MemoryManager @@ -98,10 +97,15 @@ struct _MonoMemoryManager { // Currently unused, we take the domain lock instead MonoCoopMutex lock; - MonoMemPool *mp; + // Private, don't access directly + MonoMemPool *_mp; MonoCodeManager *code_mp; LockFreeMempool *lock_free_mp; + // Protects access to _mp + // Non-coop, non-recursive + mono_mutex_t mp_mutex; + GPtrArray *class_vtable_array; GHashTable *generic_virtual_cases; @@ -237,15 +241,9 @@ mono_mem_manager_unlock (MonoMemoryManager *memory_manager); void * mono_mem_manager_alloc (MonoMemoryManager *memory_manager, guint size); -void * -mono_mem_manager_alloc_nolock (MonoMemoryManager *memory_manager, guint size); - void * mono_mem_manager_alloc0 (MonoMemoryManager *memory_manager, guint size); -void * -mono_mem_manager_alloc0_nolock (MonoMemoryManager *memory_manager, guint size); - gpointer mono_mem_manager_alloc0_lock_free (MonoMemoryManager *memory_manager, guint size); @@ -271,6 +269,9 @@ mono_mem_manager_strdup (MonoMemoryManager *memory_manager, const char *s); void mono_mem_manager_free_debug_info (MonoMemoryManager *memory_manager); +gboolean +mono_mem_manager_mp_contains_addr (MonoMemoryManager *memory_manager, gpointer addr); + G_END_DECLS #endif diff --git a/src/mono/mono/metadata/lock-tracer.h b/src/mono/mono/metadata/lock-tracer.h index f9b0bb3f9aa9..8a111fffc26c 100644 --- a/src/mono/mono/metadata/lock-tracer.h +++ b/src/mono/mono/metadata/lock-tracer.h @@ -15,7 +15,6 @@ typedef enum { InvalidLock = 0, LoaderLock, ImageDataLock, - DomainLock, DomainAssembliesLock, DomainJitCodeHashLock, IcallLock, diff --git a/src/mono/mono/metadata/memory-manager.c b/src/mono/mono/metadata/memory-manager.c index b8cf5cba84b6..901e9452778b 100644 --- a/src/mono/mono/metadata/memory-manager.c +++ b/src/mono/mono/metadata/memory-manager.c @@ -95,12 +95,12 @@ memory_manager_init (MonoMemoryManager *memory_manager, gboolean collectible) { MonoDomain *domain = mono_get_root_domain (); - memory_manager->domain = domain; memory_manager->freeing = FALSE; mono_coop_mutex_init_recursive (&memory_manager->lock); + mono_os_mutex_init (&memory_manager->mp_mutex); - memory_manager->mp = mono_mempool_new (); + memory_manager->_mp = mono_mempool_new (); memory_manager->code_mp = mono_code_manager_new (); memory_manager->lock_free_mp = lock_free_mempool_new (); @@ -183,15 +183,15 @@ memory_manager_delete (MonoMemoryManager *memory_manager, gboolean debug_unload) mono_coop_mutex_destroy (&memory_manager->lock); if (debug_unload) { - mono_mempool_invalidate (memory_manager->mp); + mono_mempool_invalidate (memory_manager->_mp); mono_code_manager_invalidate (memory_manager->code_mp); } else { #ifndef DISABLE_PERFCOUNTERS /* FIXME: use an explicit subtraction method as soon as it's available */ - mono_atomic_fetch_add_i32 (&mono_perfcounters->loader_bytes, -1 * mono_mempool_get_allocated (memory_manager->mp)); + mono_atomic_fetch_add_i32 (&mono_perfcounters->loader_bytes, -1 * mono_mempool_get_allocated (memory_manager->_mp)); #endif - mono_mempool_destroy (memory_manager->mp); - memory_manager->mp = NULL; + mono_mempool_destroy (memory_manager->_mp); + memory_manager->_mp = NULL; mono_code_manager_destroy (memory_manager->code_mp); memory_manager->code_mp = NULL; } @@ -217,57 +217,55 @@ mono_mem_manager_free_singleton (MonoSingletonMemoryManager *memory_manager, gbo void mono_mem_manager_lock (MonoMemoryManager *memory_manager) { - //mono_coop_mutex_lock (&memory_manager->lock); - mono_domain_lock (memory_manager->domain); + mono_coop_mutex_lock (&memory_manager->lock); } void mono_mem_manager_unlock (MonoMemoryManager *memory_manager) { - //mono_coop_mutex_unlock (&memory_manager->lock); - mono_domain_unlock (memory_manager->domain); + mono_coop_mutex_unlock (&memory_manager->lock); } -void * -mono_mem_manager_alloc (MonoMemoryManager *memory_manager, guint size) +static inline void +alloc_lock (MonoMemoryManager *memory_manager) { - void *res; - - mono_mem_manager_lock (memory_manager); - res = mono_mem_manager_alloc_nolock (memory_manager, size); - mono_mem_manager_unlock (memory_manager); - - return res; + mono_os_mutex_lock (&memory_manager->mp_mutex); } -void * -mono_mem_manager_alloc_nolock (MonoMemoryManager *memory_manager, guint size) +static inline void +alloc_unlock (MonoMemoryManager *memory_manager) { -#ifndef DISABLE_PERFCOUNTERS - mono_atomic_fetch_add_i32 (&mono_perfcounters->loader_bytes, size); -#endif - return mono_mempool_alloc (memory_manager->mp, size); + mono_os_mutex_unlock (&memory_manager->mp_mutex); } void * -mono_mem_manager_alloc0 (MonoMemoryManager *memory_manager, guint size) +mono_mem_manager_alloc (MonoMemoryManager *memory_manager, guint size) { void *res; - mono_mem_manager_lock (memory_manager); - res = mono_mem_manager_alloc0_nolock (memory_manager, size); - mono_mem_manager_unlock (memory_manager); + alloc_lock (memory_manager); +#ifndef DISABLE_PERFCOUNTERS + mono_atomic_fetch_add_i32 (&mono_perfcounters->loader_bytes, size); +#endif + res = mono_mempool_alloc (memory_manager->_mp, size); + alloc_unlock (memory_manager); return res; } void * -mono_mem_manager_alloc0_nolock (MonoMemoryManager *memory_manager, guint size) +mono_mem_manager_alloc0 (MonoMemoryManager *memory_manager, guint size) { + void *res; + + alloc_lock (memory_manager); #ifndef DISABLE_PERFCOUNTERS mono_atomic_fetch_add_i32 (&mono_perfcounters->loader_bytes, size); #endif - return mono_mempool_alloc0 (memory_manager->mp, size); + res = mono_mempool_alloc0 (memory_manager->_mp, size); + alloc_unlock (memory_manager); + + return res; } char* @@ -275,10 +273,21 @@ mono_mem_manager_strdup (MonoMemoryManager *memory_manager, const char *s) { char *res; - mono_mem_manager_lock (memory_manager); - res = mono_mempool_strdup (memory_manager->mp, s); - mono_mem_manager_unlock (memory_manager); + alloc_lock (memory_manager); + res = mono_mempool_strdup (memory_manager->_mp, s); + alloc_unlock (memory_manager); + + return res; +} + +gboolean +mono_mem_manager_mp_contains_addr (MonoMemoryManager *memory_manager, gpointer addr) +{ + gboolean res; + alloc_lock (memory_manager); + res = mono_mempool_contains_addr (memory_manager->_mp, addr); + alloc_unlock (memory_manager); return res; } diff --git a/src/mono/mono/metadata/object.c b/src/mono/mono/metadata/object.c index 0618631160b1..f90bb34e93cb 100644 --- a/src/mono/mono/metadata/object.c +++ b/src/mono/mono/metadata/object.c @@ -1468,7 +1468,7 @@ static MonoImtBuilderEntry* get_generic_virtual_entries (MonoMemoryManager *mem_manager, gpointer *vtable_slot); /* - * LOCKING: requires the loader and domain locks. + * LOCKING: assume the loader lock is held * */ static void @@ -1626,7 +1626,9 @@ build_imt (MonoClass *klass, MonoVTable *vt, gpointer* imt, GSList *extra_interf { MONO_REQ_GC_NEUTRAL_MODE; + mono_loader_lock (); build_imt_slots (klass, vt, imt, extra_interfaces, -1); + mono_loader_unlock (); } /** @@ -1636,7 +1638,7 @@ build_imt (MonoClass *klass, MonoVTable *vt, gpointer* imt, GSList *extra_interf * Fill the given \p imt_slot in the IMT table of \p vtable with * a trampoline or a trampoline for the case of collisions. * This is part of the internal mono API. - * LOCKING: Take the domain lock. + * LOCKING: Take the loader lock. */ void mono_vtable_build_imt_slot (MonoVTable* vtable, int imt_slot) @@ -1652,12 +1654,10 @@ mono_vtable_build_imt_slot (MonoVTable* vtable, int imt_slot) * Update and heck needs to ahppen inside the proper domain lock, as all * the changes made to a MonoVTable. */ - mono_loader_lock (); /*FIXME build_imt_slots requires the loader lock.*/ - mono_domain_lock (vtable->domain); + mono_loader_lock (); /* we change the slot only if it wasn't changed from the generic imt trampoline already */ if (!callbacks.imt_entry_inited (vtable, imt_slot)) build_imt_slots (vtable->klass, vtable, imt, NULL, imt_slot); - mono_domain_unlock (vtable->domain); mono_loader_unlock (); } @@ -1684,9 +1684,8 @@ get_generic_virtual_entries (MonoMemoryManager *mem_manager, gpointer *vtable_sl GenericVirtualCase *list; MonoImtBuilderEntry *entries; - MonoDomain *domain = mono_get_root_domain (); - mono_domain_lock (domain); + mono_mem_manager_lock (mem_manager); if (!mem_manager->generic_virtual_cases) mem_manager->generic_virtual_cases = g_hash_table_new (mono_aligned_addr_hash, NULL); @@ -1709,7 +1708,7 @@ get_generic_virtual_entries (MonoMemoryManager *mem_manager, gpointer *vtable_sl entries = entry; } - mono_domain_unlock (domain); + mono_mem_manager_unlock (mem_manager); /* FIXME: Leaking memory ? */ return entries; @@ -1740,10 +1739,11 @@ mono_method_add_generic_virtual_invocation (MonoVTable *vtable, GenericVirtualCase *gvc, *list; MonoImtBuilderEntry *entries; GPtrArray *sorted; - MonoDomain *domain = mono_get_root_domain (); MonoMemoryManager *mem_manager = m_class_get_mem_manager (vtable->klass); - mono_domain_lock (domain); + mono_loader_lock (); + + mono_mem_manager_lock (mem_manager); if (!mem_manager->generic_virtual_cases) mem_manager->generic_virtual_cases = g_hash_table_new (mono_aligned_addr_hash, NULL); @@ -1775,6 +1775,8 @@ mono_method_add_generic_virtual_invocation (MonoVTable *vtable, num_added++; } + mono_mem_manager_unlock (mem_manager); + if (++gvc->count == THUNK_THRESHOLD) { gpointer *old_thunk = (void **)*vtable_slot; gpointer vtable_trampoline = NULL; @@ -1812,7 +1814,7 @@ mono_method_add_generic_virtual_invocation (MonoVTable *vtable, } } - mono_domain_unlock (domain); + mono_loader_unlock (); } static MonoVTable *mono_class_create_runtime_vtable (MonoClass *klass, MonoError *error); @@ -2173,7 +2175,6 @@ mono_class_create_runtime_vtable (MonoClass *klass, MonoError *error) MonoReflectionTypeHandle vt_type = mono_type_get_object_handle (m_class_get_byval_arg (klass), error); vt->type = MONO_HANDLE_RAW (vt_type); if (!is_ok (error)) { - mono_domain_unlock (domain); mono_loader_unlock (); MONO_PROFILER_RAISE (vtable_failed, (vt)); goto return_null; diff --git a/src/mono/mono/metadata/sre.c b/src/mono/mono/metadata/sre.c index c70b302f0c2a..aa7c53a4d697 100644 --- a/src/mono/mono/metadata/sre.c +++ b/src/mono/mono/metadata/sre.c @@ -3853,21 +3853,15 @@ ves_icall_TypeBuilder_create_runtime_class (MonoReflectionTypeBuilderHandle ref_ reflection_setup_internal_class (ref_tb, error); mono_error_assert_ok (error); - MonoDomain *domain = MONO_HANDLE_DOMAIN (ref_tb); MonoType *type = MONO_HANDLE_GETVAL (MONO_HANDLE_CAST (MonoReflectionType, ref_tb), type); MonoClass *klass = mono_class_from_mono_type_internal (type); MonoArrayHandle cattrs = MONO_HANDLE_NEW_GET (MonoArray, ref_tb, cattrs); mono_save_custom_attrs (klass->image, klass, MONO_HANDLE_RAW (cattrs)); /* FIXME use handles */ - /* - * we need to lock the domain because the lock will be taken inside - * So, we need to keep the locking order correct. - */ mono_loader_lock (); - mono_domain_lock (domain); + if (klass->wastypebuilder) { - mono_domain_unlock (domain); mono_loader_unlock (); return mono_type_get_object_handle (m_class_get_byval_arg (klass), error); @@ -3957,7 +3951,6 @@ ves_icall_TypeBuilder_create_runtime_class (MonoReflectionTypeBuilderHandle ref_ goto_if_nok (error, failure); } - mono_domain_unlock (domain); mono_loader_unlock (); if (klass->enumtype && !mono_class_is_valid_enum (klass)) { @@ -3975,7 +3968,6 @@ ves_icall_TypeBuilder_create_runtime_class (MonoReflectionTypeBuilderHandle ref_ failure: mono_class_set_type_load_failure (klass, "TypeBuilder could not create runtime class due to: %s", mono_error_get_message (error)); klass->wastypebuilder = TRUE; - mono_domain_unlock (domain); mono_loader_unlock (); failure_unlocked: return MONO_HANDLE_CAST (MonoReflectionType, NULL_HANDLE); diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 1fa5e2660d54..daaae50705b4 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -632,6 +632,25 @@ typedef struct { InterpMethod *target_imethod; } InterpVTableEntry; +static inline GSList* +g_slist_append_node (GSList *list, GSList *new_list, gpointer data) +{ + GSList *last; + + new_list->data = data; + new_list->next = NULL; + + if (list) { + last = list; + while (last->next) + last = last->next; + last->next = new_list; + + return list; + } else + return new_list; +} + /* memory manager lock must be held */ static GSList* append_imethod (MonoMemoryManager *memory_manager, GSList *list, InterpMethod *imethod, InterpMethod *target_imethod) @@ -639,10 +658,11 @@ append_imethod (MonoMemoryManager *memory_manager, GSList *list, InterpMethod *i GSList *ret; InterpVTableEntry *entry; - entry = (InterpVTableEntry*) mono_mem_manager_alloc_nolock (memory_manager, sizeof (InterpVTableEntry)); + entry = (InterpVTableEntry*) mono_mem_manager_alloc0 (memory_manager, sizeof (InterpVTableEntry)); entry->imethod = imethod; entry->target_imethod = target_imethod; - ret = g_slist_append_mempool (memory_manager->mp, list, entry); + ret = mono_mem_manager_alloc0 (memory_manager, sizeof (GSList)); + ret = g_slist_append_node (list, ret, entry); return ret; } diff --git a/src/mono/mono/mini/mini-generic-sharing.c b/src/mono/mono/mini/mini-generic-sharing.c index 513df686b72f..6661dd6576d3 100644 --- a/src/mono/mono/mini/mini-generic-sharing.c +++ b/src/mono/mono/mini/mini-generic-sharing.c @@ -2901,7 +2901,6 @@ fill_runtime_generic_context (MonoVTable *class_vtable, MonoRuntimeGenericContex { gpointer info; int i, first_slot, size; - MonoDomain *domain = mono_get_root_domain (); MonoClass *klass = class_vtable->klass; MonoGenericContext *class_context; MonoRuntimeGenericContextInfoTemplate oti; @@ -2963,7 +2962,7 @@ fill_runtime_generic_context (MonoVTable *class_vtable, MonoRuntimeGenericContex class_context = mono_class_is_ginst (klass) ? &mono_class_get_generic_class (klass)->context : NULL; MonoGenericContext context = { class_context ? class_context->class_inst : NULL, method_inst }; - mono_domain_lock (domain); + mono_mem_manager_lock (jit_mm->mem_manager); /* First check whether that slot isn't already instantiated. This might happen because lookup doesn't lock. Allocate @@ -2984,7 +2983,7 @@ fill_runtime_generic_context (MonoVTable *class_vtable, MonoRuntimeGenericContex rgctx_index = slot - first_slot + 1 + offset; info = (MonoRuntimeGenericContext*)rgctx [rgctx_index]; if (info) { - mono_domain_unlock (domain); + mono_mem_manager_unlock (jit_mm->mem_manager); return info; } break; @@ -3002,7 +3001,7 @@ fill_runtime_generic_context (MonoVTable *class_vtable, MonoRuntimeGenericContex g_assert (!rgctx [rgctx_index]); - mono_domain_unlock (domain); + mono_mem_manager_unlock (jit_mm->mem_manager); oti = class_get_rgctx_template_oti (get_shared_class (klass), method_inst ? method_inst->type_argc : 0, slot, TRUE, TRUE, &do_free); @@ -3017,7 +3016,7 @@ fill_runtime_generic_context (MonoVTable *class_vtable, MonoRuntimeGenericContex */ /*FIXME We should use CAS here, no need to take a lock.*/ - mono_domain_lock (domain); + mono_mem_manager_lock (jit_mm->mem_manager); /* Check whether the slot hasn't been instantiated in the meantime. */ @@ -3029,7 +3028,7 @@ fill_runtime_generic_context (MonoVTable *class_vtable, MonoRuntimeGenericContex rgctx [rgctx_index] = info; } - mono_domain_unlock (domain); + mono_mem_manager_unlock (jit_mm->mem_manager); if (do_free) free_inflated_info (oti.info_type, oti.data); diff --git a/src/mono/mono/mini/mini-runtime.c b/src/mono/mono/mini/mini-runtime.c index 6af9844f7f83..48b3f766bba5 100644 --- a/src/mono/mono/mini/mini-runtime.c +++ b/src/mono/mono/mini/mini-runtime.c @@ -3267,22 +3267,19 @@ mono_jit_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObjec *exc = NULL; #ifdef MONO_ARCH_DYN_CALL_SUPPORTED - MonoDomain *domain = mono_get_root_domain (); static RuntimeInvokeDynamicFunction dyn_runtime_invoke = NULL; if (info->dyn_call_info) { if (!dyn_runtime_invoke) { - mono_domain_lock (domain); - invoke = mono_marshal_get_runtime_invoke_dynamic (); - dyn_runtime_invoke = (RuntimeInvokeDynamicFunction)mono_jit_compile_method_jit_only (invoke, error); + RuntimeInvokeDynamicFunction invoke_func = (RuntimeInvokeDynamicFunction)mono_jit_compile_method_jit_only (invoke, error); + mono_memory_barrier (); + dyn_runtime_invoke = invoke_func; if (!dyn_runtime_invoke && mono_use_interpreter) { info->use_interp = TRUE; info->dyn_call_info = NULL; } else if (!is_ok (error)) { - mono_domain_unlock (domain); return NULL; } - mono_domain_unlock (domain); } } if (info->dyn_call_info) { diff --git a/src/mono/mono/mini/mini.c b/src/mono/mono/mini/mini.c index c188f36367e4..193784601876 100644 --- a/src/mono/mono/mini/mini.c +++ b/src/mono/mono/mini/mini.c @@ -3800,10 +3800,7 @@ mini_method_compile (MonoMethod *method, guint32 opts, JitFlags flags, int parts } if (!cfg->compile_aot && !(flags & JIT_FLAG_DISCARD_RESULTS)) { - MonoDomain *domain = mono_get_root_domain (); - mono_domain_lock (domain); mono_jit_info_table_add (cfg->jit_info); - mono_domain_unlock (domain); if (cfg->method->dynamic) { MonoJitMemoryManager *jit_mm = (MonoJitMemoryManager*)cfg->jit_mm; @@ -3963,7 +3960,6 @@ mono_jit_compile_method_inner (MonoMethod *method, int opt, MonoError *error) MonoException *ex = NULL; gint64 start; MonoMethod *prof_method, *shared; - MonoDomain *target_domain = mono_get_root_domain (); error_init (error); @@ -4034,7 +4030,7 @@ mono_jit_compile_method_inner (MonoMethod *method, int opt, MonoError *error) shared = NULL; } - mono_domain_lock (target_domain); + mono_loader_lock (); if (mono_stats_method_desc && mono_method_desc_full_match (mono_stats_method_desc, method)) { g_printf ("Printing runtime stats at method: %s\n", mono_method_get_full_name (method)); @@ -4081,7 +4077,7 @@ mono_jit_compile_method_inner (MonoMethod *method, int opt, MonoError *error) mono_emit_jit_map (jinfo); mono_emit_jit_dump (jinfo, code); #endif - mono_domain_unlock (target_domain); + mono_loader_unlock (); if (!is_ok (error)) return NULL; From 6d817d9681d44c25421bd3907b0435785b87519c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Wed, 31 Mar 2021 01:42:47 +0200 Subject: [PATCH 54/98] Make CLOCK_MONOTONIC handling more precise in pal_threading.c (#50441) The root cause of the problem in https://github.com/dotnet/runtime/pull/50388 turned out to be because we do not have `pthread_condattr_setclock(..., CLOCK_MONOTONIC);` on iOS. This caused the timeout argument for `pthread_cond_timedwait` to be interpreted as an _absolute system time_ (in seconds since the Unix epoch), however the value we got back from `clock_gettime(CLOCK_MONOTONIC, ...)` was actually some value based on the uptime of the system. Since the uptime is much smaller than the system time the wait immediately returned. Harden the handling by adding a check for `HAVE_PTHREAD_CONDATTR_SETCLOCK` like we already do in `SystemNative_LowLevelMonitor_Create()` --- src/libraries/Native/Unix/System.Native/pal_threading.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Native/Unix/System.Native/pal_threading.c b/src/libraries/Native/Unix/System.Native/pal_threading.c index cf739c845427..6f69d1056b8f 100644 --- a/src/libraries/Native/Unix/System.Native/pal_threading.c +++ b/src/libraries/Native/Unix/System.Native/pal_threading.c @@ -173,7 +173,7 @@ int32_t SystemNative_LowLevelMonitor_TimedWait(LowLevelMonitor *monitor, int32_t error = pthread_cond_timedwait_relative_np(&monitor->Condition, &monitor->Mutex, &timeoutTimeSpec); #else -#if HAVE_CLOCK_MONOTONIC +#if HAVE_PTHREAD_CONDATTR_SETCLOCK && HAVE_CLOCK_MONOTONIC error = clock_gettime(CLOCK_MONOTONIC, &timeoutTimeSpec); assert(error == 0); #else From 289757c6b58c19831b726436d0e0eed78ff07d63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Rylek?= Date: Tue, 30 Mar 2021 16:44:40 -0700 Subject: [PATCH 55/98] Fix handling of array constructors in Crossgen2 (#48557) Fixes: https://github.com/dotnet/runtime/issues/48204 --- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 40 ++++++++++++++----- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 1ec3adb34e70..cd262716689e 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -1402,7 +1402,7 @@ private void ceeInfoGetCallInfo( // Its basic meaning is that shared generic methods always need instantiating // stubs as the shared generic code needs the method dictionary parameter that cannot // be provided by other means. - useInstantiatingStub = originalMethod.GetCanonMethodTarget(CanonicalFormKind.Specific).RequiresInstMethodDescArg(); + useInstantiatingStub = originalMethod.OwningType.IsArray || originalMethod.GetCanonMethodTarget(CanonicalFormKind.Specific).RequiresInstMethodDescArg(); callerMethod = HandleToObject(callerHandle); @@ -1602,7 +1602,8 @@ private void ceeInfoGetCallInfo( } methodToCall = targetMethod; - MethodDesc canonMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); + bool isArrayConstructor = targetMethod.OwningType.IsArray && targetMethod.IsConstructor; + MethodDesc canonMethod = (isArrayConstructor ? null : targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific)); if (directCall) { @@ -1616,7 +1617,7 @@ private void ceeInfoGetCallInfo( bool allowInstParam = (flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_ALLOWINSTPARAM) != 0; - if (!allowInstParam && canonMethod.RequiresInstArg()) + if (!allowInstParam && canonMethod != null && canonMethod.RequiresInstArg()) { useInstantiatingStub = true; } @@ -1640,7 +1641,17 @@ private void ceeInfoGetCallInfo( const CORINFO_CALLINFO_FLAGS LdVirtFtnMask = CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_LDFTN | CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_CALLVIRT; bool unresolvedLdVirtFtn = ((flags & LdVirtFtnMask) == LdVirtFtnMask) && !resolvedCallVirt; - if ((pResult->exactContextNeedsRuntimeLookup && useInstantiatingStub && (!allowInstParam || resolvedConstraint)) || forceUseRuntimeLookup) + if (isArrayConstructor) + { + // Constructors on arrays are special and don't actually have entrypoints. + // That would be fine by itself and wouldn't need special casing. But + // constructors on SzArray have a weird property that causes them not to have canonical forms. + // int[][] has a .ctor(int32,int32) to construct the jagged array in one go, but its canonical + // form of __Canon[] doesn't have the two-parameter constructor. The canonical form would need + // to have an unlimited number of constructors to cover stuff like "int[][][][][][]..." + pResult->kind = CORINFO_CALL_KIND.CORINFO_CALL; + } + else if ((pResult->exactContextNeedsRuntimeLookup && useInstantiatingStub && (!allowInstParam || resolvedConstraint)) || forceUseRuntimeLookup) { if (unresolvedLdVirtFtn) { @@ -1665,7 +1676,7 @@ private void ceeInfoGetCallInfo( if (allowInstParam) { useInstantiatingStub = false; - methodToCall = canonMethod; + methodToCall = canonMethod ?? methodToCall; } pResult->kind = CORINFO_CALL_KIND.CORINFO_CALL; @@ -1892,12 +1903,19 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO nonUnboxingMethod = rawPinvoke.Target; } - // READYTORUN: FUTURE: Direct calls if possible - pResult->codePointerOrStubLookup.constLookup = CreateConstLookupToSymbol( - _compilation.NodeFactory.MethodEntrypoint( - ComputeMethodWithToken(nonUnboxingMethod, ref pResolvedToken, constrainedType, unboxing: isUnboxingStub), - isInstantiatingStub: useInstantiatingStub, - isPrecodeImportRequired: (flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_LDFTN) != 0)); + if (methodToCall.OwningType.IsArray && methodToCall.IsConstructor) + { + pResult->codePointerOrStubLookup.constLookup = default; + } + else + { + // READYTORUN: FUTURE: Direct calls if possible + pResult->codePointerOrStubLookup.constLookup = CreateConstLookupToSymbol( + _compilation.NodeFactory.MethodEntrypoint( + ComputeMethodWithToken(nonUnboxingMethod, ref pResolvedToken, constrainedType, unboxing: isUnboxingStub), + isInstantiatingStub: useInstantiatingStub, + isPrecodeImportRequired: (flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_LDFTN) != 0)); + } // If the abi of the method isn't stable, this will cause a usage of the RequiresRuntimeJitSymbol, which will trigger a RequiresRuntimeJitException UpdateConstLookupWithRequiresRuntimeJitSymbolIfNeeded(ref pResult->codePointerOrStubLookup.constLookup, targetMethod); From 082af357d39f9df590fb4b123bcb6c21b0dd4369 Mon Sep 17 00:00:00 2001 From: David Pine Date: Tue, 30 Mar 2021 19:20:57 -0500 Subject: [PATCH 56/98] Added the `ConfigureDefaults` API, as an extension method of `IHostBuilder` (#50447) * Added the ConfigureDefaults API, as an extension method on the IHostBuilder interface. Fix #36003 * Apply suggestions from code review Co-authored-by: Eric Erhardt * Update src/libraries/Microsoft.Extensions.Hosting/src/Host.cs Co-authored-by: Eric Erhardt --- .../ref/Microsoft.Extensions.Hosting.cs | 1 + .../Microsoft.Extensions.Hosting/src/Host.cs | 81 +------------- .../src/HostingHostBuilderExtensions.cs | 105 +++++++++++++++++- .../tests/UnitTests/HostBuilderTests.cs | 17 ++- 4 files changed, 122 insertions(+), 82 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.cs b/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.cs index 63c5913808ed..1f92e812f2fc 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.cs @@ -39,6 +39,7 @@ public static partial class HostingHostBuilderExtensions { public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureAppConfiguration(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureDelegate) { throw null; } public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureContainer(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureDelegate) { throw null; } + public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureDefaults(this Microsoft.Extensions.Hosting.IHostBuilder builder, string[] args) { throw null; } public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureLogging(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureLogging) { throw null; } public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureLogging(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureLogging) { throw null; } public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureServices(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureDelegate) { throw null; } diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/Host.cs b/src/libraries/Microsoft.Extensions.Hosting/src/Host.cs index 7859c873f8dc..874abe23e400 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/Host.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/Host.cs @@ -2,11 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.IO; -using System.Reflection; -using System.Runtime.InteropServices; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.EventLog; namespace Microsoft.Extensions.Hosting { @@ -55,82 +52,8 @@ public static IHostBuilder CreateDefaultBuilder() => /// The initialized . public static IHostBuilder CreateDefaultBuilder(string[] args) { - var builder = new HostBuilder(); - - builder.UseContentRoot(Directory.GetCurrentDirectory()); - builder.ConfigureHostConfiguration(config => - { - config.AddEnvironmentVariables(prefix: "DOTNET_"); - if (args != null) - { - config.AddCommandLine(args); - } - }); - - builder.ConfigureAppConfiguration((hostingContext, config) => - { - IHostEnvironment env = hostingContext.HostingEnvironment; - - bool reloadOnChange = hostingContext.Configuration.GetValue("hostBuilder:reloadConfigOnChange", defaultValue: true); - - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: reloadOnChange) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: reloadOnChange); - - if (env.IsDevelopment() && !string.IsNullOrEmpty(env.ApplicationName)) - { - var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName)); - if (appAssembly != null) - { - config.AddUserSecrets(appAssembly, optional: true, reloadOnChange: reloadOnChange); - } - } - - config.AddEnvironmentVariables(); - - if (args != null) - { - config.AddCommandLine(args); - } - }) - .ConfigureLogging((hostingContext, logging) => - { - bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - - // IMPORTANT: This needs to be added *before* configuration is loaded, this lets - // the defaults be overridden by the configuration. - if (isWindows) - { - // Default the EventLogLoggerProvider to warning or above - logging.AddFilter(level => level >= LogLevel.Warning); - } - - logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); - logging.AddConsole(); - logging.AddDebug(); - logging.AddEventSourceLogger(); - - if (isWindows) - { - // Add the EventLogLoggerProvider on windows machines - logging.AddEventLog(); - } - - logging.Configure(options => - { - options.ActivityTrackingOptions = ActivityTrackingOptions.SpanId - | ActivityTrackingOptions.TraceId - | ActivityTrackingOptions.ParentId; - }); - - }) - .UseDefaultServiceProvider((context, options) => - { - bool isDevelopment = context.HostingEnvironment.IsDevelopment(); - options.ValidateScopes = isDevelopment; - options.ValidateOnBuild = isDevelopment; - }); - - return builder; + HostBuilder builder = new(); + return builder.ConfigureDefaults(args); } } } diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs b/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs index ca7e84abff8a..28e32a4e92e4 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs @@ -3,12 +3,16 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting.Internal; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.EventLog; namespace Microsoft.Extensions.Hosting { @@ -45,7 +49,7 @@ public static IHostBuilder UseContentRoot(this IHostBuilder hostBuilder, string configBuilder.AddInMemoryCollection(new[] { new KeyValuePair(HostDefaults.ContentRootKey, - contentRoot ?? throw new ArgumentNullException(nameof(contentRoot))) + contentRoot ?? throw new ArgumentNullException(nameof(contentRoot))) }); }); } @@ -134,6 +138,105 @@ public static IHostBuilder ConfigureContainer(this IHostBuild return hostBuilder.ConfigureContainer((context, builder) => configureDelegate(builder)); } + /// + /// Configures an existing instance with pre-configured defaults. + /// + /// + /// The following defaults are applied to the : + /// + /// set the to the result of + /// load host from "DOTNET_" prefixed environment variables + /// load host from supplied command line args + /// load app from 'appsettings.json' and 'appsettings.[].json' + /// load app from User Secrets when is 'Development' using the entry assembly + /// load app from environment variables + /// load app from supplied command line args + /// configure the to log to the console, debug, and event source output + /// enables scope validation on the dependency injection container when is 'Development' + /// + /// + /// The existing builder to configure. + /// The command line args. + /// The same instance of the for chaining. + public static IHostBuilder ConfigureDefaults(this IHostBuilder builder, string[] args) + { + builder.UseContentRoot(Directory.GetCurrentDirectory()); + builder.ConfigureHostConfiguration(config => + { + config.AddEnvironmentVariables(prefix: "DOTNET_"); + if (args is { Length: > 0 }) + { + config.AddCommandLine(args); + } + }); + + builder.ConfigureAppConfiguration((hostingContext, config) => + { + IHostEnvironment env = hostingContext.HostingEnvironment; + + bool reloadOnChange = hostingContext.Configuration.GetValue("hostBuilder:reloadConfigOnChange", defaultValue: true); + + config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: reloadOnChange) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: reloadOnChange); + + if (env.IsDevelopment() && env.ApplicationName is { Length: > 0 }) + { + var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName)); + if (appAssembly is not null) + { + config.AddUserSecrets(appAssembly, optional: true, reloadOnChange: reloadOnChange); + } + } + + config.AddEnvironmentVariables(); + + if (args is { Length: > 0 }) + { + config.AddCommandLine(args); + } + }) + .ConfigureLogging((hostingContext, logging) => + { + bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + + // IMPORTANT: This needs to be added *before* configuration is loaded, this lets + // the defaults be overridden by the configuration. + if (isWindows) + { + // Default the EventLogLoggerProvider to warning or above + logging.AddFilter(level => level >= LogLevel.Warning); + } + + logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); + logging.AddConsole(); + logging.AddDebug(); + logging.AddEventSourceLogger(); + + if (isWindows) + { + // Add the EventLogLoggerProvider on windows machines + logging.AddEventLog(); + } + + logging.Configure(options => + { + options.ActivityTrackingOptions = + ActivityTrackingOptions.SpanId | + ActivityTrackingOptions.TraceId | + ActivityTrackingOptions.ParentId; + }); + + }) + .UseDefaultServiceProvider((context, options) => + { + bool isDevelopment = context.HostingEnvironment.IsDevelopment(); + options.ValidateScopes = isDevelopment; + options.ValidateOnBuild = isDevelopment; + }); + + return builder; + } + /// /// Listens for Ctrl+C or SIGTERM and calls to start the shutdown process. /// This will unblock extensions like RunAsync and WaitForShutdownAsync. diff --git a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/HostBuilderTests.cs b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/HostBuilderTests.cs index d1e61255c061..df2d42a574c4 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/HostBuilderTests.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/HostBuilderTests.cs @@ -559,10 +559,23 @@ public void HostServicesSameServiceProviderAsInHostBuilder() Assert.Same(appServicesFromHostBuilder, host.Services); } + [Fact] + public void HostBuilderConfigureDefaultsInterleavesMissingConfigValues() + { + IHostBuilder hostBuilder = new HostBuilder(); + hostBuilder.ConfigureDefaults(args: null); + + using var host = hostBuilder.Build(); + var env = host.Services.GetRequiredService(); + + var expectedContentRootPath = Directory.GetCurrentDirectory(); + Assert.Equal(expectedContentRootPath, env.ContentRootPath); + } + private class FakeFileProvider : IFileProvider, IDisposable { - public bool Disposed { get; set; } - public void Dispose() { Disposed = true; } + public bool Disposed { get; private set; } + public void Dispose() => Disposed = true; public IDirectoryContents GetDirectoryContents(string subpath) => throw new NotImplementedException(); public IFileInfo GetFileInfo(string subpath) => throw new NotImplementedException(); public IChangeToken Watch(string filter) => throw new NotImplementedException(); From 99d6215ef2336b8fa51fe19b2e8e782df62959df Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 30 Mar 2021 20:51:24 -0700 Subject: [PATCH 57/98] Merge REGUTIL logic for environment variables into CLRConfig (#50393) * Merge REGUTIL logic for environment variables into CLRConfig Remove REGUTIL Remove unused CLR configuration options --- src/coreclr/inc/clrconfig.h | 76 +-- src/coreclr/inc/clrconfigvalues.h | 367 ++++++------ src/coreclr/inc/corhdr.h | 12 - src/coreclr/inc/utilcode.h | 134 +---- src/coreclr/md/debug_metadata.h | 2 +- src/coreclr/utilcode/CMakeLists.txt | 1 - src/coreclr/utilcode/clrconfig.cpp | 503 ++++++++++++---- src/coreclr/utilcode/regutil.cpp | 759 ------------------------- src/coreclr/vm/ceemain.cpp | 8 +- src/coreclr/vm/comcallablewrapper.cpp | 5 - src/coreclr/vm/compatibilityswitch.cpp | 2 +- src/coreclr/vm/gcenv.ee.cpp | 6 +- src/coreclr/vm/jithost.cpp | 4 +- 13 files changed, 596 insertions(+), 1283 deletions(-) delete mode 100644 src/coreclr/utilcode/regutil.cpp diff --git a/src/coreclr/inc/clrconfig.h b/src/coreclr/inc/clrconfig.h index 715a5ada9f24..842313df2408 100644 --- a/src/coreclr/inc/clrconfig.h +++ b/src/coreclr/inc/clrconfig.h @@ -6,13 +6,8 @@ // // -// Unified method of accessing configuration values from environment variables, registry and config file(s). -// This class replaces all GetConfigDWORD and GetConfigString methods in EEConfig and REGUTIL. To define a -// flag, add an entry in the table in file:CLRConfigValues.h. -// -// -// -// +// Unified method of accessing configuration values. +// To define a flag, add an entry in the table in file:CLRConfigValues.h. // -------------------------------------------------------------------------------------------------- @@ -25,28 +20,18 @@ class CLRConfig { public: - // - // Types - // + // Setting each option results in some change to the config value. + enum class LookupOptions + { + // Default options. + Default = 0, - // Setting each option results in some change to the config value lookup method. Default behavior is (in - // the following order): - // * Look at environment variables (prepending COMPlus to the name) - // * Look at the framework registry keys (HKCU\Software\Microsoft\.NETFramework then - // HKLM\Software\Microsoft\.NETFramework) - // * Look at the available config files (system, application, host and user). For details see TODO: - // Link to BOTR documentation - enum LookupOptions { - // If set, don't look in environment variables. - IgnoreEnv = 0x1, // If set, do not prepend "COMPlus_" when doing environment variable lookup. - DontPrependCOMPlus_ = 0x2, + DontPrependCOMPlus_ = 0x1, + // Remove any whitespace at beginning and end of value. (Only applicable for // *string* configuration values.) - TrimWhiteSpaceFromStringValue = 0x100, - - // Legacy EEConfig-style lookup. - EEConfig_default = 0, + TrimWhiteSpaceFromStringValue = 0x2, }; // Struct used to store information about where/how to find a Config DWORD. @@ -100,6 +85,7 @@ class CLRConfig #define CONFIG_STRING_INFO(symbol, name, description) #define CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions) #endif // _DEBUG + // Now that we have defined what what the macros in file:CLRConfigValues.h mean, include it to generate the code. #include "clrconfigvalues.h" @@ -144,39 +130,10 @@ class CLRConfig static BOOL IsConfigOptionSpecified(LPCWSTR name); // Free a string returned by GetConfigValue - static void FreeConfigString(__in __in_z LPWSTR name); - -private: - - // Helper method to translate LookupOptions to REGUTIL::CORConfigLevel - static REGUTIL::CORConfigLevel GetConfigLevel(LookupOptions options); + static void FreeConfigString(__in __in_z LPWSTR name); - // - // Helper methods. - // - - // Helper method to check if a certain option is set in a ConfigDWORDInfo struct. - static inline BOOL CheckLookupOption(const ConfigDWORDInfo & info, LookupOptions option) - { - LIMITED_METHOD_CONTRACT; - return ((info.options & option) == option) ? TRUE : FALSE; - } - - // Helper method to check if a certain option is set in a ConfigStringInfo struct. - static inline BOOL CheckLookupOption(const ConfigStringInfo & info, LookupOptions option) - { - LIMITED_METHOD_CONTRACT; - return ((info.options & option) == option) ? TRUE : FALSE; - } - - // Helper method to check if a certain option is set in an options enum. - static inline BOOL CheckLookupOption(LookupOptions infoOptions, LookupOptions optionToCheck) - { - LIMITED_METHOD_CONTRACT; - return ((infoOptions & optionToCheck) == optionToCheck) ? TRUE : FALSE; - } - - static HRESULT TrimWhiteSpace(LPCWSTR wszOrig, __deref_out_z LPWSTR * pwszTrimmed); + // Populate the caches with current state to improve lookup times. + static void InitCache(); }; inline CLRConfig::LookupOptions operator|(CLRConfig::LookupOptions lhs, CLRConfig::LookupOptions rhs) @@ -184,6 +141,11 @@ inline CLRConfig::LookupOptions operator|(CLRConfig::LookupOptions lhs, CLRConfi return static_cast(static_cast(lhs) | static_cast(rhs)); } +inline CLRConfig::LookupOptions operator&(CLRConfig::LookupOptions lhs, CLRConfig::LookupOptions rhs) +{ + return static_cast(static_cast(lhs) & static_cast(rhs)); +} + typedef Wrapper CLRConfigStringHolder; #endif //__CLRConfig_h__ diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index a3acc7ed03a1..46db05a29358 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -43,7 +43,7 @@ // CONFIG_DWORD_INFO(symbol, name, defaultValue, description) // -------------------------------------------------------------------------- // Use this macro to define a basic DWORD value. CLRConfig will look in environment variables (adding -// COMPlus_ to the name), the registry (HKLM and HKCU), and all the config files for this value. To customize +// COMPlus_ to the name) for this value. To customize // where CLRConfig looks, use the extended version of the macro below. IMPORTANT: please follow the // code:#NamingConventions for the symbol and the name! // @@ -56,7 +56,7 @@ // of options and their descriptions, see code:CLRConfig.LookupOptions // // Example: CONFIG_DWORD_INFO_EX(INTERNAL_EnableInternetHREFexes, W("EnableInternetHREFexes"), 0, "", -// (CLRConfig::LookupOptions) (CLRConfig::IgnoreEnv | CLRConfig::IgnoreHKCU)) +// (CLRConfig::LookupOptions) (CLRConfig::LookupOptions::DontPrependCOMPlus_)) // // #Strings: // -------------------------------------------------------------------------- @@ -112,11 +112,6 @@ /// /// AppDomain /// -CONFIG_DWORD_INFO(INTERNAL_ADDumpSB, W("ADDumpSB"), 0, "Not used") -CONFIG_DWORD_INFO(INTERNAL_ADForceSB, W("ADForceSB"), 0, "Forces sync block creation for all objects") -CONFIG_DWORD_INFO(INTERNAL_ADLogMemory, W("ADLogMemory"), 0, "Superseded by test hooks") -CONFIG_DWORD_INFO(INTERNAL_ADTakeDHSnapShot, W("ADTakeDHSnapShot"), 0, "Superseded by test hooks") -CONFIG_DWORD_INFO(INTERNAL_ADTakeSnapShot, W("ADTakeSnapShot"), 0, "Superseded by test hooks") CONFIG_DWORD_INFO(INTERNAL_EnableFullDebug, W("EnableFullDebug"), 0, "Heavy-weight checking for AD boundary violations (AD leaks)") /// @@ -133,103 +128,102 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_JitPitchMaxVal, W("JitPitchMaxVal"), (DWORD)0x /// /// Assembly Loader /// -CONFIG_DWORD_INFO_EX(INTERNAL_GetAssemblyIfLoadedIgnoreRidMap, W("GetAssemblyIfLoadedIgnoreRidMap"), 0, "Used to force loader to ignore assemblies cached in the rid-map", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_GetAssemblyIfLoadedIgnoreRidMap, W("GetAssemblyIfLoadedIgnoreRidMap"), 0, "Used to force loader to ignore assemblies cached in the rid-map") /// /// Conditional breakpoints /// -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_BreakOnBadExit, W("BreakOnBadExit"), 0, "", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_BreakOnBadExit, W("BreakOnBadExit"), 0, "") CONFIG_STRING_INFO(INTERNAL_BreakOnClassBuild, W("BreakOnClassBuild"), "Very useful for debugging class layout code.") CONFIG_STRING_INFO(INTERNAL_BreakOnClassLoad, W("BreakOnClassLoad"), "Very useful for debugging class loading code.") CONFIG_STRING_INFO(INTERNAL_BreakOnComToClrNativeInfoInit, W("BreakOnComToClrNativeInfoInit"), "Throws an assert when native information about a COM -> CLR call are about to be gathered.") -CONFIG_DWORD_INFO_EX(INTERNAL_BreakOnDebugBreak, W("BreakOnDebugBreak"), 0, "Allows an assert in debug builds when a user break is hit", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_BreakOnDILoad, W("BreakOnDILoad"), 0, "Allows an assert when the DI is loaded", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_BreakOnDumpToken, W("BreakOnDumpToken"), 0xffffffff, "Breaks when using internal logging on a particular token value.", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_BreakOnEELoad, W("BreakOnEELoad"), 0, "", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_BreakOnDebugBreak, W("BreakOnDebugBreak"), 0, "Allows an assert in debug builds when a user break is hit") +CONFIG_DWORD_INFO(INTERNAL_BreakOnDILoad, W("BreakOnDILoad"), 0, "Allows an assert when the DI is loaded") +CONFIG_DWORD_INFO(INTERNAL_BreakOnDumpToken, W("BreakOnDumpToken"), 0xffffffff, "Breaks when using internal logging on a particular token value.") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_BreakOnEELoad, W("BreakOnEELoad"), 0, "") CONFIG_DWORD_INFO(INTERNAL_BreakOnEEShutdown, W("BreakOnEEShutdown"), 0, "") -CONFIG_DWORD_INFO_EX(INTERNAL_BreakOnExceptionInGetThrowable, W("BreakOnExceptionInGetThrowable"), 0, "", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_BreakOnExceptionInGetThrowable, W("BreakOnExceptionInGetThrowable"), 0, "") CONFIG_DWORD_INFO(INTERNAL_BreakOnFindMethod, W("BreakOnFindMethod"), 0, "Breaks in findMethodInternal when it searches for the specified token.") -CONFIG_DWORD_INFO_EX(INTERNAL_BreakOnFirstPass, W("BreakOnFirstPass"), 0, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_BreakOnHR, W("BreakOnHR"), 0, "Debug.cpp, IfFailxxx use this macro to stop if hr matches ", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_BreakOnFirstPass, W("BreakOnFirstPass"), 0, "") +CONFIG_DWORD_INFO(INTERNAL_BreakOnHR, W("BreakOnHR"), 0, "Debug.cpp, IfFailxxx use this macro to stop if hr matches ") CONFIG_STRING_INFO(INTERNAL_BreakOnInstantiation, W("BreakOnInstantiation"), "Very useful for debugging generic class instantiation.") CONFIG_STRING_INFO(INTERNAL_BreakOnInteropStubSetup, W("BreakOnInteropStubSetup"), "Throws an assert when marshaling stub for the given method is about to be built.") -CONFIG_STRING_INFO_EX(INTERNAL_BreakOnInteropVTableBuild, W("BreakOnInteropVTableBuild"), "Specifies a type name for which an assert should be thrown when building interop v-table.", CLRConfig::EEConfig_default) +CONFIG_STRING_INFO(INTERNAL_BreakOnInteropVTableBuild, W("BreakOnInteropVTableBuild"), "Specifies a type name for which an assert should be thrown when building interop v-table.") CONFIG_STRING_INFO(INTERNAL_BreakOnMethodName, W("BreakOnMethodName"), "Very useful for debugging method override placement code.") -CONFIG_DWORD_INFO_EX(INTERNAL_BreakOnNotify, W("BreakOnNotify"), 0, "", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_BreakOnRetailAssert, W("BreakOnRetailAssert"), 0, "Used for debugging \"retail\" asserts (fatal errors)", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_BreakOnSecondPass, W("BreakOnSecondPass"), 0, "", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_BreakOnSO, W("BreakOnSO"), 0, "", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_BreakOnNotify, W("BreakOnNotify"), 0, "") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_BreakOnRetailAssert, W("BreakOnRetailAssert"), 0, "Used for debugging \"retail\" asserts (fatal errors)") +CONFIG_DWORD_INFO(INTERNAL_BreakOnSecondPass, W("BreakOnSecondPass"), 0, "") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_BreakOnSO, W("BreakOnSO"), 0, "") CONFIG_STRING_INFO(INTERNAL_BreakOnStructMarshalSetup, W("BreakOnStructMarshalSetup"), "Throws an assert when field marshalers for the given type with layout are about to be created.") -CONFIG_DWORD_INFO_EX(INTERNAL_BreakOnUEF, W("BreakOnUEF"), 0, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_BreakOnUncaughtException, W("BreakOnUncaughtException"), 0, "", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_BreakOnUEF, W("BreakOnUEF"), 0, "") +CONFIG_DWORD_INFO(INTERNAL_BreakOnUncaughtException, W("BreakOnUncaughtException"), 0, "") /// - /// Debugger /// -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_EnableDiagnostics, W("EnableDiagnostics"), 1, "Allows the debugger, profiler, and EventPipe diagnostics to be disabled", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_D__FCE, W("D::FCE"), 0, "Allows an assert when crawling the managed stack for an exception handler", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgBreakIfLocksUnavailable, W("DbgBreakIfLocksUnavailable"), 0, "Allows an assert when the debugger can't take a lock ", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgBreakOnErr, W("DbgBreakOnErr"), 0, "Allows an assert when we get a failing hresult", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgBreakOnMapPatchToDJI, W("DbgBreakOnMapPatchToDJI"), 0, "Allows an assert when mapping a patch to an address", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgBreakOnRawInt3, W("DbgBreakOnRawInt3"), 0, "Allows an assert for test coverage for debug break or other int3 breaks", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgBreakOnSendBreakpoint, W("DbgBreakOnSendBreakpoint"), 0, "Allows an assert when sending a breakpoint to the right side", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgBreakOnSetIP, W("DbgBreakOnSetIP"), 0, "Allows an assert when setting the IP", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgCheckInt3, W("DbgCheckInt3"), 0, "Asserts if the debugger explicitly writes int3 instead of calling SetUnmanagedBreakpoint", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableDiagnostics, W("EnableDiagnostics"), 1, "Allows the debugger, profiler, and EventPipe diagnostics to be disabled") +CONFIG_DWORD_INFO(INTERNAL_D__FCE, W("D::FCE"), 0, "Allows an assert when crawling the managed stack for an exception handler") +CONFIG_DWORD_INFO(INTERNAL_DbgBreakIfLocksUnavailable, W("DbgBreakIfLocksUnavailable"), 0, "Allows an assert when the debugger can't take a lock ") +CONFIG_DWORD_INFO(INTERNAL_DbgBreakOnErr, W("DbgBreakOnErr"), 0, "Allows an assert when we get a failing hresult") +CONFIG_DWORD_INFO(INTERNAL_DbgBreakOnMapPatchToDJI, W("DbgBreakOnMapPatchToDJI"), 0, "Allows an assert when mapping a patch to an address") +CONFIG_DWORD_INFO(INTERNAL_DbgBreakOnRawInt3, W("DbgBreakOnRawInt3"), 0, "Allows an assert for test coverage for debug break or other int3 breaks") +CONFIG_DWORD_INFO(INTERNAL_DbgBreakOnSendBreakpoint, W("DbgBreakOnSendBreakpoint"), 0, "Allows an assert when sending a breakpoint to the right side") +CONFIG_DWORD_INFO(INTERNAL_DbgBreakOnSetIP, W("DbgBreakOnSetIP"), 0, "Allows an assert when setting the IP") +CONFIG_DWORD_INFO(INTERNAL_DbgCheckInt3, W("DbgCheckInt3"), 0, "Asserts if the debugger explicitly writes int3 instead of calling SetUnmanagedBreakpoint") RETAIL_CONFIG_DWORD_INFO(INTERNAL_DbgForcePDBSymbols, W("DbgForcePDBSymbols"), 0, "") CONFIG_DWORD_INFO(INTERNAL_DbgDACAssertOnMismatch, W("DbgDACAssertOnMismatch"), 0, "Allows an assert when the mscordacwks and mscorwks dll versions don't match") -CONFIG_DWORD_INFO_EX(INTERNAL_DbgDACEnableAssert, W("DbgDACEnableAssert"), 0, "Enables extra validity checking in DAC - assumes target isn't corrupt", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_DbgDACSkipVerifyDlls, W("DbgDACSkipVerifyDlls"), 0, "Allows disabling the check to ensure mscordacwks and mscorwks dll versions match", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgDelayHelper, W("DbgDelayHelper"), 0, "Varies the wait in the helper thread startup for testing race between threads", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_DbgDisableDynamicSymsCompat, W("DbgDisableDynamicSymsCompat"), 0, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgDisableTargetConsistencyAsserts, W("DbgDisableTargetConsistencyAsserts"), 0, "Allows explicitly testing with corrupt targets", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_DbgEnableMixedModeDebugging, W("DbgEnableMixedModeDebuggingInternalOnly"), 0, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgExtraThreads, W("DbgExtraThreads"), 0, "Allows extra unmanaged threads to run and throw debug events for stress testing", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgExtraThreadsCantStop, W("DbgExtraThreadsCantStop"), 0, "Allows extra unmanaged threads in can't stop region to run and throw debug events for stress testing", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgExtraThreadsIB, W("DbgExtraThreadsIB"), 0, "Allows extra in-band unmanaged threads to run and throw debug events for stress testing", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgExtraThreadsOOB, W("DbgExtraThreadsOOB"), 0, "Allows extra out of band unmanaged threads to run and throw debug events for stress testing", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgFaultInHandleIPCEvent, W("DbgFaultInHandleIPCEvent"), 0, "Allows testing the unhandled event filter", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgInjectFEE, W("DbgInjectFEE"), 0, "Allows injecting a fatal execution error for testing Watson", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgLeakCheck, W("DbgLeakCheck"), 0, "Allows checking for leaked Cordb objects", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgNo2ndChance, W("DbgNo2ndChance"), 0, "Allows breaking on (and catching bogus) 2nd chance exceptions", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgNoDebugger, W("DbgNoDebugger"), 0, "Allows breaking if we don't want to lazily initialize the debugger", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_DbgNoForceContinue, W("DbgNoForceContinue"), 1, "Used to force a continue on longhorn", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgNoOpenMDByFile, W("DbgNoOpenMDByFile"), 0, "Allows opening MD by memory for perf testing", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_DbgDACEnableAssert, W("DbgDACEnableAssert"), 0, "Enables extra validity checking in DAC - assumes target isn't corrupt") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_DbgDACSkipVerifyDlls, W("DbgDACSkipVerifyDlls"), 0, "Allows disabling the check to ensure mscordacwks and mscorwks dll versions match") +CONFIG_DWORD_INFO(INTERNAL_DbgDelayHelper, W("DbgDelayHelper"), 0, "Varies the wait in the helper thread startup for testing race between threads") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_DbgDisableDynamicSymsCompat, W("DbgDisableDynamicSymsCompat"), 0, "") +CONFIG_DWORD_INFO(INTERNAL_DbgDisableTargetConsistencyAsserts, W("DbgDisableTargetConsistencyAsserts"), 0, "Allows explicitly testing with corrupt targets") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_DbgEnableMixedModeDebugging, W("DbgEnableMixedModeDebuggingInternalOnly"), 0, "") +CONFIG_DWORD_INFO(INTERNAL_DbgExtraThreads, W("DbgExtraThreads"), 0, "Allows extra unmanaged threads to run and throw debug events for stress testing") +CONFIG_DWORD_INFO(INTERNAL_DbgExtraThreadsCantStop, W("DbgExtraThreadsCantStop"), 0, "Allows extra unmanaged threads in can't stop region to run and throw debug events for stress testing") +CONFIG_DWORD_INFO(INTERNAL_DbgExtraThreadsIB, W("DbgExtraThreadsIB"), 0, "Allows extra in-band unmanaged threads to run and throw debug events for stress testing") +CONFIG_DWORD_INFO(INTERNAL_DbgExtraThreadsOOB, W("DbgExtraThreadsOOB"), 0, "Allows extra out of band unmanaged threads to run and throw debug events for stress testing") +CONFIG_DWORD_INFO(INTERNAL_DbgFaultInHandleIPCEvent, W("DbgFaultInHandleIPCEvent"), 0, "Allows testing the unhandled event filter") +CONFIG_DWORD_INFO(INTERNAL_DbgInjectFEE, W("DbgInjectFEE"), 0, "Allows injecting a fatal execution error for testing Watson") +CONFIG_DWORD_INFO(INTERNAL_DbgLeakCheck, W("DbgLeakCheck"), 0, "Allows checking for leaked Cordb objects") +CONFIG_DWORD_INFO(INTERNAL_DbgNo2ndChance, W("DbgNo2ndChance"), 0, "Allows breaking on (and catching bogus) 2nd chance exceptions") +CONFIG_DWORD_INFO(INTERNAL_DbgNoDebugger, W("DbgNoDebugger"), 0, "Allows breaking if we don't want to lazily initialize the debugger") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_DbgNoForceContinue, W("DbgNoForceContinue"), 1, "Used to force a continue on longhorn") +CONFIG_DWORD_INFO(INTERNAL_DbgNoOpenMDByFile, W("DbgNoOpenMDByFile"), 0, "Allows opening MD by memory for perf testing") CONFIG_DWORD_INFO(INTERNAL_DbgOOBinFEEE, W("DbgOOBinFEEE"), 0, "Allows forcing oob breakpoints when a fatal error occurs") -CONFIG_DWORD_INFO_EX(INTERNAL_DbgPingInterop, W("DbgPingInterop"), 0, "Allows checking for deadlocks in interop debugging", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgRace, W("DbgRace"), 0, "Allows pausing for native debug events to get hijicked", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_DbgRedirect, W("DbgRedirect"), 0, "Allows for redirecting the event pipeline", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_DbgPingInterop, W("DbgPingInterop"), 0, "Allows checking for deadlocks in interop debugging") +CONFIG_DWORD_INFO(INTERNAL_DbgRace, W("DbgRace"), 0, "Allows pausing for native debug events to get hijicked") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_DbgRedirect, W("DbgRedirect"), 0, "Allows for redirecting the event pipeline") RETAIL_CONFIG_STRING_INFO(EXTERNAL_DbgRedirectApplication, W("DbgRedirectApplication"), "Specifies the auxiliary debugger application to launch.") RETAIL_CONFIG_STRING_INFO(EXTERNAL_DbgRedirectAttachCmd, W("DbgRedirectAttachCmd"), "Specifies command parameters for attaching the auxiliary debugger.") RETAIL_CONFIG_STRING_INFO(EXTERNAL_DbgRedirectCommonCmd, W("DbgRedirectCommonCmd"), "Specifies a command line format string for the auxiliary debugger.") RETAIL_CONFIG_STRING_INFO(EXTERNAL_DbgRedirectCreateCmd, W("DbgRedirectCreateCmd"), "Specifies command parameters when creating the auxiliary debugger.") -CONFIG_DWORD_INFO_EX(INTERNAL_DbgShortcutCanary, W("DbgShortcutCanary"), 0, "Allows a way to force canary to fail to be able to test failure paths", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgSkipMEOnStep, W("DbgSkipMEOnStep"), 0, "Turns off MethodEnter checks", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgSkipVerCheck, W("DbgSkipVerCheck"), 0, "Allows different RS and LS versions (for servicing work)", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgTC, W("DbgTC"), 0, "Allows checking boundary compression for offset mappings", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgTransportFaultInject, W("DbgTransportFaultInject"), 0, "Allows injecting a fault for testing the debug transport", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_DbgShortcutCanary, W("DbgShortcutCanary"), 0, "Allows a way to force canary to fail to be able to test failure paths") +CONFIG_DWORD_INFO(INTERNAL_DbgSkipMEOnStep, W("DbgSkipMEOnStep"), 0, "Turns off MethodEnter checks") +CONFIG_DWORD_INFO(INTERNAL_DbgSkipVerCheck, W("DbgSkipVerCheck"), 0, "Allows different RS and LS versions (for servicing work)") +CONFIG_DWORD_INFO(INTERNAL_DbgTC, W("DbgTC"), 0, "Allows checking boundary compression for offset mappings") +CONFIG_DWORD_INFO(INTERNAL_DbgTransportFaultInject, W("DbgTransportFaultInject"), 0, "Allows injecting a fault for testing the debug transport") CONFIG_DWORD_INFO(INTERNAL_DbgTransportLog, W("DbgTransportLog"), 0 /* LE_None */, "Turns on logging for the debug transport") CONFIG_DWORD_INFO(INTERNAL_DbgTransportLogClass, W("DbgTransportLogClass"), (DWORD)-1 /* LC_All */, "Mask to control what is logged in DbgTransportLog") -RETAIL_CONFIG_STRING_INFO_EX(UNSUPPORTED_DbgTransportProxyAddress, W("DbgTransportProxyAddress"), "Allows specifying the transport proxy address", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgTrapOnSkip, W("DbgTrapOnSkip"), 0, "Allows breaking when we skip a breakpoint", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgWaitTimeout, W("DbgWaitTimeout"), 1, "Specifies the timeout value for waits", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_DbgWFDETimeout, W("DbgWFDETimeout"), 25, "Specifies the timeout value for wait when waiting for a debug event", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_RaiseExceptionOnAssert, W("RaiseExceptionOnAssert"), 0, "Raise a first chance (if set to 1) or second chance (if set to 2) exception on asserts.", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DebugBreakOnAssert, W("DebugBreakOnAssert"), 0, "If DACCESS_COMPILE is defined, break on asserts.", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DebugBreakOnVerificationFailure, W("DebugBreakOnVerificationFailure"), 0, "Halts the jit on verification failure", CLRConfig::EEConfig_default) -CONFIG_STRING_INFO_EX(INTERNAL_DebuggerBreakPoint, W("DebuggerBreakPoint"), "Allows counting various debug events", CLRConfig::EEConfig_default) -CONFIG_STRING_INFO_EX(INTERNAL_DebugVerify, W("DebugVerify"), "Control for tracing in peverify", CLRConfig::EEConfig_default) +RETAIL_CONFIG_STRING_INFO(UNSUPPORTED_DbgTransportProxyAddress, W("DbgTransportProxyAddress"), "Allows specifying the transport proxy address") +CONFIG_DWORD_INFO(INTERNAL_DbgTrapOnSkip, W("DbgTrapOnSkip"), 0, "Allows breaking when we skip a breakpoint") +CONFIG_DWORD_INFO(INTERNAL_DbgWaitTimeout, W("DbgWaitTimeout"), 1, "Specifies the timeout value for waits") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_DbgWFDETimeout, W("DbgWFDETimeout"), 25, "Specifies the timeout value for wait when waiting for a debug event") +CONFIG_DWORD_INFO(INTERNAL_RaiseExceptionOnAssert, W("RaiseExceptionOnAssert"), 0, "Raise a first chance (if set to 1) or second chance (if set to 2) exception on asserts.") +CONFIG_DWORD_INFO(INTERNAL_DebugBreakOnAssert, W("DebugBreakOnAssert"), 0, "If DACCESS_COMPILE is defined, break on asserts.") +CONFIG_DWORD_INFO(INTERNAL_DebugBreakOnVerificationFailure, W("DebugBreakOnVerificationFailure"), 0, "Halts the jit on verification failure") +CONFIG_STRING_INFO(INTERNAL_DebuggerBreakPoint, W("DebuggerBreakPoint"), "Allows counting various debug events") +CONFIG_STRING_INFO(INTERNAL_DebugVerify, W("DebugVerify"), "Control for tracing in peverify") CONFIG_DWORD_INFO(INTERNAL_EncApplyChanges, W("EncApplyChanges"), 0, "Allows breaking when ApplyEditAndContinue is called") -CONFIG_DWORD_INFO_EX(INTERNAL_EnCBreakOnRemapComplete, W("EnCBreakOnRemapComplete"), 0, "Allows breaking after N RemapCompletes", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_EnCBreakOnRemapOpportunity, W("EnCBreakOnRemapOpportunity"), 0, "Allows breaking after N RemapOpportunities", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_EnCBreakOnRemapComplete, W("EnCBreakOnRemapComplete"), 0, "Allows breaking after N RemapCompletes") +CONFIG_DWORD_INFO(INTERNAL_EnCBreakOnRemapOpportunity, W("EnCBreakOnRemapOpportunity"), 0, "Allows breaking after N RemapOpportunities") CONFIG_DWORD_INFO(INTERNAL_EncDumpApplyChanges, W("EncDumpApplyChanges"), 0, "Allows dumping edits in delta metadata and il files") CONFIG_DWORD_INFO(INTERNAL_EncFixupFieldBreak, W("EncFixupFieldBreak"), 0, "Unlikely that this is used anymore.") CONFIG_DWORD_INFO(INTERNAL_EncJitUpdatedFunction, W("EncJitUpdatedFunction"), 0, "Allows breaking when an updated function is jitted") CONFIG_DWORD_INFO(INTERNAL_EnCResolveField, W("EnCResolveField"), 0, "Allows breaking when computing the address of an EnC-added field") CONFIG_DWORD_INFO(INTERNAL_EncResumeInUpdatedFunction, W("EncResumeInUpdatedFunction"), 0, "Allows breaking when execution resumes in a new EnC version of a function") -CONFIG_DWORD_INFO_EX(INTERNAL_DbgAssertOnDebuggeeDebugBreak, W("DbgAssertOnDebuggeeDebugBreak"), 0, "If non-zero causes the managed-only debugger to assert on unhandled breakpoints in the debuggee", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_DbgAssertOnDebuggeeDebugBreak, W("DbgAssertOnDebuggeeDebugBreak"), 0, "If non-zero causes the managed-only debugger to assert on unhandled breakpoints in the debuggee") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_DbgDontResumeThreadsOnUnhandledException, W("UNSUPPORTED_DbgDontResumeThreadsOnUnhandledException"), 0, "If non-zero, then don't try to unsuspend threads after continuing a 2nd-chance native exception") -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_DbgSkipStackCheck, W("DbgSkipStackCheck"), 0, "Skip the stack pointer check during stackwalking", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_DbgSkipStackCheck, W("DbgSkipStackCheck"), 0, "Skip the stack pointer check during stackwalking") #ifdef DACCESS_COMPILE CONFIG_DWORD_INFO(INTERNAL_DumpGeneration_IntentionallyCorruptDataFromTarget, W("IntentionallyCorruptDataFromTarget"), 0, "Intentionally fakes bad data retrieved from target to try and break dump generation.") #endif @@ -240,17 +234,16 @@ CONFIG_DWORD_INFO(UNSUPPORTED_Debugging_RequiredVersion, W("UNSUPPORTED_Debuggin RETAIL_CONFIG_DWORD_INFO(INTERNAL_MiniMdBufferCapacity, W("MiniMdBufferCapacity"), 64 * 1024, "The max size of the buffer to store mini metadata information for triage- and mini-dumps.") #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS -CONFIG_DWORD_INFO_EX(INTERNAL_DbgNativeCodeBpBindsAcrossVersions, W("DbgNativeCodeBpBindsAcrossVersions"), 0, "If non-zero causes native breakpoints at offset 0 to bind in all tiered compilation versions of the given method", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_DbgNativeCodeBpBindsAcrossVersions, W("DbgNativeCodeBpBindsAcrossVersions"), 0, "If non-zero causes native breakpoints at offset 0 to bind in all tiered compilation versions of the given method") /// /// Diagnostics (internal general-purpose) /// CONFIG_DWORD_INFO(INTERNAL_ConditionalContracts, W("ConditionalContracts"), 0, "If ENABLE_CONTRACTS_IMPL is defined, sets whether contracts are conditional. (?)") CONFIG_DWORD_INFO(INTERNAL_ConsistencyCheck, W("ConsistencyCheck"), 0, "") -CONFIG_DWORD_INFO_EX(INTERNAL_ContinueOnAssert, W("ContinueOnAssert"), 0, "If set, doesn't break on asserts.", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_disableStackOverflowProbing, W("disableStackOverflowProbing"), 0, "", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_ContinueOnAssert, W("ContinueOnAssert"), 0, "If set, doesn't break on asserts.") CONFIG_DWORD_INFO(INTERNAL_InjectFatalError, W("InjectFatalError"), 0, "") -CONFIG_DWORD_INFO_EX(INTERNAL_InjectFault, W("InjectFault"), 0, "", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_InjectFault, W("InjectFault"), 0, "") CONFIG_DWORD_INFO(INTERNAL_SuppressChecks, W("SuppressChecks"),0, "") #ifdef FEATURE_EH_FUNCLETS CONFIG_DWORD_INFO(INTERNAL_SuppressLockViolationsOnReentryFromOS, W("SuppressLockViolationsOnReentryFromOS"), 0, "64 bit OOM tests re-enter the CLR via RtlVirtualUnwind. This indicates whether to suppress resulting locking violations.") @@ -260,10 +253,10 @@ CONFIG_DWORD_INFO(INTERNAL_SuppressLockViolationsOnReentryFromOS, W("SuppressLoc /// Exception Handling /// CONFIG_DWORD_INFO(INTERNAL_AssertOnFailFast, W("AssertOnFailFast"), 1, "") -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_legacyCorruptedStateExceptionsPolicy, W("legacyCorruptedStateExceptionsPolicy"), 0, "Enabled Pre-V4 CSE behavior", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_SuppressLostExceptionTypeAssert, W("SuppressLostExceptionTypeAssert"), 0, "", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_UseEntryPointFilter, W("UseEntryPointFilter"), 0, "", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_Corhost_Swallow_Uncaught_Exceptions, W("Corhost_Swallow_Uncaught_Exceptions"), 0, "", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_legacyCorruptedStateExceptionsPolicy, W("legacyCorruptedStateExceptionsPolicy"), 0, "Enabled Pre-V4 CSE behavior") +CONFIG_DWORD_INFO(INTERNAL_SuppressLostExceptionTypeAssert, W("SuppressLostExceptionTypeAssert"), 0, "") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_UseEntryPointFilter, W("UseEntryPointFilter"), 0, "") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_Corhost_Swallow_Uncaught_Exceptions, W("Corhost_Swallow_Uncaught_Exceptions"), 0, "") /// /// Garbage collector @@ -283,8 +276,8 @@ RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_StatsUpdatePeriod, W("StatsUpdatePeriod"), RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_GCRetainVM, W("GCRetainVM"), 0, "When set we put the segments that should be deleted on a standby list (instead of releasing them back to the OS) which will be considered to satisfy new segment requests (note that the same thing can be specified via API which is the supported way)") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_GCLOHThreshold, W("GCLOHThreshold"), 0, "Specifies the size that will make objects go on LOH") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_gcAllowVeryLargeObjects, W("gcAllowVeryLargeObjects"), 1, "Allow allocation of 2GB+ objects on GC heap") -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_GCStress, W("GCStress"), 0, "Trigger GCs at regular intervals", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_GcStressOnDirectCalls, W("GcStressOnDirectCalls"), 0, "Whether to trigger a GC on direct calls", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_GCStress, W("GCStress"), 0, "Trigger GCs at regular intervals") +CONFIG_DWORD_INFO(INTERNAL_GcStressOnDirectCalls, W("GcStressOnDirectCalls"), 0, "Whether to trigger a GC on direct calls") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_HeapVerify, W("HeapVerify"), 0, "When set verifies the integrity of the managed heap on entry and exit of each GC") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_GCNumaAware, W("GCNumaAware"), 1, "Specifies if to enable GC NUMA aware") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_GCCpuGroup, W("GCCpuGroup"), 0, "Specifies if to enable GC to support CPU groups") @@ -293,32 +286,32 @@ RETAIL_CONFIG_STRING_INFO(EXTERNAL_GCName, W("GCName"), "") /// /// IBC /// -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_ConvertIbcData, W("ConvertIbcData"), 1, "Converts between v1 and v2 IBC data", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_DisableIBC, W("DisableIBC"), 0, "Disables the use of IBC data", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_UseIBCFile, W("UseIBCFile"), 0, "", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_ConvertIbcData, W("ConvertIbcData"), 1, "Converts between v1 and v2 IBC data") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_DisableIBC, W("DisableIBC"), 0, "Disables the use of IBC data") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_UseIBCFile, W("UseIBCFile"), 0, "") /// /// JIT /// -CONFIG_DWORD_INFO_EX(INTERNAL_JitBreakEmit, W("JitBreakEmit"), (DWORD)-1, "", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_JitBreakEmit, W("JitBreakEmit"), (DWORD)-1, "") CONFIG_DWORD_INFO(INTERNAL_JitDebuggable, W("JitDebuggable"), 0, "") #if !defined(DEBUG) && !defined(_DEBUG) #define INTERNAL_JitEnableNoWayAssert_Default 0 #else #define INTERNAL_JitEnableNoWayAssert_Default 1 #endif -RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_JitEnableNoWayAssert, W("JitEnableNoWayAssert"), INTERNAL_JitEnableNoWayAssert_Default, "", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(INTERNAL_JitEnableNoWayAssert, W("JitEnableNoWayAssert"), INTERNAL_JitEnableNoWayAssert_Default, "") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_JitFramed, W("JitFramed"), 0, "Forces EBP frames") -CONFIG_DWORD_INFO_EX(INTERNAL_JitGCStress, W("JitGCStress"), 0, "GC stress mode for jit", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_JitGCStress, W("JitGCStress"), 0, "GC stress mode for jit") CONFIG_DWORD_INFO(INTERNAL_JitHeartbeat, W("JitHeartbeat"), 0, "") CONFIG_DWORD_INFO(INTERNAL_JitHelperLogging, W("JitHelperLogging"), 0, "") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_JITMinOpts, W("JITMinOpts"), 0, "Forces MinOpts") RETAIL_CONFIG_STRING_INFO(EXTERNAL_JitName, W("JitName"), "Primary Jit to use") #if defined(ALLOW_SXS_JIT) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_AltJitName, W("AltJitName"), "Alternative Jit to use, will fall back to primary jit.", CLRConfig::EEConfig_default) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_AltJit, W("AltJit"), "Enables AltJit and selectively limits it to the specified methods.", CLRConfig::EEConfig_default) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_AltJitExcludeAssemblies, W("AltJitExcludeAssemblies"), "Do not use AltJit on this semicolon-delimited list of assemblies.", CLRConfig::EEConfig_default) +RETAIL_CONFIG_STRING_INFO(EXTERNAL_AltJitName, W("AltJitName"), "Alternative Jit to use, will fall back to primary jit.") +RETAIL_CONFIG_STRING_INFO(EXTERNAL_AltJit, W("AltJit"), "Enables AltJit and selectively limits it to the specified methods.") +RETAIL_CONFIG_STRING_INFO(EXTERNAL_AltJitExcludeAssemblies, W("AltJitExcludeAssemblies"), "Do not use AltJit on this semicolon-delimited list of assemblies.") #endif // defined(ALLOW_SXS_JIT) #if defined(FEATURE_STACK_SAMPLING) @@ -329,21 +322,21 @@ RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_StackSamplingNumMethods, W("StackSamplingNu #endif // defined(FEATURE_JIT_SAMPLING) #if defined(ALLOW_SXS_JIT_NGEN) -RETAIL_CONFIG_STRING_INFO_EX(INTERNAL_AltJitNgen, W("AltJitNgen"), "Enables AltJit for NGEN and selectively limits it to the specified methods.", CLRConfig::EEConfig_default) +RETAIL_CONFIG_STRING_INFO(INTERNAL_AltJitNgen, W("AltJitNgen"), "Enables AltJit for NGEN and selectively limits it to the specified methods.") #endif // defined(ALLOW_SXS_JIT_NGEN) RETAIL_CONFIG_DWORD_INFO(EXTERNAL_JitHostMaxSlabCache, W("JitHostMaxSlabCache"), 0x1000000, "Sets jit host max slab cache size, 16MB default") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_JitOptimizeType, W("JitOptimizeType"), 0 /* OPT_DEFAULT */, "") -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_JitPrintInlinedMethods, W("JitPrintInlinedMethods"), 0, "", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_JitPrintInlinedMethods, W("JitPrintInlinedMethods"), 0, "") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_JitTelemetry, W("JitTelemetry"), 1, "If non-zero, gather JIT telemetry data") RETAIL_CONFIG_STRING_INFO(INTERNAL_JitTimeLogFile, W("JitTimeLogFile"), "If set, gather JIT throughput data and write to this file.") RETAIL_CONFIG_STRING_INFO(INTERNAL_JitTimeLogCsv, W("JitTimeLogCsv"), "If set, gather JIT throughput data and write to a CSV file. This mode must be used in internal retail builds.") RETAIL_CONFIG_STRING_INFO(INTERNAL_JitFuncInfoLogFile, W("JitFuncInfoLogFile"), "If set, gather JIT function info and write to this file.") CONFIG_DWORD_INFO(INTERNAL_JitVerificationDisable, W("JitVerificationDisable"), 0, "") RETAIL_CONFIG_DWORD_INFO(INTERNAL_JitLockWrite, W("JitLockWrite"), 0, "Force all volatile writes to be 'locked'") -CONFIG_STRING_INFO_EX(INTERNAL_TailCallMax, W("TailCallMax"), "", CLRConfig::EEConfig_default) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_TailCallOpt, W("TailCallOpt"), "", CLRConfig::EEConfig_default) +CONFIG_STRING_INFO(INTERNAL_TailCallMax, W("TailCallMax"), "") +RETAIL_CONFIG_STRING_INFO(EXTERNAL_TailCallOpt, W("TailCallOpt"), "") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TailCallLoopOpt, W("TailCallLoopOpt"), 1, "Convert recursive tail calls to loops") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_Jit_NetFx40PInvokeStackResilience, W("NetFx40_PInvokeStackResilience"), (DWORD)-1, "Makes P/Invoke resilient against mismatched signature and calling convention (significant perf penalty).") @@ -353,10 +346,10 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_AltJitAssertOnNYI, W("AltJitAssertOnNYI"), 0, #else RETAIL_CONFIG_DWORD_INFO(INTERNAL_AltJitAssertOnNYI, W("AltJitAssertOnNYI"), 1, "Controls the AltJit behavior of NYI stuff") #endif -CONFIG_DWORD_INFO_EX(INTERNAL_JitLargeBranches, W("JitLargeBranches"), 0, "Force using the largest conditional branch format", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_JitRegisterFP, W("JitRegisterFP"), 3, "Control FP enregistration", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_JitLargeBranches, W("JitLargeBranches"), 0, "Force using the largest conditional branch format") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_JitRegisterFP, W("JitRegisterFP"), 3, "Control FP enregistration") RETAIL_CONFIG_DWORD_INFO(INTERNAL_JitELTHookEnabled, W("JitELTHookEnabled"), 0, "On ARM, setting this will emit Enter/Leave/TailCall callbacks") -RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_JitMemStats, W("JitMemStats"), 0, "Display JIT memory usage statistics", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(INTERNAL_JitMemStats, W("JitMemStats"), 0, "Display JIT memory usage statistics") RETAIL_CONFIG_DWORD_INFO(INTERNAL_JitVNMapSelBudget, W("JitVNMapSelBudget"), 100, "Max # of MapSelect's considered for a particular top-level invocation.") #if defined(TARGET_AMD64) || defined(TARGET_X86) || defined(TARGET_ARM64) #define EXTERNAL_FeatureSIMD_Default 1 @@ -368,10 +361,10 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_JitVNMapSelBudget, W("JitVNMapSelBudget"), 100 #else // !(defined(TARGET_AMD64) || defined(TARGET_X86) #define EXTERNAL_JitEnableAVX_Default 0 #endif // !(defined(TARGET_AMD64) || defined(TARGET_X86) -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_FeatureSIMD, W("FeatureSIMD"), EXTERNAL_FeatureSIMD_Default, "Enable SIMD intrinsics recognition in System.Numerics.dll and/or System.Numerics.Vectors.dll", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_FeatureSIMD, W("FeatureSIMD"), EXTERNAL_FeatureSIMD_Default, "Enable SIMD intrinsics recognition in System.Numerics.dll and/or System.Numerics.Vectors.dll") RETAIL_CONFIG_DWORD_INFO(INTERNAL_SIMD16ByteOnly, W("SIMD16ByteOnly"), 0, "Limit maximum SIMD vector length to 16 bytes (used by x64_arm64_altjit)") -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_EnableAVX, W("EnableAVX"), EXTERNAL_JitEnableAVX_Default, "Enable AVX instruction set for wide operations as default", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_TrackDynamicMethodDebugInfo, W("TrackDynamicMethodDebugInfo"), 0, "Specifies whether debug info should be generated and tracked for dynamic methods", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableAVX, W("EnableAVX"), EXTERNAL_JitEnableAVX_Default, "Enable AVX instruction set for wide operations as default") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TrackDynamicMethodDebugInfo, W("TrackDynamicMethodDebugInfo"), 0, "Specifies whether debug info should be generated and tracked for dynamic methods") #ifdef FEATURE_MULTICOREJIT @@ -384,18 +377,18 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_MultiCoreJitProfileWriteDelay, W("MultiCoreJit /// /// Interpreter /// -RETAIL_CONFIG_STRING_INFO_EX(INTERNAL_Interpret, W("Interpret"), "Selectively uses the interpreter to execute the specified methods", CLRConfig::EEConfig_default) -RETAIL_CONFIG_STRING_INFO_EX(INTERNAL_InterpretExclude, W("InterpretExclude"), "Excludes the specified methods from the set selected by 'Interpret'", CLRConfig::EEConfig_default) +RETAIL_CONFIG_STRING_INFO(INTERNAL_Interpret, W("Interpret"), "Selectively uses the interpreter to execute the specified methods") +RETAIL_CONFIG_STRING_INFO(INTERNAL_InterpretExclude, W("InterpretExclude"), "Excludes the specified methods from the set selected by 'Interpret'") RETAIL_CONFIG_DWORD_INFO(INTERNAL_InterpreterMethHashMin, W("InterpreterMethHashMin"), 0, "Only interpret methods selected by 'Interpret' whose hash is at least this value. or after nth") RETAIL_CONFIG_DWORD_INFO(INTERNAL_InterpreterMethHashMax, W("InterpreterMethHashMax"), UINT32_MAX, "If non-zero, only interpret methods selected by 'Interpret' whose hash is at most this value") RETAIL_CONFIG_DWORD_INFO(INTERNAL_InterpreterStubMin, W("InterpreterStubMin"), 0, "Only interpret methods selected by 'Interpret' whose stub num is at least this value.") RETAIL_CONFIG_DWORD_INFO(INTERNAL_InterpreterStubMax, W("InterpreterStubMax"), UINT32_MAX, "If non-zero, only interpret methods selected by 'Interpret' whose stub number is at most this value.") RETAIL_CONFIG_DWORD_INFO(INTERNAL_InterpreterJITThreshold, W("InterpreterJITThreshold"), 10, "The number of times a method should be interpreted before being JITted") RETAIL_CONFIG_DWORD_INFO(INTERNAL_InterpreterDoLoopMethods, W("InterpreterDoLoopMethods"), 0, "If set, don't check for loops, start by interpreting *all* methods") -RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_InterpreterUseCaching, W("InterpreterUseCaching"), 1, "If non-zero, use the caching mechanism.", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_InterpreterLooseRules, W("InterpreterLooseRules"), 1, "If non-zero, allow ECMA spec violations required by managed C++.", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(INTERNAL_InterpreterUseCaching, W("InterpreterUseCaching"), 1, "If non-zero, use the caching mechanism.") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_InterpreterLooseRules, W("InterpreterLooseRules"), 1, "If non-zero, allow ECMA spec violations required by managed C++.") RETAIL_CONFIG_DWORD_INFO(INTERNAL_InterpreterPrintPostMortem, W("InterpreterPrintPostMortem"), 0, "Prints summary information about the execution to the console") -RETAIL_CONFIG_STRING_INFO_EX(INTERNAL_InterpreterLogFile, W("InterpreterLogFile"), "If non-null, append interpreter logging to this file, else use stdout", CLRConfig::EEConfig_default) +RETAIL_CONFIG_STRING_INFO(INTERNAL_InterpreterLogFile, W("InterpreterLogFile"), "If non-null, append interpreter logging to this file, else use stdout") RETAIL_CONFIG_DWORD_INFO(INTERNAL_DumpInterpreterStubs, W("DumpInterpreterStubs"), 0, "Prints all interpreter stubs that are created to the console") RETAIL_CONFIG_DWORD_INFO(INTERNAL_TraceInterpreterEntries, W("TraceInterpreterEntries"), 0, "Logs entries to interpreted methods to the console") RETAIL_CONFIG_DWORD_INFO(INTERNAL_TraceInterpreterIL, W("TraceInterpreterIL"), 0, "Logs individual instructions of interpreted methods to the console") @@ -415,7 +408,7 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_InterpreterFallback, W("InterpreterFallback"), /// /// Loader heap /// -CONFIG_DWORD_INFO_EX(INTERNAL_LoaderHeapCallTracing, W("LoaderHeapCallTracing"), 0, "Loader heap troubleshooting", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_LoaderHeapCallTracing, W("LoaderHeapCallTracing"), 0, "Loader heap troubleshooting") RETAIL_CONFIG_DWORD_INFO(INTERNAL_CodeHeapReserveForJumpStubs, W("CodeHeapReserveForJumpStubs"), 1, "Percentage of code heap to reserve for jump stubs") RETAIL_CONFIG_DWORD_INFO(INTERNAL_NGenReserveForJumpStubs, W("NGenReserveForJumpStubs"), 0, "Percentage of ngen image size to reserve for jump stubs") RETAIL_CONFIG_DWORD_INFO(INTERNAL_BreakOnOutOfMemoryWithinRange, W("BreakOnOutOfMemoryWithinRange"), 0, "Break before out of memory within range exception is thrown") @@ -427,7 +420,7 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_LogEnable, W("LogEnable"), 0, "Turns on the tr RETAIL_CONFIG_DWORD_INFO(INTERNAL_LogFacility, W("LogFacility"), 0, "Specifies a facility mask for CLR log. (See 'loglf.h'; VM interprets string value as hex number.) Also used by stresslog.") RETAIL_CONFIG_DWORD_INFO(INTERNAL_LogFacility2, W("LogFacility2"), 0, "Specifies a facility mask for CLR log. (See 'loglf.h'; VM interprets string value as hex number.) Also used by stresslog.") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_logFatalError, W("logFatalError"), 1, "Specifies whether EventReporter logs fatal errors in the Windows event log.") -CONFIG_STRING_INFO_EX(INTERNAL_LogFile, W("LogFile"), "Specifies a file name for the CLR log.", CLRConfig::EEConfig_default) +CONFIG_STRING_INFO(INTERNAL_LogFile, W("LogFile"), "Specifies a file name for the CLR log.") CONFIG_DWORD_INFO(INTERNAL_LogFileAppend, W("LogFileAppend"), 0 , "Specifies whether to append to or replace the CLR log file.") CONFIG_DWORD_INFO(INTERNAL_LogFlushFile, W("LogFlushFile"), 0 , "Specifies whether to flush the CLR log file on each write.") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_LogLevel, W("LogLevel"), 0 , "4=10 msgs, 9=1000000, 10=everything") @@ -439,32 +432,32 @@ CONFIG_DWORD_INFO(INTERNAL_LogWithPid, W("LogWithPid"), FALSE, "Appends pid to f /// /// MetaData /// -CONFIG_DWORD_INFO_EX(INTERNAL_MD_ApplyDeltaBreak, W("MD_ApplyDeltaBreak"), 0, "ASSERT when applying EnC", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_MD_ApplyDeltaBreak, W("MD_ApplyDeltaBreak"), 0, "ASSERT when applying EnC") RETAIL_CONFIG_DWORD_INFO(INTERNAL_AssertOnBadImageFormat, W("AssertOnBadImageFormat"), 0, "ASSERT when invalid MD read") -RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_MD_DeltaCheck, W("MD_DeltaCheck"), 1, "Some checks of GUID when applying EnC (?)", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_MD_EncDelta, W("MD_EncDelta"), 0, "Forces EnC Delta format in MD (?)", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_MD_ForceNoColDesSharing, W("MD_ForceNoColDesSharing"), 0, "Don't know - the only usage I could find is #if 0 (?)", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_MD_KeepKnownCA, W("MD_KeepKnownCA"), 0, "Something with known CAs (?)", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_MD_MiniMDBreak, W("MD_MiniMDBreak"), 0, "ASSERT when creating CMiniMdRw class", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_MD_PreSaveBreak, W("MD_PreSaveBreak"), 0, "ASSERT when calling CMiniMdRw::PreSave", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_MD_RegMetaBreak, W("MD_RegMetaBreak"), 0, "ASSERT when creating RegMeta class", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_MD_RegMetaDump, W("MD_RegMetaDump"), 0, "Dump MD in 4 functions (?)", CLRConfig::EEConfig_default) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_DOTNET_MODIFIABLE_ASSEMBLIES, W("DOTNET_MODIFIABLE_ASSEMBLIES"), "Enables hot reload on debug built assemblies with the 'debug' keyword", CLRConfig::DontPrependCOMPlus_ | CLRConfig::TrimWhiteSpaceFromStringValue); +RETAIL_CONFIG_DWORD_INFO(INTERNAL_MD_DeltaCheck, W("MD_DeltaCheck"), 1, "Some checks of GUID when applying EnC (?)") +CONFIG_DWORD_INFO(INTERNAL_MD_EncDelta, W("MD_EncDelta"), 0, "Forces EnC Delta format in MD (?)") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_MD_ForceNoColDesSharing, W("MD_ForceNoColDesSharing"), 0, "Don't know - the only usage I could find is #if 0 (?)") +CONFIG_DWORD_INFO(INTERNAL_MD_KeepKnownCA, W("MD_KeepKnownCA"), 0, "Something with known CAs (?)") +CONFIG_DWORD_INFO(INTERNAL_MD_MiniMDBreak, W("MD_MiniMDBreak"), 0, "ASSERT when creating CMiniMdRw class") +CONFIG_DWORD_INFO(INTERNAL_MD_PreSaveBreak, W("MD_PreSaveBreak"), 0, "ASSERT when calling CMiniMdRw::PreSave") +CONFIG_DWORD_INFO(INTERNAL_MD_RegMetaBreak, W("MD_RegMetaBreak"), 0, "ASSERT when creating RegMeta class") +CONFIG_DWORD_INFO(INTERNAL_MD_RegMetaDump, W("MD_RegMetaDump"), 0, "Dump MD in 4 functions (?)") +RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_DOTNET_MODIFIABLE_ASSEMBLIES, W("DOTNET_MODIFIABLE_ASSEMBLIES"), "Enables hot reload on debug built assemblies with the 'debug' keyword", CLRConfig::LookupOptions::DontPrependCOMPlus_ | CLRConfig::LookupOptions::TrimWhiteSpaceFromStringValue); // Metadata - mscordbi only - this flag is only intended to mitigate potential issues in bug fix 458597. -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_MD_PreserveDebuggerMetadataMemory, W("MD_PreserveDebuggerMetadataMemory"), 0, "Save all versions of metadata memory in the debugger when debuggee metadata is updated", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_MD_PreserveDebuggerMetadataMemory, W("MD_PreserveDebuggerMetadataMemory"), 0, "Save all versions of metadata memory in the debugger when debuggee metadata is updated") /// /// Spinning heuristics /// // Note that these only take effect once the runtime has been started; prior to that the values hardcoded in g_SpinConstants (vars.cpp) are used -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_SpinInitialDuration, W("SpinInitialDuration"), 0x32, "Hex value specifying the first spin duration", EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_SpinBackoffFactor, W("SpinBackoffFactor"), 0x3, "Hex value specifying the growth of each successive spin duration", EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_SpinLimitProcCap, W("SpinLimitProcCap"), 0xFFFFFFFF, "Hex value specifying the largest value of NumProcs to use when calculating the maximum spin duration", EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_SpinLimitProcFactor, W("SpinLimitProcFactor"), 0x4E20, "Hex value specifying the multiplier on NumProcs to use when calculating the maximum spin duration", EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_SpinLimitConstant, W("SpinLimitConstant"), 0x0, "Hex value specifying the constant to add when calculating the maximum spin duration", EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_SpinRetryCount, W("SpinRetryCount"), 0xA, "Hex value specifying the number of times the entire spin process is repeated (when applicable)", EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_Monitor_SpinCount, W("Monitor_SpinCount"), 0x1e, "Hex value specifying the maximum number of spin iterations Monitor may perform upon contention on acquiring the lock before waiting.", EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_SpinInitialDuration, W("SpinInitialDuration"), 0x32, "Hex value specifying the first spin duration") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_SpinBackoffFactor, W("SpinBackoffFactor"), 0x3, "Hex value specifying the growth of each successive spin duration") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_SpinLimitProcCap, W("SpinLimitProcCap"), 0xFFFFFFFF, "Hex value specifying the largest value of NumProcs to use when calculating the maximum spin duration") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_SpinLimitProcFactor, W("SpinLimitProcFactor"), 0x4E20, "Hex value specifying the multiplier on NumProcs to use when calculating the maximum spin duration") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_SpinLimitConstant, W("SpinLimitConstant"), 0x0, "Hex value specifying the constant to add when calculating the maximum spin duration") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_SpinRetryCount, W("SpinRetryCount"), 0xA, "Hex value specifying the number of times the entire spin process is repeated (when applicable)") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_Monitor_SpinCount, W("Monitor_SpinCount"), 0x1e, "Hex value specifying the maximum number of spin iterations Monitor may perform upon contention on acquiring the lock before waiting.") /// /// Native Binder @@ -474,27 +467,26 @@ CONFIG_DWORD_INFO(INTERNAL_NgenBind_ZapForbid, W("NgenBind_ZapForbid CONFIG_STRING_INFO(INTERNAL_NgenBind_ZapForbidExcludeList, W("NgenBind_ZapForbidExcludeList"), "") CONFIG_STRING_INFO(INTERNAL_NgenBind_ZapForbidList, W("NgenBind_ZapForbidList"), "") -CONFIG_DWORD_INFO_EX(INTERNAL_SymDiffDump, W("SymDiffDump"), 0, "Used to create the map file while binding the assembly. Used by SemanticDiffer", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_SymDiffDump, W("SymDiffDump"), 0, "Used to create the map file while binding the assembly. Used by SemanticDiffer") /// /// NGEN /// -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_NGen_JitName, W("NGen_JitName"), "", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_NGenFramed, W("NGenFramed"), (DWORD)-1, "Same as JitFramed, but for ngen", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_NGenOnlyOneMethod, W("NGenOnlyOneMethod"), 0, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_NgenOrder, W("NgenOrder"), 0, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_partialNGenStress, W("partialNGenStress"), 0, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_ZapDoNothing, W("ZapDoNothing"), 0, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_NgenForceFailureMask, W("NgenForceFailureMask"), (DWORD)-1, "Bitmask used to control which locations will check and raise the failure (defaults to bits: -1)", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_NgenForceFailureCount, W("NgenForceFailureCount"), 0, "If set to >0 and we have IBC data we will force a failure after we reference an IBC data item times", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_NgenForceFailureKind, W("NgenForceFailureKind"), 1, "If set to 1, We will throw a TypeLoad exception; If set to 2, We will cause an A/V", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_NGenFramed, W("NGenFramed"), (DWORD)-1, "Same as JitFramed, but for ngen") +CONFIG_DWORD_INFO(INTERNAL_NGenOnlyOneMethod, W("NGenOnlyOneMethod"), 0, "") +CONFIG_DWORD_INFO(INTERNAL_NgenOrder, W("NgenOrder"), 0, "") +CONFIG_DWORD_INFO(INTERNAL_partialNGenStress, W("partialNGenStress"), 0, "") +CONFIG_DWORD_INFO(INTERNAL_ZapDoNothing, W("ZapDoNothing"), 0, "") +CONFIG_DWORD_INFO(INTERNAL_NgenForceFailureMask, W("NgenForceFailureMask"), (DWORD)-1, "Bitmask used to control which locations will check and raise the failure (defaults to bits: -1)") +CONFIG_DWORD_INFO(INTERNAL_NgenForceFailureCount, W("NgenForceFailureCount"), 0, "If set to >0 and we have IBC data we will force a failure after we reference an IBC data item times") +CONFIG_DWORD_INFO(INTERNAL_NgenForceFailureKind, W("NgenForceFailureKind"), 1, "If set to 1, We will throw a TypeLoad exception; If set to 2, We will cause an A/V") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_NGenEnableCreatePdb, W("NGenEnableCreatePdb"), 0, "If set to >0 ngen.exe displays help on, recognizes createpdb in the command line") RETAIL_CONFIG_DWORD_INFO(INTERNAL_NGenSimulateDiskFull, W("NGenSimulateDiskFull"), 0, "If set to 1, ngen will throw a Disk full exception in ZapWriter.cpp:Save()") RETAIL_CONFIG_DWORD_INFO(INTERNAL_PartialNGen, W("PartialNGen"), (DWORD)-1, "Generate partial NGen images") CONFIG_DWORD_INFO(INTERNAL_NoASLRForNgen, W("NoASLRForNgen"), 0, "Turn off IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE bit in generated ngen images. Makes nidump output repeatable from run to run.") -RETAIL_CONFIG_STRING_INFO_EX(INTERNAL_NativeImageSearchPaths, W("NativeImageSearchPaths"), "Extra search paths for native composite R2R images", CLRConfig::EEConfig_default) +RETAIL_CONFIG_STRING_INFO(INTERNAL_NativeImageSearchPaths, W("NativeImageSearchPaths"), "Extra search paths for native composite R2R images") #ifdef CROSSGEN_COMPILE RETAIL_CONFIG_DWORD_INFO(INTERNAL_CrossGenAssumeInputSigned, W("CrossGenAssumeInputSigned"), 1, "CrossGen should assume that its input assemblies will be signed before deployment") @@ -503,19 +495,19 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_CrossGenAssumeInputSigned, W("CrossGenAssumeIn /// /// Profiling API / ETW /// -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_COR_ENABLE_PROFILING, W("COR_ENABLE_PROFILING"), 0, "Flag to indicate whether profiling should be enabled for the currently running process.", CLRConfig::DontPrependCOMPlus_) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_COR_PROFILER, W("COR_PROFILER"), "Specifies GUID of profiler to load into currently running process", CLRConfig::DontPrependCOMPlus_) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_COR_PROFILER_PATH, W("COR_PROFILER_PATH"), "Specifies the path to the DLL of profiler to load into currently running process", CLRConfig::DontPrependCOMPlus_) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_COR_PROFILER_PATH_32, W("COR_PROFILER_PATH_32"), "Specifies the path to the DLL of profiler to load into currently running 32 bits process", CLRConfig::DontPrependCOMPlus_) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_COR_PROFILER_PATH_64, W("COR_PROFILER_PATH_64"), "Specifies the path to the DLL of profiler to load into currently running 64 bits process", CLRConfig::DontPrependCOMPlus_) -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_CORECLR_ENABLE_PROFILING, W("CORECLR_ENABLE_PROFILING"), 0, "CoreCLR only: Flag to indicate whether profiling should be enabled for the currently running process.", CLRConfig::DontPrependCOMPlus_) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER, W("CORECLR_PROFILER"), "CoreCLR only: Specifies GUID of profiler to load into currently running process", CLRConfig::DontPrependCOMPlus_) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH, W("CORECLR_PROFILER_PATH"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running process", CLRConfig::DontPrependCOMPlus_) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH_32, W("CORECLR_PROFILER_PATH_32"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running 32 process", CLRConfig::DontPrependCOMPlus_) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH_64, W("CORECLR_PROFILER_PATH_64"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running 64 process", CLRConfig::DontPrependCOMPlus_) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH_ARM32, W("CORECLR_PROFILER_PATH_ARM32"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running ARM32 process", CLRConfig::DontPrependCOMPlus_) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH_ARM64, W("CORECLR_PROFILER_PATH_ARM64"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running ARM64 process", CLRConfig::DontPrependCOMPlus_) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_ProfAPI_ProfilerCompatibilitySetting, W("ProfAPI_ProfilerCompatibilitySetting"), "Specifies the profiler loading policy (the default is not to load a V2 profiler in V4)", CLRConfig::EEConfig_default | CLRConfig::TrimWhiteSpaceFromStringValue) +RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_COR_ENABLE_PROFILING, W("COR_ENABLE_PROFILING"), 0, "Flag to indicate whether profiling should be enabled for the currently running process.", CLRConfig::LookupOptions::DontPrependCOMPlus_) +RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_COR_PROFILER, W("COR_PROFILER"), "Specifies GUID of profiler to load into currently running process", CLRConfig::LookupOptions::DontPrependCOMPlus_) +RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_COR_PROFILER_PATH, W("COR_PROFILER_PATH"), "Specifies the path to the DLL of profiler to load into currently running process", CLRConfig::LookupOptions::DontPrependCOMPlus_) +RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_COR_PROFILER_PATH_32, W("COR_PROFILER_PATH_32"), "Specifies the path to the DLL of profiler to load into currently running 32 bits process", CLRConfig::LookupOptions::DontPrependCOMPlus_) +RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_COR_PROFILER_PATH_64, W("COR_PROFILER_PATH_64"), "Specifies the path to the DLL of profiler to load into currently running 64 bits process", CLRConfig::LookupOptions::DontPrependCOMPlus_) +RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_CORECLR_ENABLE_PROFILING, W("CORECLR_ENABLE_PROFILING"), 0, "CoreCLR only: Flag to indicate whether profiling should be enabled for the currently running process.", CLRConfig::LookupOptions::DontPrependCOMPlus_) +RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER, W("CORECLR_PROFILER"), "CoreCLR only: Specifies GUID of profiler to load into currently running process", CLRConfig::LookupOptions::DontPrependCOMPlus_) +RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH, W("CORECLR_PROFILER_PATH"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running process", CLRConfig::LookupOptions::DontPrependCOMPlus_) +RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH_32, W("CORECLR_PROFILER_PATH_32"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running 32 process", CLRConfig::LookupOptions::DontPrependCOMPlus_) +RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH_64, W("CORECLR_PROFILER_PATH_64"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running 64 process", CLRConfig::LookupOptions::DontPrependCOMPlus_) +RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH_ARM32, W("CORECLR_PROFILER_PATH_ARM32"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running ARM32 process", CLRConfig::LookupOptions::DontPrependCOMPlus_) +RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH_ARM64, W("CORECLR_PROFILER_PATH_ARM64"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running ARM64 process", CLRConfig::LookupOptions::DontPrependCOMPlus_) +RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_ProfAPI_ProfilerCompatibilitySetting, W("ProfAPI_ProfilerCompatibilitySetting"), "Specifies the profiler loading policy (the default is not to load a V2 profiler in V4)", CLRConfig::LookupOptions::TrimWhiteSpaceFromStringValue) RETAIL_CONFIG_DWORD_INFO(EXTERNAL_ProfAPI_DetachMinSleepMs, W("ProfAPI_DetachMinSleepMs"), 0, "The minimum time, in milliseconds, the CLR will wait before checking whether a profiler that is in the process of detaching is ready to be unloaded.") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_ProfAPI_DetachMaxSleepMs, W("ProfAPI_DetachMaxSleepMs"), 0, "The maximum time, in milliseconds, the CLR will wait before checking whether a profiler that is in the process of detaching is ready to be unloaded.") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_ProfAPI_RejitOnAttach, W("ProfApi_RejitOnAttach"), 1, "Enables the ability for profilers to rejit methods on attach.") @@ -527,13 +519,13 @@ CONFIG_DWORD_INFO(INTERNAL_TestOnlyEnableICorProfilerInfo, W("ProfAPI_TestOnlyEn CONFIG_DWORD_INFO(INTERNAL_TestOnlyEnableObjectAllocatedHook, W("TestOnlyEnableObjectAllocatedHook"), 0, "Test-only flag that forces CLR to initialize on startup as if ObjectAllocated callback were requested, to enable post-attach ObjectAllocated functionality.") CONFIG_DWORD_INFO(INTERNAL_TestOnlyEnableSlowELTHooks, W("TestOnlyEnableSlowELTHooks"), 0, "Test-only flag that forces CLR to initialize on startup as if slow-ELT were requested, to enable post-attach ELT functionality.") -RETAIL_CONFIG_STRING_INFO_EX(UNSUPPORTED_ETW_ObjectAllocationEventsPerTypePerSec, W("ETW_ObjectAllocationEventsPerTypePerSec"), "Desired number of GCSampledObjectAllocation ETW events to be logged per type per second. If 0, then the default built in to the implementation for the enabled event (e.g., High, Low), will be used.", CLRConfig::EEConfig_default) +RETAIL_CONFIG_STRING_INFO(UNSUPPORTED_ETW_ObjectAllocationEventsPerTypePerSec, W("ETW_ObjectAllocationEventsPerTypePerSec"), "Desired number of GCSampledObjectAllocation ETW events to be logged per type per second. If 0, then the default built in to the implementation for the enabled event (e.g., High, Low), will be used.") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_ProfAPI_ValidateNGENInstrumentation, W("ProfAPI_ValidateNGENInstrumentation"), 0, "This flag enables additional validations when using the IMetaDataEmit APIs for NGEN'ed images to ensure only supported edits are made.") #ifdef FEATURE_PERFMAP -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_PerfMapEnabled, W("PerfMapEnabled"), 0, "This flag is used on Linux to enable writing /tmp/perf-$pid.map. It is disabled by default", CLRConfig::EEConfig_default) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_PerfMapJitDumpPath, W("PerfMapJitDumpPath"), "Specifies a path to write the perf jitdump file. Defaults to GetTempPathA()", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_PerfMapIgnoreSignal, W("PerfMapIgnoreSignal"), 0, "When perf map is enabled, this option will configure the specified signal to be accepted and ignored as a marker in the perf logs. It is disabled by default", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_PerfMapEnabled, W("PerfMapEnabled"), 0, "This flag is used on Linux to enable writing /tmp/perf-$pid.map. It is disabled by default") +RETAIL_CONFIG_STRING_INFO(EXTERNAL_PerfMapJitDumpPath, W("PerfMapJitDumpPath"), "Specifies a path to write the perf jitdump file. Defaults to GetTempPathA()") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_PerfMapIgnoreSignal, W("PerfMapIgnoreSignal"), 0, "When perf map is enabled, this option will configure the specified signal to be accepted and ignored as a marker in the perf logs. It is disabled by default") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_PerfMapShowOptimizationTiers, W("PerfMapShowOptimizationTiers"), 1, "Shows optimization tiers in the perf map for methods, as part of the symbol name. Useful for seeing separate stack frames for different optimization tiers of each method.") RETAIL_CONFIG_STRING_INFO(EXTERNAL_NativeImagePerfMapFormat, W("NativeImagePerfMapFormat"), "Specifies the format of native image perfmap files generated by crossgen. Valid options are RVA or OFFSET.") #endif @@ -543,12 +535,12 @@ RETAIL_CONFIG_STRING_INFO(EXTERNAL_StartupDelayMS, W("StartupDelayMS"), "") /// /// Stress /// -CONFIG_DWORD_INFO_EX(INTERNAL_StressCOMCall, W("StressCOMCall"), 0, "", CLRConfig::EEConfig_default) RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_StressLog, W("StressLog"), 0, "Turns on the stress log.") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_ForceEnc, W("ForceEnc"), 0, "Forces Edit and Continue to be on for all eligible modules.") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_StressLogSize, W("StressLogSize"), 0, "Stress log size in bytes per thread.") RETAIL_CONFIG_STRING_INFO(UNSUPPORTED_StressLogFilename, W("StressLogFilename"), "Stress log filename for memory mapped stress log.") -CONFIG_DWORD_INFO_EX(INTERNAL_stressSynchronized, W("stressSynchronized"), 0, "Unknown if or where this is used; unless a test is specifically depending on this, it can be removed.", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_stressSynchronized, W("stressSynchronized"), 0, "Unknown if or where this is used; unless a test is specifically depending on this, it can be removed.") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TotalStressLogSize, W("TotalStressLogSize"), 0, "Total stress log size in bytes.") /// /// Thread Suspend @@ -633,7 +625,7 @@ CONFIG_DWORD_INFO(INTERNAL_OSR_HighId, W("OSR_HighId"), 10000000, "High end of e /// Profile Guided Opts /// #ifdef FEATURE_PGO -RETAIL_CONFIG_STRING_INFO_EX(INTERNAL_PGODataPath, W("PGODataPath"), "Read/Write PGO data from/to the indicated file.", CLRConfig::EEConfig_default) +RETAIL_CONFIG_STRING_INFO(INTERNAL_PGODataPath, W("PGODataPath"), "Read/Write PGO data from/to the indicated file.") RETAIL_CONFIG_DWORD_INFO(INTERNAL_ReadPGOData, W("ReadPGOData"), 0, "Read PGO data") RETAIL_CONFIG_DWORD_INFO(INTERNAL_WritePGOData, W("WritePGOData"), 0, "Write PGO data") RETAIL_CONFIG_DWORD_INFO(INTERNAL_TieredPGO, W("TieredPGO"), 0, "Instrument Tier0 code and make counts available to Tier1") @@ -654,14 +646,14 @@ CONFIG_DWORD_INFO(INTERNAL_TypeLoader_InjectInterfaceDuplicates, W("INTERNAL_Typ /// /// Virtual call stubs /// -CONFIG_DWORD_INFO_EX(INTERNAL_VirtualCallStubCollideMonoPct, W("VirtualCallStubCollideMonoPct"), 0, "Used only when STUB_LOGGING is defined, which by default is not.", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_VirtualCallStubCollideWritePct, W("VirtualCallStubCollideWritePct"), 100, "Used only when STUB_LOGGING is defined, which by default is not.", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_VirtualCallStubDumpLogCounter, W("VirtualCallStubDumpLogCounter"), 0, "Used only when STUB_LOGGING is defined, which by default is not.", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_VirtualCallStubDumpLogIncr, W("VirtualCallStubDumpLogIncr"), 0, "Used only when STUB_LOGGING is defined, which by default is not.", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_VirtualCallStubLogging, W("VirtualCallStubLogging"), 0, "Worth keeping, but should be moved into \"#ifdef STUB_LOGGING\" blocks. This goes for most (or all) of the stub logging infrastructure.", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_VirtualCallStubMissCount, W("VirtualCallStubMissCount"), 100, "Used only when STUB_LOGGING is defined, which by default is not.", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_VirtualCallStubResetCacheCounter, W("VirtualCallStubResetCacheCounter"), 0, "Used only when STUB_LOGGING is defined, which by default is not.", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_VirtualCallStubResetCacheIncr, W("VirtualCallStubResetCacheIncr"), 0, "Used only when STUB_LOGGING is defined, which by default is not.", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_VirtualCallStubCollideMonoPct, W("VirtualCallStubCollideMonoPct"), 0, "Used only when STUB_LOGGING is defined, which by default is not.") +CONFIG_DWORD_INFO(INTERNAL_VirtualCallStubCollideWritePct, W("VirtualCallStubCollideWritePct"), 100, "Used only when STUB_LOGGING is defined, which by default is not.") +CONFIG_DWORD_INFO(INTERNAL_VirtualCallStubDumpLogCounter, W("VirtualCallStubDumpLogCounter"), 0, "Used only when STUB_LOGGING is defined, which by default is not.") +CONFIG_DWORD_INFO(INTERNAL_VirtualCallStubDumpLogIncr, W("VirtualCallStubDumpLogIncr"), 0, "Used only when STUB_LOGGING is defined, which by default is not.") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_VirtualCallStubLogging, W("VirtualCallStubLogging"), 0, "Worth keeping, but should be moved into \"#ifdef STUB_LOGGING\" blocks. This goes for most (or all) of the stub logging infrastructure.") +CONFIG_DWORD_INFO(INTERNAL_VirtualCallStubMissCount, W("VirtualCallStubMissCount"), 100, "Used only when STUB_LOGGING is defined, which by default is not.") +CONFIG_DWORD_INFO(INTERNAL_VirtualCallStubResetCacheCounter, W("VirtualCallStubResetCacheCounter"), 0, "Used only when STUB_LOGGING is defined, which by default is not.") +CONFIG_DWORD_INFO(INTERNAL_VirtualCallStubResetCacheIncr, W("VirtualCallStubResetCacheIncr"), 0, "Used only when STUB_LOGGING is defined, which by default is not.") /// /// Watson @@ -679,15 +671,15 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_CreateDumpDiagnostics, W("CreateDumpDiagnostic /// /// Zap /// -RETAIL_CONFIG_STRING_INFO_EX(INTERNAL_ZapBBInstr, W("ZapBBInstr"), "", CLRConfig::EEConfig_default) +RETAIL_CONFIG_STRING_INFO(INTERNAL_ZapBBInstr, W("ZapBBInstr"), "") RETAIL_CONFIG_STRING_INFO(EXTERNAL_ZapBBInstrDir, W("ZapBBInstrDir"), "") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_ZapDisable, W("ZapDisable"), 0, "") -CONFIG_STRING_INFO_EX(INTERNAL_ZapExclude, W("ZapExclude"), "", CLRConfig::EEConfig_default) -CONFIG_STRING_INFO_EX(INTERNAL_ZapOnly, W("ZapOnly"), "", CLRConfig::EEConfig_default) +CONFIG_STRING_INFO(INTERNAL_ZapExclude, W("ZapExclude"), "") +CONFIG_STRING_INFO(INTERNAL_ZapOnly, W("ZapOnly"), "") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_ZapRequire, W("ZapRequire"), 0, "") RETAIL_CONFIG_STRING_INFO(EXTERNAL_ZapRequireExcludeList, W("ZapRequireExcludeList"), "") RETAIL_CONFIG_STRING_INFO(EXTERNAL_ZapRequireList, W("ZapRequireList"), "") -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_ZapSet, W("ZapSet"), "", CLRConfig::EEConfig_default) +RETAIL_CONFIG_STRING_INFO(EXTERNAL_ZapSet, W("ZapSet"), "") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_ReadyToRun, W("ReadyToRun"), 1, "Enable/disable use of ReadyToRun native code") // On by default for CoreCLR RETAIL_CONFIG_STRING_INFO(EXTERNAL_ReadyToRunExcludeList, W("ReadyToRunExcludeList"), "List of assemblies that cannot use Ready to Run images") @@ -707,7 +699,6 @@ RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_InteropValidatePinnedObjects, W("InteropVal RETAIL_CONFIG_DWORD_INFO(EXTERNAL_InteropLogArguments, W("InteropLogArguments"), 0, "Log all pinned arguments passed to an interop call") RETAIL_CONFIG_STRING_INFO(UNSUPPORTED_LogCCWRefCountChange, W("LogCCWRefCountChange"), "Outputs debug information and calls LogCCWRefCountChange_BREAKPOINT when AddRef or Release is called on a CCW.") RETAIL_CONFIG_DWORD_INFO(INTERNAL_EnableRCWCleanupOnSTAShutdown, W("EnableRCWCleanupOnSTAShutdown"), 0, "Performs RCW cleanup when STA shutdown is detected using IInitializeSpy in classic processes.") -RETAIL_CONFIG_DWORD_INFO(EXTERNAL_AllowDComReflection, W("AllowDComReflection"), 0, "Allows out of process DCOM clients to marshal blocked reflection types.") // // EventPipe @@ -731,14 +722,14 @@ RETAIL_CONFIG_STRING_INFO(INTERNAL_GCGenAnalysisCmd, W("GCGenAnalysisCmd"), "An // // Diagnostics Ports // -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_DOTNET_DefaultDiagnosticPortSuspend, W("DOTNET_DefaultDiagnosticPortSuspend"), 0, "This sets the deafult diagnostic port to suspend causing the runtime to pause during startup before major subsystems are started. Resume using the Diagnostics IPC ResumeStartup command on the default diagnostic port.", CLRConfig::DontPrependCOMPlus_); -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_DOTNET_DiagnosticPorts, W("DOTNET_DiagnosticPorts"), "A semicolon delimited list of additional Diagnostic Ports, where a Diagnostic Port is a NamedPipe path without '\\\\.\\pipe\\' on Windows or the full path of Unix Domain Socket on Linux/Unix followed by optional tags, e.g., ',connect,nosuspend;'", CLRConfig::DontPrependCOMPlus_); +RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_DOTNET_DefaultDiagnosticPortSuspend, W("DOTNET_DefaultDiagnosticPortSuspend"), 0, "This sets the deafult diagnostic port to suspend causing the runtime to pause during startup before major subsystems are started. Resume using the Diagnostics IPC ResumeStartup command on the default diagnostic port.", CLRConfig::LookupOptions::DontPrependCOMPlus_); +RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_DOTNET_DiagnosticPorts, W("DOTNET_DiagnosticPorts"), "A semicolon delimited list of additional Diagnostic Ports, where a Diagnostic Port is a NamedPipe path without '\\\\.\\pipe\\' on Windows or the full path of Unix Domain Socket on Linux/Unix followed by optional tags, e.g., ',connect,nosuspend;'", CLRConfig::LookupOptions::DontPrependCOMPlus_); // // LTTng // RETAIL_CONFIG_STRING_INFO(INTERNAL_LTTngConfig, W("LTTngConfig"), "Configuration for LTTng.") -RETAIL_CONFIG_DWORD_INFO(UNSUPORTED_LTTng, W("LTTng"), 1, "If COMPlus_LTTng is set to 0, this will prevent the LTTng library from being loaded at runtime") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_LTTng, W("LTTng"), 1, "If COMPlus_LTTng is set to 0, this will prevent the LTTng library from being loaded at runtime") #ifdef FEATURE_GDBJIT @@ -763,32 +754,32 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_GDBJitEmitDebugFrame, W("GDBJitEmitDebugFrame" // // DO NOT ADD ANY MORE CONFIG SWITCHES TO THIS SECTION! // ** -CONFIG_DWORD_INFO_EX(INTERNAL_ActivatePatchSkip, W("ActivatePatchSkip"), 0, "Allows an assert when ActivatePatchSkip is called", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_ActivatePatchSkip, W("ActivatePatchSkip"), 0, "Allows an assert when ActivatePatchSkip is called") CONFIG_DWORD_INFO(INTERNAL_AlwaysUseMetadataInterfaceMapLayout, W("AlwaysUseMetadataInterfaceMapLayout"), 0, "Used for debugging generic interface map layout.") CONFIG_DWORD_INFO(INTERNAL_AssertOnUnneededThis, W("AssertOnUnneededThis"), 0, "While the ConfigDWORD is unnecessary, the contained ASSERT should be kept. This may result in some work tracking down violating MethodDescCallSites.") -CONFIG_DWORD_INFO_EX(INTERNAL_AssertStacktrace, W("AssertStacktrace"), 1, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_clearNativeImageStress, W("clearNativeImageStress"), 0, "", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_AssertStacktrace, W("AssertStacktrace"), 1, "") +CONFIG_DWORD_INFO(INTERNAL_clearNativeImageStress, W("clearNativeImageStress"), 0, "") CONFIG_DWORD_INFO(INTERNAL_CPUFamily, W("CPUFamily"), 0xFFFFFFFF, "") CONFIG_DWORD_INFO(INTERNAL_CPUFeatures, W("CPUFeatures"), 0xFFFFFFFF, "") -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_DisableConfigCache, W("DisableConfigCache"), 0, "Used to disable the \"probabilistic\" config cache, which walks through the appropriate config registry keys on init and probabilistically keeps track of which exist.", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_DisableConfigCache, W("DisableConfigCache"), 0, "Used to disable the \"probabilistic\" config cache, which walks through the appropriate config registry keys on init and probabilistically keeps track of which exist.") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_DisableStackwalkCache, W("DisableStackwalkCache"), 0, "") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_DoubleArrayToLargeObjectHeap, W("DoubleArrayToLargeObjectHeap"), 0, "Controls double[] placement") CONFIG_STRING_INFO(INTERNAL_DumpOnClassLoad, W("DumpOnClassLoad"), "Dumps information about loaded class to log.") CONFIG_DWORD_INFO(INTERNAL_ExpandAllOnLoad, W("ExpandAllOnLoad"), 0, "") -CONFIG_DWORD_INFO_EX(INTERNAL_ForceRelocs, W("ForceRelocs"), 0, "", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_ForceRelocs, W("ForceRelocs"), 0, "") CONFIG_DWORD_INFO(INTERNAL_GenerateLongJumpDispatchStubRatio, W("GenerateLongJumpDispatchStubRatio"), 0, "Useful for testing VSD on AMD64") -CONFIG_DWORD_INFO_EX(INTERNAL_HashStack, W("HashStack"), 0, "", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_HashStack, W("HashStack"), 0, "") CONFIG_DWORD_INFO(INTERNAL_HostManagerConfig, W("HostManagerConfig"), (DWORD)-1, "") CONFIG_DWORD_INFO(INTERNAL_HostTestThreadAbort, W("HostTestThreadAbort"), 0, "") CONFIG_STRING_INFO(INTERNAL_InvokeHalt, W("InvokeHalt"), "Throws an assert when the given method is invoked through reflection.") CONFIG_DWORD_INFO(INTERNAL_MaxStubUnwindInfoSegmentSize, W("MaxStubUnwindInfoSegmentSize"), 0, "") CONFIG_DWORD_INFO(INTERNAL_MessageDebugOut, W("MessageDebugOut"), 0, "") -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_NativeImageRequire, W("NativeImageRequire"), 0, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_NestedEhOom, W("NestedEhOom"), 0, "", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_NativeImageRequire, W("NativeImageRequire"), 0, "") +CONFIG_DWORD_INFO(INTERNAL_NestedEhOom, W("NestedEhOom"), 0, "") #define INTERNAL_NoGuiOnAssert_Default 1 -RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_NoGuiOnAssert, W("NoGuiOnAssert"), INTERNAL_NoGuiOnAssert_Default, "", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_NoProcedureSplitting, W("NoProcedureSplitting"), 0, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_NoStringInterning, W("NoStringInterning"), 1, "Disallows string interning. I see no value in it anymore.", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(INTERNAL_NoGuiOnAssert, W("NoGuiOnAssert"), INTERNAL_NoGuiOnAssert_Default, "") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_NoProcedureSplitting, W("NoProcedureSplitting"), 0, "") +CONFIG_DWORD_INFO(INTERNAL_NoStringInterning, W("NoStringInterning"), 1, "Disallows string interning. I see no value in it anymore.") CONFIG_DWORD_INFO(INTERNAL_PauseOnLoad, W("PauseOnLoad"), 0, "Stops in SystemDomain::init. I think it can be removed.") CONFIG_DWORD_INFO(INTERNAL_PerfAllocsSizeThreshold, W("PerfAllocsSizeThreshold"), 0x3FFFFFFF, "Log facility LF_GCALLOC logs object allocations. This flag controls which ones also log stacktraces. Predates ClrProfiler.") CONFIG_DWORD_INFO(INTERNAL_PerfNumAllocsThreshold, W("PerfNumAllocsThreshold"), 0x3FFFFFFF, "Log facility LF_GCALLOC logs object allocations. This flag controls which ones also log stacktraces. Predates ClrProfiler.") @@ -797,18 +788,16 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_Prepopulate1, W("Prepopulate1"), 1, "") CONFIG_STRING_INFO(INTERNAL_PrestubGC, W("PrestubGC"), "") CONFIG_STRING_INFO(INTERNAL_PrestubHalt, W("PrestubHalt"), "") RETAIL_CONFIG_STRING_INFO(EXTERNAL_RestrictedGCStressExe, W("RestrictedGCStressExe"), "") -CONFIG_DWORD_INFO_EX(INTERNAL_ReturnSourceTypeForTesting, W("ReturnSourceTypeForTesting"), 0, "Allows returning the (internal only) source type of an IL to Native mapping for debugging purposes", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_RSStressLog, W("RSStressLog"), 0, "Allows turning on logging for RS startup", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_ReturnSourceTypeForTesting, W("ReturnSourceTypeForTesting"), 0, "Allows returning the (internal only) source type of an IL to Native mapping for debugging purposes") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_RSStressLog, W("RSStressLog"), 0, "Allows turning on logging for RS startup") CONFIG_DWORD_INFO(INTERNAL_SBDumpOnNewIndex, W("SBDumpOnNewIndex"), 0, "Used for Syncblock debugging. It's been a while since any of those have been used.") CONFIG_DWORD_INFO(INTERNAL_SBDumpOnResize, W("SBDumpOnResize"), 0, "Used for Syncblock debugging. It's been a while since any of those have been used.") CONFIG_DWORD_INFO(INTERNAL_SBDumpStyle, W("SBDumpStyle"), 0, "Used for Syncblock debugging. It's been a while since any of those have been used.") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_SleepOnExit, W("SleepOnExit"), 0, "Used for lrak detection. I'd say deprecated by umdh.") CONFIG_DWORD_INFO(INTERNAL_StubLinkerUnwindInfoVerificationOn, W("StubLinkerUnwindInfoVerificationOn"), 0, "") -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_SuccessExit, W("SuccessExit"), 0, "", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_SuccessExit, W("SuccessExit"), 0, "") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TestDataConsistency, W("TestDataConsistency"), FALSE, "Allows ensuring the left side is not holding locks (and may thus be in an inconsistent state) when inspection occurs") -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_ThreadGuardPages, W("ThreadGuardPages"), 0, "", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_Timeline, W("Timeline"), 0, "", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TotalStressLogSize, W("TotalStressLogSize"), 0, "Total stress log size in bytes.") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_ThreadGuardPages, W("ThreadGuardPages"), 0, "") #ifdef _DEBUG RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TraceWrap, W("TraceWrap"), 0, "") diff --git a/src/coreclr/inc/corhdr.h b/src/coreclr/inc/corhdr.h index a4c5e6d639bf..de3e95df31e0 100644 --- a/src/coreclr/inc/corhdr.h +++ b/src/coreclr/inc/corhdr.h @@ -16,18 +16,6 @@ #ifndef __CORHDR_H__ #define __CORHDR_H__ -#define FRAMEWORK_REGISTRY_KEY "Software\\Microsoft\\.NETFramework" -#define FRAMEWORK_REGISTRY_KEY_W W("Software\\Microsoft\\.NETFramework") - -// keys for HKCU -#ifdef HOST_64BIT -#define USER_FRAMEWORK_REGISTRY_KEY "Software\\Microsoft\\.NETFramework64" -#define USER_FRAMEWORK_REGISTRY_KEY_W W("Software\\Microsoft\\.NETFramework64") -#else -#define USER_FRAMEWORK_REGISTRY_KEY "Software\\Microsoft\\.NETFramework" -#define USER_FRAMEWORK_REGISTRY_KEY_W W("Software\\Microsoft\\.NETFramework") -#endif - #include #ifdef _MSC_VER diff --git a/src/coreclr/inc/utilcode.h b/src/coreclr/inc/utilcode.h index d2aea0041f7b..a3c446bb7478 100644 --- a/src/coreclr/inc/utilcode.h +++ b/src/coreclr/inc/utilcode.h @@ -1010,134 +1010,6 @@ void SplitPath(__in SString const &path, __inout_opt SString *fname, __inout_opt SString *ext); -#if !defined(NO_CLRCONFIG) - -//***************************************************************************** -// -// **** REGUTIL - Static helper functions for reading/writing to Windows registry. -// -//***************************************************************************** - - -class REGUTIL -{ -public: -//***************************************************************************** - - enum CORConfigLevel - { - COR_CONFIG_ENV = 0x01, - COR_CONFIG_USER = 0x02, - COR_CONFIG_MACHINE = 0x04, - - COR_CONFIG_REGISTRY = (COR_CONFIG_USER|COR_CONFIG_MACHINE), - COR_CONFIG_ALL = (COR_CONFIG_ENV|COR_CONFIG_USER|COR_CONFIG_MACHINE), - }; - - // - // NOTE: The following function is deprecated; use the CLRConfig class instead. - // To access a configuration value through CLRConfig, add an entry in file:../inc/CLRConfigValues.h. - // - static DWORD GetConfigDWORD_DontUse_( - LPCWSTR name, - DWORD defValue, - CORConfigLevel level = COR_CONFIG_ALL, - BOOL fPrependCOMPLUS = TRUE); - - // - // NOTE: The following function is deprecated; use the CLRConfig class instead. - // To access a configuration value through CLRConfig, add an entry in file:../inc/CLRConfigValues.h. - // - static HRESULT GetConfigDWORD_DontUse_( - LPCWSTR name, - DWORD defValue, - __out DWORD * result, - CORConfigLevel level = COR_CONFIG_ALL, - BOOL fPrependCOMPLUS = TRUE); - - static ULONGLONG GetConfigULONGLONG_DontUse_( - LPCWSTR name, - ULONGLONG defValue, - CORConfigLevel level = COR_CONFIG_ALL, - BOOL fPrependCOMPLUS = TRUE); - - // - // NOTE: The following function is deprecated; use the CLRConfig class instead. - // To access a configuration value through CLRConfig, add an entry in file:../inc/CLRConfigValues.h. - // - static DWORD GetConfigFlag_DontUse_( - LPCWSTR name, - DWORD bitToSet, - BOOL defValue = FALSE); - - // - // NOTE: The following function is deprecated; use the CLRConfig class instead. - // To access a configuration value through CLRConfig, add an entry in file:../inc/CLRConfigValues.h. - // - static LPWSTR GetConfigString_DontUse_( - LPCWSTR name, - BOOL fPrependCOMPLUS = TRUE, - CORConfigLevel level = COR_CONFIG_ALL, - BOOL fUsePerfCache = TRUE); - - static void FreeConfigString(__in __in_z LPWSTR name); - -private: - static LPWSTR EnvGetString(LPCWSTR name, BOOL fPrependCOMPLUS); - -private: -//***************************************************************************** -// Get either a DWORD or ULONGLONG. Always puts the result in a ULONGLONG that -// you can safely cast to a DWORD if fGetDWORD is TRUE. -//***************************************************************************** - static HRESULT GetConfigInteger( - LPCWSTR name, - ULONGLONG defValue, - __out ULONGLONG * result, - BOOL fGetDWORD = TRUE, - CORConfigLevel level = COR_CONFIG_ALL, - BOOL fPrependCOMPLUS = TRUE); -public: - - -//***************************************************************************** -// (Optional) Initialize the config registry cache -// (see ConfigCacheValueNameSeenPerhaps, below.) -//***************************************************************************** - static void InitOptionalConfigCache(); - -private: - - -//***************************************************************************** -// Return TRUE if the registry value name might have been seen in the registry -// at startup; -// return FALSE if the value was definitely not seen at startup. -// -// Perf Optimization for VSWhidbey:113373. -//***************************************************************************** - static BOOL RegCacheValueNameSeenPerhaps( - LPCWSTR name); -//***************************************************************************** -// Return TRUE if the environment variable name might have been seen at startup; -// return FALSE if the value was definitely not seen at startup. -//***************************************************************************** - static BOOL EnvCacheValueNameSeenPerhaps( - LPCWSTR name); - - static BOOL s_fUseRegCache; // Enable registry cache; if FALSE, CCVNSP - // always returns TRUE. - static BOOL s_fUseEnvCache; // Enable env cache. - - // Open the .NetFramework keys once and cache the handles - static HKEY s_hMachineFrameworkKey; - static HKEY s_hUserFrameworkKey; -}; - -#include "clrconfig.h" - -#endif // defined(NO_CLRCONFIG) - #include "ostype.h" #define CLRGetTickCount64() GetTickCount64() @@ -3741,10 +3613,10 @@ class MethodNamesList : public MethodNamesListBase } }; -#if !defined(NO_CLRCONFIG) +#include "clrconfig.h" /**************************************************************************/ -/* simple wrappers around the REGUTIL and MethodNameList routines that make +/* simple wrappers around the CLRConfig and MethodNameList routines that make the lookup lazy */ /* to be used as static variable - no constructor/destructor, assumes zero @@ -3835,8 +3707,6 @@ class ConfigMethodSet BYTE m_inited; }; -#endif // !defined(NO_CLRCONFIG) - //***************************************************************************** // Convert a pointer to a string into a GUID. //***************************************************************************** diff --git a/src/coreclr/md/debug_metadata.h b/src/coreclr/md/debug_metadata.h index 7e18e4affc0f..6dfb79931490 100644 --- a/src/coreclr/md/debug_metadata.h +++ b/src/coreclr/md/debug_metadata.h @@ -33,7 +33,7 @@ #pragma once -// Include for REGUTIL class used in Debug_ReportError +// Include for CLRConfig class used in Debug_ReportError #include // -------------------------------------------------------------------------------------- diff --git a/src/coreclr/utilcode/CMakeLists.txt b/src/coreclr/utilcode/CMakeLists.txt index c7c5861f129b..0f9606d93535 100644 --- a/src/coreclr/utilcode/CMakeLists.txt +++ b/src/coreclr/utilcode/CMakeLists.txt @@ -36,7 +36,6 @@ set(UTILCODE_COMMON_SOURCES corimage.cpp format1.cpp prettyprintsig.cpp - regutil.cpp sha1.cpp sigbuilder.cpp sigparser.cpp diff --git a/src/coreclr/utilcode/clrconfig.cpp b/src/coreclr/utilcode/clrconfig.cpp index 6c0f6eca6547..01dd0412e68c 100644 --- a/src/coreclr/utilcode/clrconfig.cpp +++ b/src/coreclr/utilcode/clrconfig.cpp @@ -4,18 +4,333 @@ // CLRConfig.cpp // -// -// Unified method of accessing configuration values from environment variables, -// registry and config file. See file:../inc/CLRConfigValues.h for details on how to add config values. -// -//***************************************************************************** - #include "stdafx.h" #include "clrconfig.h" +#include "sstring.h" +#include "ex.h" + +// Config prefixes +#define COMPLUS_PREFIX W("COMPlus_") +#define LEN_OF_COMPLUS_PREFIX 8 + +using ConfigDWORDInfo = CLRConfig::ConfigDWORDInfo; +using ConfigStringInfo = CLRConfig::ConfigStringInfo; +using LookupOptions = CLRConfig::LookupOptions; + +namespace +{ + // + // ProbabilisticNameSet: + // + // (Used by ConfigCache, below. If used elsewhere, might justify + // promotion to a standalone header file.) + // + // Represent a set of names in a small, fixed amount of storage. + // We turn a name into a small integer, then add the integer to a bitvector. + // An old trick we used in VC++4 minimal rebuild. + // + // For best results, the number of elements should be a fraction of + // the total number of bits in 'bits'. + // + // Note, only the const methods are thread-safe. + // Callers are responsible for providing their own synchronization when + // constructing and Add'ing names to the set. + // + class ProbabilisticNameSet { + public: + ProbabilisticNameSet() + { + WRAPPER_NO_CONTRACT; + + memset(bits, 0, sizeof(bits)); + } + + // Add a name to the set. + // + void Add(LPCWSTR name) + { + WRAPPER_NO_CONTRACT; + + unsigned i, mask; + GetBitIndex(name, 0, &i, &mask); + bits[i] |= mask; + } + + void Add(LPCWSTR name, DWORD count) + { + WRAPPER_NO_CONTRACT; + + unsigned i, mask; + GetBitIndex(name, count, &i, &mask); + bits[i] |= mask; + } + + // Return TRUE if a name *may have* been added to the set; + // return FALSE if the name *definitely* was NOT ever added to the set. + // + BOOL MayContain(LPCWSTR name) const + { + WRAPPER_NO_CONTRACT; + + unsigned i, mask; + GetBitIndex(name, 0, &i, &mask); + return !!(bits[i] & mask); + } + + private: + static const unsigned cbitSet = 256U; + static const unsigned cbitWord = 8U*sizeof(unsigned); + unsigned bits[cbitSet/cbitWord]; + + // Return the word index and bit mask corresponding to the bitvector member + // addressed by the *case-insensitive* hash of the given name. + // + void GetBitIndex(LPCWSTR name, DWORD count, unsigned* pi, unsigned* pmask) const + { + LIMITED_METHOD_CONTRACT; + unsigned hash; + if (count > 0) + hash = HashiStringNKnownLower80(name, count) % cbitSet; + else + hash = HashiStringKnownLower80(name) % cbitSet; + *pi = hash / cbitWord; + *pmask = (1U << (hash % cbitWord)); + } + }; + + BOOL s_fUseEnvCache = FALSE; + ProbabilisticNameSet s_EnvNames; // set of environment value names seen + + BOOL EnvCacheValueNameSeenPerhaps(LPCWSTR name) + { + WRAPPER_NO_CONTRACT; + + return !s_fUseEnvCache + || s_EnvNames.MayContain(name); + } + + //***************************************************************************** + // Reads from the environment setting + //***************************************************************************** + LPWSTR EnvGetString(LPCWSTR name, bool fPrependCOMPLUS) + { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + CANNOT_TAKE_LOCK; + } + CONTRACTL_END; + + WCHAR buff[64]; + + if(wcslen(name) > (size_t)(64 - 1 - (fPrependCOMPLUS ? LEN_OF_COMPLUS_PREFIX : 0))) + { + return NULL; + } + + if (fPrependCOMPLUS) + { + if (!EnvCacheValueNameSeenPerhaps(name)) + return NULL; + + wcscpy_s(buff, _countof(buff), COMPLUS_PREFIX); + } + else + { + *buff = 0; + } + + wcscat_s(buff, _countof(buff), name); + + FAULT_NOT_FATAL(); // We don't report OOM errors here, we return a default value. + + NewArrayHolder ret = NULL; + HRESULT hr = S_OK; + DWORD Len; + EX_TRY + { + PathString temp; + + Len = WszGetEnvironmentVariable(buff, temp); + if (Len != 0) + { + ret = temp.GetCopyOfUnicodeString(); + } + + } + EX_CATCH_HRESULT(hr); + + if (hr != S_OK) + { + SetLastError(hr); + } + + if(ret != NULL) + return ret.Extract(); + + return NULL; + } + + HRESULT GetConfigDWORD( + LPCWSTR name, + DWORD defValue, + __out DWORD *result, + bool fPrependCOMPLUS) + { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + CANNOT_TAKE_LOCK; + } + CONTRACTL_END; + + SUPPORTS_DAC_HOST_ONLY; + + FAULT_NOT_FATAL(); // We don't report OOM errors here, we return a default value. + + NewArrayHolder val = EnvGetString(name, fPrependCOMPLUS); + if (val != NULL) + { + errno = 0; + LPWSTR endPtr; + DWORD configMaybe = wcstoul(val, &endPtr, 16); // treat it has hex + BOOL fSuccess = ((errno != ERANGE) && (endPtr != val)); + if (fSuccess) + { + *result = configMaybe; + return (S_OK); + } + } + + *result = defValue; + return (E_FAIL); + } + + LPWSTR GetConfigString( + LPCWSTR name, + bool fPrependCOMPLUS) + { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + } + CONTRACTL_END; + + NewArrayHolder ret(NULL); + + FAULT_NOT_FATAL(); // We don't report OOM errors here, we return a default value. + + ret = EnvGetString(name, fPrependCOMPLUS); + if (ret != NULL) + { + if (*ret != W('\0')) + { + ret.SuppressRelease(); + return(ret); + } + ret.Clear(); + } + + return NULL; + } + + bool CheckLookupOption(const ConfigDWORDInfo & info, LookupOptions option) + { + LIMITED_METHOD_CONTRACT; + return ((info.options & option) == option); + } + + bool CheckLookupOption(const ConfigStringInfo & info, LookupOptions option) + { + LIMITED_METHOD_CONTRACT; + return ((info.options & option) == option); + } -#ifndef ERANGE -#define ERANGE 34 -#endif + bool CheckLookupOption(LookupOptions infoOptions, LookupOptions optionToCheck) + { + LIMITED_METHOD_CONTRACT; + return ((infoOptions & optionToCheck) == optionToCheck); + } + + //--------------------------------------------------------------------------------------- + // + // Given an input string, returns a newly-allocated string equal to the input but with + // leading and trailing whitespace trimmed off. If input is already trimmed, or if + // trimming would result in an empty string, this function sets the output string to NULL + // + // Caller must free *pwszTrimmed if non-NULL + // + // Arguments: + // * wszOrig - String to trim + // * pwszTrimmed - [out]: On return, points to newly allocated, trimmed string (or + // NULL) + // + // Return Value: + // HRESULT indicating success or failure. + // + HRESULT TrimWhiteSpace(LPCWSTR wszOrig, __deref_out_z LPWSTR * pwszTrimmed) + { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + _ASSERTE(wszOrig != NULL); + _ASSERTE(pwszTrimmed != NULL); + + // In case we return early, set [out] to NULL by default + *pwszTrimmed = NULL; + + // Get pointers into internal string that show where to do the trimming. + size_t cchOrig = wcslen(wszOrig); + if (!FitsIn(cchOrig)) + return COR_E_OVERFLOW; + DWORD cchAfterTrim = (DWORD) cchOrig; + LPCWSTR wszAfterTrim = wszOrig; + ::TrimWhiteSpace(&wszAfterTrim, &cchAfterTrim); + + // Is input string already trimmed? If so, save an allocation and just return. + if ((wszOrig == wszAfterTrim) && (cchOrig == cchAfterTrim)) + { + // Yup, just return success + return S_OK; + } + + if (cchAfterTrim == 0) + { + // After trimming, there's nothing left, so just return NULL + return S_OK; + } + + // Create a new buffer to hold a copy of the trimmed string. Caller will be + // responsible for this buffer if we return it. + NewArrayHolder wszTrimmedCopy(new (nothrow) WCHAR[cchAfterTrim + 1]); + if (wszTrimmedCopy == NULL) + { + return E_OUTOFMEMORY; + } + + errno_t err = wcsncpy_s(wszTrimmedCopy, cchAfterTrim + 1, wszAfterTrim, cchAfterTrim); + if (err != 0) + { + return E_FAIL; + } + + // Successfully made a copy of the trimmed string. Return it. Caller will be responsible for + // deleting it. + wszTrimmedCopy.SuppressRelease(); + *pwszTrimmed = wszTrimmedCopy; + return S_OK; + } +} // // Creating structs using the macro table in CLRConfigValues.h @@ -23,13 +338,13 @@ // These macros intialize ConfigDWORDInfo structs. #define RETAIL_CONFIG_DWORD_INFO(symbol, name, defaultValue, description) \ - const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, CLRConfig::EEConfig_default}; + const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, CLRConfig::LookupOptions::Default}; #define RETAIL_CONFIG_DWORD_INFO_EX(symbol, name, defaultValue, description, lookupOptions) \ const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, lookupOptions}; // These macros intialize ConfigStringInfo structs. #define RETAIL_CONFIG_STRING_INFO(symbol, name, description) \ - const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, CLRConfig::EEConfig_default}; + const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, CLRConfig::LookupOptions::Default}; #define RETAIL_CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions) \ const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, lookupOptions}; // @@ -37,11 +352,11 @@ // #ifdef _DEBUG #define CONFIG_DWORD_INFO(symbol, name, defaultValue, description) \ - const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, CLRConfig::EEConfig_default}; + const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, CLRConfig::LookupOptions::Default}; #define CONFIG_DWORD_INFO_EX(symbol, name, defaultValue, description, lookupOptions) \ const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, lookupOptions}; #define CONFIG_STRING_INFO(symbol, name, description) \ - const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, CLRConfig::EEConfig_default}; + const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, CLRConfig::LookupOptions::Default}; #define CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions) \ const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, lookupOptions}; #else @@ -63,7 +378,6 @@ #undef CONFIG_DWORD_INFO_EX #undef CONFIG_STRING_INFO_EX - // // Look up a DWORD config value. // @@ -88,14 +402,9 @@ DWORD CLRConfig::GetConfigValue(const ConfigDWORDInfo & info, /* [Out] */ bool * _ASSERTE (isDefault != nullptr); - // - // Set up REGUTIL options. - // - REGUTIL::CORConfigLevel level = GetConfigLevel(info.options); - BOOL prependCOMPlus = !CheckLookupOption(info, DontPrependCOMPlus_); - + bool prependCOMPlus = !CheckLookupOption(info, LookupOptions::DontPrependCOMPlus_); DWORD resultMaybe; - HRESULT hr = REGUTIL::GetConfigDWORD_DontUse_(info.name, info.defaultValue, &resultMaybe, level, prependCOMPlus); + HRESULT hr = GetConfigDWORD(info.name, info.defaultValue, &resultMaybe, prependCOMPlus); // Ignore the default value even if it's set explicitly. if (resultMaybe != info.defaultValue) @@ -195,16 +504,10 @@ HRESULT CLRConfig::GetConfigValue(const ConfigStringInfo & info, __deref_out_z L LPWSTR result = NULL; + bool prependCOMPlus = !CheckLookupOption(info, LookupOptions::DontPrependCOMPlus_); + result = GetConfigString(info.name, prependCOMPlus); - // - // Set up REGUTIL options. - // - REGUTIL::CORConfigLevel level = GetConfigLevel(info.options); - BOOL prependCOMPlus = !CheckLookupOption(info, DontPrependCOMPlus_); - - result = REGUTIL::GetConfigString_DontUse_(info.name, prependCOMPlus, level); - - if ((result != NULL) && CheckLookupOption(info, TrimWhiteSpaceFromStringValue)) + if ((result != NULL) && CheckLookupOption(info, LookupOptions::TrimWhiteSpaceFromStringValue)) { // If this fails, result remains untouched, so we'll just return the untrimmed // value. @@ -239,46 +542,44 @@ BOOL CLRConfig::IsConfigOptionSpecified(LPCWSTR name) } CONTRACTL_END; - // Check REGUTIL, both with and without the COMPlus_ prefix { LPWSTR result = NULL; - result = REGUTIL::GetConfigString_DontUse_(name, TRUE); + result = GetConfigString(name, true /* fPrependCOMPLUS */); if (result != NULL) { FreeConfigString(result); return TRUE; } - result = REGUTIL::GetConfigString_DontUse_(name, FALSE); + result = GetConfigString(name, false /* fPrependCOMPLUS */); if (result != NULL) { FreeConfigString(result); return TRUE; } - } return FALSE; } -//--------------------------------------------------------------------------------------- // -// Given an input string, returns a newly-allocated string equal to the input but with -// leading and trailing whitespace trimmed off. If input is already trimmed, or if -// trimming would result in an empty string, this function sets the output string to NULL -// -// Caller must free *pwszTrimmed if non-NULL +// Deallocation function for code:CLRConfig::FreeConfigString // -// Arguments: -// * wszOrig - String to trim -// * pwszTrimmed - [out]: On return, points to newly allocated, trimmed string (or -// NULL) +// static +void CLRConfig::FreeConfigString(__in_z LPWSTR str) +{ + LIMITED_METHOD_CONTRACT; + + // See EnvGetString(). + delete [] str; +} + // -// Return Value: -// HRESULT indicating success or failure. +// Initialize the internal cache faster lookup. // -HRESULT CLRConfig::TrimWhiteSpace(LPCWSTR wszOrig, __deref_out_z LPWSTR * pwszTrimmed) +// static +void CLRConfig::InitCache() { CONTRACTL { @@ -287,77 +588,49 @@ HRESULT CLRConfig::TrimWhiteSpace(LPCWSTR wszOrig, __deref_out_z LPWSTR * pwszTr } CONTRACTL_END; - _ASSERTE(wszOrig != NULL); - _ASSERTE(pwszTrimmed != NULL); - - // In case we return early, set [out] to NULL by default - *pwszTrimmed = NULL; + // Check if caching is disabled. + if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_DisableConfigCache) != 0) + return; - // Get pointers into internal string that show where to do the trimming. - size_t cchOrig = wcslen(wszOrig); - if (!FitsIn(cchOrig)) - return COR_E_OVERFLOW; - DWORD cchAfterTrim = (DWORD) cchOrig; - LPCWSTR wszAfterTrim = wszOrig; - ::TrimWhiteSpace(&wszAfterTrim, &cchAfterTrim); - - // Is input string already trimmed? If so, save an allocation and just return. - if ((wszOrig == wszAfterTrim) && (cchOrig == cchAfterTrim)) - { - // Yup, just return success - return S_OK; - } - - if (cchAfterTrim == 0) +#ifdef TARGET_WINDOWS + // Create a cache of environment variables + WCHAR* wszStrings = WszGetEnvironmentStrings(); + if (wszStrings != NULL) { - // After trimming, there's nothing left, so just return NULL - return S_OK; - } + // GetEnvironmentStrings returns pointer to a null terminated block containing + // null terminated strings + for(WCHAR *wszCurr = wszStrings; *wszCurr; wszCurr++) + { + WCHAR wch = towlower(*wszCurr); + + // Lets only cache env variables with the COMPlus prefix only + if (wch == W('c')) + { + WCHAR *wszName = wszCurr; + + // Look for the separator between name and value + while (*wszCurr && *wszCurr != W('=')) + wszCurr++; + + if (*wszCurr == W('=')) + { + // Check the prefix + if(!SString::_wcsnicmp(wszName, COMPLUS_PREFIX, LEN_OF_COMPLUS_PREFIX)) + { + wszName += LEN_OF_COMPLUS_PREFIX; + s_EnvNames.Add(wszName, (DWORD) (wszCurr - wszName)); + } + } + + } + // Look for current string termination + while (*wszCurr) + wszCurr++; - // Create a new buffer to hold a copy of the trimmed string. Caller will be - // responsible for this buffer if we return it. - NewArrayHolder wszTrimmedCopy(new (nothrow) WCHAR[cchAfterTrim + 1]); - if (wszTrimmedCopy == NULL) - { - return E_OUTOFMEMORY; - } + } - errno_t err = wcsncpy_s(wszTrimmedCopy, cchAfterTrim + 1, wszAfterTrim, cchAfterTrim); - if (err != 0) - { - return E_FAIL; + WszFreeEnvironmentStrings(wszStrings); + s_fUseEnvCache = TRUE; } - - // Successfully made a copy of the trimmed string. Return it. Caller will be responsible for - // deleting it. - wszTrimmedCopy.SuppressRelease(); - *pwszTrimmed = wszTrimmedCopy; - return S_OK; -} - - -// -// Deallocation function for code:CLRConfig::FreeConfigString -// -void CLRConfig::FreeConfigString(__in_z LPWSTR str) -{ - LIMITED_METHOD_CONTRACT; - - delete [] str; -} - -// -// Helper method to translate LookupOptions to REGUTIL::CORConfigLevel. -// -//static -REGUTIL::CORConfigLevel CLRConfig::GetConfigLevel(LookupOptions options) -{ - LIMITED_METHOD_CONTRACT; - - REGUTIL::CORConfigLevel level = (REGUTIL::CORConfigLevel) 0; - - if(CheckLookupOption(options, IgnoreEnv) == FALSE) - level = static_cast(level | REGUTIL::COR_CONFIG_ENV); - - return static_cast(level | REGUTIL::COR_CONFIG_USER | REGUTIL::COR_CONFIG_MACHINE); +#endif // TARGET_WINDOWS } diff --git a/src/coreclr/utilcode/regutil.cpp b/src/coreclr/utilcode/regutil.cpp deleted file mode 100644 index fcfbe6eb7c7c..000000000000 --- a/src/coreclr/utilcode/regutil.cpp +++ /dev/null @@ -1,759 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -//***************************************************************************** -// regutil.cpp -// - -// -// This module contains a set of functions that can be used to access the -// registry. -// -//***************************************************************************** - - -#include "stdafx.h" -#include "utilcode.h" -#include "mscoree.h" -#include "sstring.h" -#include "ex.h" - -#define COMPLUS_PREFIX W("COMPlus_") -#define LEN_OF_COMPLUS_PREFIX 8 - -#if (!defined(FEATURE_UTILCODE_NO_DEPENDENCIES) || defined(DEBUG)) && !defined(TARGET_UNIX) -#define ALLOW_REGISTRY -#endif - -#undef WszRegCreateKeyEx -#undef WszRegOpenKeyEx -#undef WszRegOpenKey -#define WszRegCreateKeyEx RegCreateKeyExW -#define WszRegOpenKeyEx RegOpenKeyExW -#define WszRegOpenKey(hKey, wszSubKey, phkRes) RegOpenKeyExW(hKey, wszSubKey, 0, KEY_ALL_ACCESS, phkRes) - -//***************************************************************************** -// Reads from the environment setting -//***************************************************************************** -LPWSTR REGUTIL::EnvGetString(LPCWSTR name, BOOL fPrependCOMPLUS) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - FORBID_FAULT; - CANNOT_TAKE_LOCK; - } - CONTRACTL_END; - - WCHAR buff[64]; - - if(wcslen(name) > (size_t)(64 - 1 - (fPrependCOMPLUS ? LEN_OF_COMPLUS_PREFIX : 0))) - { - return NULL; - } - - if (fPrependCOMPLUS) - { -#ifdef ALLOW_REGISTRY - if (!EnvCacheValueNameSeenPerhaps(name)) - return NULL; -#endif // ALLOW_REGISTRY - wcscpy_s(buff, _countof(buff), COMPLUS_PREFIX); - } - else - { - *buff = 0; - } - - wcscat_s(buff, _countof(buff), name); - - FAULT_NOT_FATAL(); // We don't report OOM errors here, we return a default value. - - - NewArrayHolder ret = NULL; - HRESULT hr = S_OK; - DWORD Len; - EX_TRY - { - PathString temp; - - Len = WszGetEnvironmentVariable(buff, temp); - if (Len != 0) - { - ret = temp.GetCopyOfUnicodeString(); - } - - } - EX_CATCH_HRESULT(hr); - - if (hr != S_OK) - { - SetLastError(hr); - } - - if(ret != NULL) - { - return ret.Extract(); - } - - return NULL; - - -} - -//***************************************************************************** -// Reads a DWORD from the COR configuration according to the level specified -// Returns back defValue if the key cannot be found -//***************************************************************************** -DWORD REGUTIL::GetConfigDWORD_DontUse_(LPCWSTR name, DWORD defValue, CORConfigLevel level, BOOL fPrependCOMPLUS) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - FORBID_FAULT; - CANNOT_TAKE_LOCK; - } - CONTRACTL_END; - - SUPPORTS_DAC_HOST_ONLY; - - ULONGLONG result; - GetConfigInteger(name, defValue, &result, TRUE, level, fPrependCOMPLUS); - - return (DWORD)result; -} - -#define uniwcst(val, endptr, base) (fGetDWORD ? wcstoul(val, endptr, base) : _wcstoui64(val, endptr, base)) - -// -// Look up a dword config value, and write the result to the DWORD passed in by reference. -// -// Return value: -// * E_FAIL if the value is not found. (result is assigned the default value) -// * S_OK if the value is found. (result is assigned the value that was found) -// -// Arguments: -// * info - see file:../inc/CLRConfig.h for details -// * result - Pointer to the output DWORD. -// -// static -HRESULT REGUTIL::GetConfigDWORD_DontUse_(LPCWSTR name, DWORD defValue, __out DWORD * result, CORConfigLevel level, BOOL fPrependCOMPLUS) -{ - ULONGLONG ullResult; - HRESULT hr = GetConfigInteger(name, defValue, &ullResult, TRUE, level, fPrependCOMPLUS); - *result = (DWORD)ullResult; - return hr; -} - -ULONGLONG REGUTIL::GetConfigULONGLONG_DontUse_(LPCWSTR name, ULONGLONG defValue, CORConfigLevel level, BOOL fPrependCOMPLUS) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - FORBID_FAULT; - CANNOT_TAKE_LOCK; - } - CONTRACTL_END; - - SUPPORTS_DAC_HOST_ONLY; - - ULONGLONG result; - GetConfigInteger(name, defValue, &result, FALSE, level, fPrependCOMPLUS); - - return result; -} - -// This function should really be refactored to return the string from the environment and let the caller decide -// what to convert it to; and return the buffer read from the reg call. -// Note for PAL: right now PAL does not have a _wcstoui64 API, so I am temporarily reading in all numbers as -// a 32-bit number. When we have the _wcstoui64 API on MAC we will use uniwcst instead of wcstoul. -HRESULT REGUTIL::GetConfigInteger(LPCWSTR name, ULONGLONG defValue, __out ULONGLONG * result, BOOL fGetDWORD, CORConfigLevel level, BOOL fPrependCOMPLUS) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - FORBID_FAULT; - CANNOT_TAKE_LOCK; - } - CONTRACTL_END; - - SUPPORTS_DAC_HOST_ONLY; - - ULONGLONG rtn; - ULONGLONG ret = 0; - DWORD type = 0; - DWORD size = 4; - - FAULT_NOT_FATAL(); // We don't report OOM errors here, we return a default value. - - if (level & COR_CONFIG_ENV) - { - WCHAR* val = EnvGetString(name, fPrependCOMPLUS); // try getting it from the environement first - if (val != 0) { - errno = 0; - LPWSTR endPtr; - rtn = uniwcst(val, &endPtr, 16); // treat it has hex - BOOL fSuccess = ((errno != ERANGE) && (endPtr != val)); - delete[] val; - - if (fSuccess) // success - { - *result = rtn; - return (S_OK); - } - } - } - - // Early out if no registry access, simplifies following code. - // - if (!(level & COR_CONFIG_REGISTRY)) - { - *result = defValue; - return (E_FAIL); - } - -#ifdef ALLOW_REGISTRY - // Probe the config cache to see if there is any point - // probing the registry; if not, don't bother. - // - if (!RegCacheValueNameSeenPerhaps(name)) - { - *result = defValue; - return (E_FAIL); - } -#endif // ALLOW_REGISTRY - - if (level & COR_CONFIG_USER) - { -#ifdef ALLOW_REGISTRY - { - LONG retVal = ERROR_SUCCESS; - BOOL bCloseHandle = FALSE; - HKEY userKey = s_hUserFrameworkKey; - - if (userKey == INVALID_HANDLE_VALUE) - { - retVal = WszRegOpenKeyEx(HKEY_CURRENT_USER, FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &userKey); - bCloseHandle = TRUE; - } - - if (retVal == ERROR_SUCCESS) - { - rtn = WszRegQueryValueEx(userKey, name, 0, &type, (LPBYTE)&ret, &size); - - if (bCloseHandle) - VERIFY(!RegCloseKey(userKey)); - - if (rtn == ERROR_SUCCESS && (type == REG_DWORD || (!fGetDWORD && type == REG_QWORD))) - { - *result = ret; - return (S_OK); - } - } - } -#endif // ALLOW_REGISTRY - } - - if (level & COR_CONFIG_MACHINE) - { -#ifdef ALLOW_REGISTRY - { - LONG retVal = ERROR_SUCCESS; - BOOL bCloseHandle = FALSE; - HKEY machineKey = s_hMachineFrameworkKey; - - if (machineKey == INVALID_HANDLE_VALUE) - { - retVal = WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &machineKey); - bCloseHandle = TRUE; - } - - if (retVal == ERROR_SUCCESS) - { - rtn = WszRegQueryValueEx(machineKey, name, 0, &type, (LPBYTE)&ret, &size); - - if (bCloseHandle) - VERIFY(!RegCloseKey(machineKey)); - - if (rtn == ERROR_SUCCESS && (type == REG_DWORD || (!fGetDWORD && type == REG_QWORD))) - { - *result = ret; - return (S_OK); - } - } - } -#endif // ALLOW_REGISTRY - } - - *result = defValue; - return (E_FAIL); -} - -//***************************************************************************** -// Reads a string from the COR configuration according to the level specified -// The caller is responsible for deallocating the returned string by -// calling code:REGUTIL::FreeConfigString or using a code:ConfigStringHolder -//***************************************************************************** - -LPWSTR REGUTIL::GetConfigString_DontUse_(LPCWSTR name, BOOL fPrependCOMPLUS, CORConfigLevel level, BOOL fUsePerfCache) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - FORBID_FAULT; - } - CONTRACTL_END; - -#ifdef ALLOW_REGISTRY - HRESULT lResult; - RegKeyHolder userKey = NULL; - RegKeyHolder machineKey = NULL; - RegKeyHolder fusionKey = NULL; - DWORD type; - DWORD size; -#endif // ALLOW_REGISTRY - NewArrayHolder ret(NULL); - - FAULT_NOT_FATAL(); // We don't report OOM errors here, we return a default value. - - if (level & COR_CONFIG_ENV) - { - ret = EnvGetString(name, fPrependCOMPLUS); // try getting it from the environement first - if (ret != 0) { - if (*ret != 0) - { - ret.SuppressRelease(); - return(ret); - } - ret.Clear(); - } - } - - // Early out if no registry access, simplifies following code. - // - if (!(level & COR_CONFIG_REGISTRY)) - { - return(ret); - } - -#ifdef ALLOW_REGISTRY - // Probe the config cache to see if there is any point - // probing the registry; if not, don't bother. - // - if (fUsePerfCache && !RegCacheValueNameSeenPerhaps(name)) - return ret; -#endif // ALLOW_REGISTRY - - if (level & COR_CONFIG_USER) - { -#ifdef ALLOW_REGISTRY - BOOL bUsingCachedKey = FALSE; - - if (s_hUserFrameworkKey != INVALID_HANDLE_VALUE) - { - bUsingCachedKey = TRUE; - userKey = s_hUserFrameworkKey; - } - - if (bUsingCachedKey || WszRegOpenKeyEx(HKEY_CURRENT_USER, FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &userKey) == ERROR_SUCCESS) - { - BOOL bReturn = FALSE; - if (WszRegQueryValueEx(userKey, name, 0, &type, 0, &size) == ERROR_SUCCESS && - type == REG_SZ) - { - ret = (LPWSTR) new (nothrow) BYTE [size]; - if (ret) - { - ret[0] = W('\0'); - lResult = WszRegQueryValueEx(userKey, name, 0, 0, (LPBYTE) ret.GetValue(), &size); - _ASSERTE(lResult == ERROR_SUCCESS); - { - ret.SuppressRelease(); - } - } - bReturn = TRUE; - } - - if (bUsingCachedKey) - userKey.SuppressRelease(); - - if (bReturn) - return ret; - } - -#endif // ALLOW_REGISTRY - } - - if (level & COR_CONFIG_MACHINE) - { -#ifdef ALLOW_REGISTRY - BOOL bUsingCachedKey = FALSE; - - if (s_hMachineFrameworkKey != INVALID_HANDLE_VALUE) - { - bUsingCachedKey = TRUE; - machineKey = s_hMachineFrameworkKey; - } - - if (bUsingCachedKey || WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &machineKey) == ERROR_SUCCESS) - { - BOOL bReturn = FALSE; - if (WszRegQueryValueEx(machineKey, name, 0, &type, 0, &size) == ERROR_SUCCESS && - type == REG_SZ) - { - ret = (LPWSTR) new (nothrow) BYTE [size]; - if (ret) - { - ret[0] = W('\0'); - lResult = WszRegQueryValueEx(machineKey, name, 0, 0, (LPBYTE) ret.GetValue(), &size); - _ASSERTE(lResult == ERROR_SUCCESS); - { - ret.SuppressRelease(); - } - } - bReturn = TRUE; - } - - if (bUsingCachedKey) - machineKey.SuppressRelease(); - - if (bReturn) - return ret; - } - -#endif // ALLOW_REGISTRY - } - - return NULL; -} - -//***************************************************************************** -// Deallocation function for code:REGUTIL::GetConfigString_DontUse_ -// -// Notes: -// Use a code:ConfigStringHolder to automatically call this. -//***************************************************************************** -void REGUTIL::FreeConfigString(__in_z LPWSTR str) -{ - LIMITED_METHOD_CONTRACT; - - delete [] str; -} - -//***************************************************************************** -// Reads a BIT flag from the COR configuration according to the level specified -// Returns back defValue if the key cannot be found -//***************************************************************************** -DWORD REGUTIL::GetConfigFlag_DontUse_(LPCWSTR name, DWORD bitToSet, BOOL defValue) -{ - WRAPPER_NO_CONTRACT; - - return(GetConfigDWORD_DontUse_(name, defValue) != 0 ? bitToSet : 0); -} - - -#ifdef ALLOW_REGISTRY - - - -// -// ProbabilisticNameSet: -// -// (Used by ConfigCache, below. If used elsewhere, might justify -// promotion to a standalone header file.) -// -// Represent a set of names in a small, fixed amount of storage. -// We turn a name into a small integer, then add the integer to a bitvector. -// An old trick we used in VC++4 minimal rebuild. -// -// For best results, the number of elements should be a fraction of -// the total number of bits in 'bits'. -// -// Note, only the const methods are thread-safe. -// Callers are responsible for providing their own synchronization when -// constructing and Add'ing names to the set. -// -class ProbabilisticNameSet { -public: - ProbabilisticNameSet() - { - WRAPPER_NO_CONTRACT; - - memset(bits, 0, sizeof(bits)); - } - - // Add a name to the set. - // - void Add(LPCWSTR name) - { - WRAPPER_NO_CONTRACT; - - unsigned i, mask; - GetBitIndex(name, 0, &i, &mask); - bits[i] |= mask; - } - - void Add(LPCWSTR name, DWORD count) - { - WRAPPER_NO_CONTRACT; - - unsigned i, mask; - GetBitIndex(name, count, &i, &mask); - bits[i] |= mask; - } - - // Return TRUE if a name *may have* been added to the set; - // return FALSE if the name *definitely* was NOT ever added to the set. - // - BOOL MayContain(LPCWSTR name) const - { - WRAPPER_NO_CONTRACT; - - unsigned i, mask; - GetBitIndex(name, 0, &i, &mask); - return !!(bits[i] & mask); - } - -private: - static const unsigned cbitSet = 256U; - static const unsigned cbitWord = 8U*sizeof(unsigned); - unsigned bits[cbitSet/cbitWord]; - - // Return the word index and bit mask corresponding to the bitvector member - // addressed by the *case-insensitive* hash of the given name. - // - void GetBitIndex(LPCWSTR name, DWORD count, unsigned* pi, unsigned* pmask) const - { - LIMITED_METHOD_CONTRACT; - unsigned hash; - if (count > 0) - hash = HashiStringNKnownLower80(name, count) % cbitSet; - else - hash = HashiStringKnownLower80(name) % cbitSet; - *pi = hash / cbitWord; - *pmask = (1U << (hash % cbitWord)); - } - -}; - - -// From the Win32 SDK docs: -// Registry Element Size Limits -// ... -// The maximum size of a value name is as follows: -// Windows Server 2003 and Windows XP: 16,383 characters -// Windows 2000: 260 ANSI characters or 16,383 Unicode characters. -// Windows Me/98/95: 255 characters -// Despite that, we only cache value names of 80 characters or less -- -// longer names don't make sense as configuration settings names. -// -static const unsigned cchRegValueNameMax = 80; - -BOOL REGUTIL::s_fUseRegCache = FALSE; -BOOL REGUTIL::s_fUseEnvCache = FALSE; -HKEY REGUTIL::s_hMachineFrameworkKey = (HKEY) INVALID_HANDLE_VALUE; -HKEY REGUTIL::s_hUserFrameworkKey = (HKEY) INVALID_HANDLE_VALUE; -static ProbabilisticNameSet regNames; // set of registry value names seen; should be - // a static field of REGUTIL but I don't - // want to expose ProbabilisticNameSet. -static ProbabilisticNameSet envNames; // set of environment value names seen; - -// "Registry Configuration Cache" -// -// Initialize the (optional) registry config cache. -// -// The purpose of the cache is to avoid hundreds of registry probes -// otherwise incurred by calls to GetConfigDWORD_DontUse_ and GetConfigString_DontUse_. -// -// We accomplish this by enumerating the relevant registry keys and -// remembering the extant value names; and then by avoiding probing -// for a name that was not seen in the enumeration (initialization) phase. -// -// It is optional in the sense that REGUTIL facilities like -// GetConfigDWORD_DontUse_ and GetConfigString_DontUse_ will work fine if the cache -// is never initialized; however, each config access then will hit -// the registry (typically multiple times to search HKCU and HKLM). -// -// -// Initialization: Enumerate these registry keys -// HKCU Software\Microsoft\.NetFramework -// HKLM Software\Microsoft\.NetFramework -// for value names, and "remember" them in the ProbalisticNameSet 'names'. -// -// If we ever find a reg value named DisableConfigCache under any of these -// three keys, the feature is disabled. -// -// This method is not thread-safe. It should only be called once. -// -// Perf Optimization for VSWhidbey:113373. -// -void REGUTIL::InitOptionalConfigCache() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - } - CONTRACTL_END; - - static const HKEY roots[] = { HKEY_CURRENT_USER, - HKEY_LOCAL_MACHINE}; - - LONG l = ERROR_SUCCESS; // general Win32 API error return code - HKEY hkey = NULL; - - // No caching if the environment variable COMPlus_DisableConfigCache is set - // - if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_DisableConfigCache) != 0) - goto failure; - - // Enumerate each root - // - for (int i = 0; i < NumItems(roots); i++) { - hkey = NULL; // defensive - l = WszRegOpenKeyEx(roots[i], FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &hkey); - if (l == ERROR_FILE_NOT_FOUND) { - // That registry key is not present. - // For example, installation with no HKCU\...\.NETFramework. - // Should be OK to proceed. - continue; - } - else if (l == ERROR_ACCESS_DENIED) { - // If we encounter access denied for the current key, ignore - // the failure and continue to cache the rest. Effectively this means - // we are caching that key as containing no values, which is correct - // because in the unlikely event there are values hiding underneath - // later attempts to access them (open the key) would also hit access - // denied and continue on probing other locations. - continue; - } - else if (l != ERROR_SUCCESS) { - // Something else went wrong. To be safe, don't enable the cache. - goto failure; - } - - // Enumerate every value name under this key. - // - for (int j = 0; ; j++) { - WCHAR wszValue[cchRegValueNameMax + 1]; - DWORD dwValueSize = NumItems(wszValue); - l = WszRegEnumValue(hkey, j, wszValue, &dwValueSize, - NULL, NULL, NULL, NULL); - - if (l == ERROR_SUCCESS) { - // Add value name to the names cache. - regNames.Add(wszValue); - } - else if (l == ERROR_NO_MORE_ITEMS) { - // Expected case: we've considered every value under this key. - break; - } - else if ((l == ERROR_INSUFFICIENT_BUFFER || l == ERROR_MORE_DATA) && - (dwValueSize > cchRegValueNameMax)) { - // Name is too long. That's OK, we don't cache such names. - continue; - } - else if (l == ERROR_ACCESS_DENIED) { - // As above, ignore access denied and continue on trying to cache - continue; - } - else { - // WszRegEnumValue failed OOM, or something else went wrong. - // To be safe, don't enable the cache. - goto failure; - } - } - - // Save the handles to framework regkeys so that future reads dont have to - // open it again - if (roots[i] == HKEY_CURRENT_USER) - s_hUserFrameworkKey = hkey; - else if (roots[i] == HKEY_LOCAL_MACHINE) - s_hMachineFrameworkKey = hkey; - else - RegCloseKey(hkey); - - hkey = NULL; - } - - // Success. We've enumerated all value names under the roots; - // enable the REGUTIL value name config cache. - // - s_fUseRegCache = TRUE; - - // Now create a cache of environment variables - if (WCHAR * wszStrings = WszGetEnvironmentStrings()) - { - // GetEnvironmentStrings returns pointer to a null terminated block containing - // null terminated strings - for(WCHAR *wszCurr = wszStrings; *wszCurr; wszCurr++) - { - WCHAR wch = towlower(*wszCurr); - - // Lets only cache env variables with the COMPlus prefix only - if (wch == W('c')) - { - WCHAR *wszName = wszCurr; - - // Look for the separator between name and value - while (*wszCurr && *wszCurr != W('=')) - wszCurr++; - - if (*wszCurr == W('=')) - { - // Check the prefix - if(!SString::_wcsnicmp(wszName, COMPLUS_PREFIX, LEN_OF_COMPLUS_PREFIX)) - { - wszName += LEN_OF_COMPLUS_PREFIX; - envNames.Add(wszName, (DWORD) (wszCurr - wszName)); - } - } - - } - // Look for current string termination - while (*wszCurr) - wszCurr++; - - } - - WszFreeEnvironmentStrings(wszStrings); - s_fUseEnvCache = TRUE; - - } - return; - -failure: - if (hkey != NULL) - RegCloseKey(hkey); -} - -// Return TRUE if the registry value name was seen (or might have been seen) -// in the registry at cache initialization time; -// return FALSE if it definitely was not seen at startup. -// -// If not using the config cache, return TRUE always. -// -// Perf Optimization for VSWhidbey:113373. -// -BOOL REGUTIL::RegCacheValueNameSeenPerhaps(LPCWSTR name) -{ - WRAPPER_NO_CONTRACT; - - return !s_fUseRegCache - || (wcslen(name) > cchRegValueNameMax) - || regNames.MayContain(name); -} - -BOOL REGUTIL::EnvCacheValueNameSeenPerhaps(LPCWSTR name) -{ - WRAPPER_NO_CONTRACT; - - return !s_fUseEnvCache - || envNames.MayContain(name); -} - -#endif // ALLOW_REGISTRY diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index 0e8060ef65e0..3efde73df285 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -305,12 +305,8 @@ HRESULT EnsureEEStarted() { BEGIN_ENTRYPOINT_NOTHROW; -#ifndef TARGET_UNIX - // The sooner we do this, the sooner we avoid probing registry entries. - // (Perf Optimization for VSWhidbey:113373.) - REGUTIL::InitOptionalConfigCache(); -#endif - + // Initialize our configuration cache to avoid unuseful probing. + CLRConfig::InitCache(); BOOL bStarted=FALSE; diff --git a/src/coreclr/vm/comcallablewrapper.cpp b/src/coreclr/vm/comcallablewrapper.cpp index 06357458e769..018cb91076e7 100644 --- a/src/coreclr/vm/comcallablewrapper.cpp +++ b/src/coreclr/vm/comcallablewrapper.cpp @@ -4192,11 +4192,6 @@ BOOL ComCallWrapperTemplate::IsSafeTypeForMarshalling() return TRUE; } - if ((CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_AllowDComReflection) != 0)) - { - return TRUE; - } - BOOL isSafe = TRUE; PTR_MethodTable pMt = this->GetClassType().GetMethodTable(); EX_TRY diff --git a/src/coreclr/vm/compatibilityswitch.cpp b/src/coreclr/vm/compatibilityswitch.cpp index 49ce4a400c6e..aedddbc7cbbf 100644 --- a/src/coreclr/vm/compatibilityswitch.cpp +++ b/src/coreclr/vm/compatibilityswitch.cpp @@ -23,7 +23,7 @@ FCIMPL1(StringObject*, CompatibilitySwitch::GetValue, StringObject* switchNameUN HELPER_METHOD_FRAME_BEGIN_RET_1(name); CLRConfig::ConfigStringInfo info; info.name = name->GetBuffer(); - info.options = CLRConfig::EEConfig_default; + info.options = CLRConfig::LookupOptions::Default; LPWSTR strVal = CLRConfig::GetConfigValue(info); refName = StringObject::NewString(strVal); HELPER_METHOD_FRAME_END(); diff --git a/src/coreclr/vm/gcenv.ee.cpp b/src/coreclr/vm/gcenv.ee.cpp index 2318c7ba2810..7849958d0687 100644 --- a/src/coreclr/vm/gcenv.ee.cpp +++ b/src/coreclr/vm/gcenv.ee.cpp @@ -1125,7 +1125,7 @@ bool GCToEEInterface::GetBooleanConfigValue(const char* privateKey, const char* // otherwise, ask the config subsystem. if (CLRConfig::IsConfigOptionSpecified(configKey)) { - CLRConfig::ConfigDWORDInfo info { configKey , 0, CLRConfig::EEConfig_default }; + CLRConfig::ConfigDWORDInfo info { configKey , 0, CLRConfig::LookupOptions::Default }; *value = CLRConfig::GetConfigValue(info) != 0; return true; } @@ -1170,7 +1170,7 @@ bool GCToEEInterface::GetIntConfigValue(const char* privateKey, const char* publ // so have to fake it with getting the string and converting to uint64_t if (CLRConfig::IsConfigOptionSpecified(configKey)) { - CLRConfig::ConfigStringInfo info { configKey, CLRConfig::EEConfig_default }; + CLRConfig::ConfigStringInfo info { configKey, CLRConfig::LookupOptions::Default }; LPWSTR out = CLRConfig::GetConfigValue(info); if (!out) { @@ -1226,7 +1226,7 @@ bool GCToEEInterface::GetStringConfigValue(const char* privateKey, const char* p return false; } - CLRConfig::ConfigStringInfo info { configKey, CLRConfig::EEConfig_default }; + CLRConfig::ConfigStringInfo info { configKey, CLRConfig::LookupOptions::Default }; LPWSTR fromClrConfig = CLRConfig::GetConfigValue(info); LPCWSTR out = fromClrConfig; if (out == NULL) diff --git a/src/coreclr/vm/jithost.cpp b/src/coreclr/vm/jithost.cpp index 798d594c4630..504e389faa62 100644 --- a/src/coreclr/vm/jithost.cpp +++ b/src/coreclr/vm/jithost.cpp @@ -26,7 +26,7 @@ int JitHost::getIntConfigValue(const WCHAR* name, int defaultValue) WRAPPER_NO_CONTRACT; // Translate JIT call into runtime configuration query - CLRConfig::ConfigDWORDInfo info{ name, (DWORD)defaultValue, CLRConfig::EEConfig_default }; + CLRConfig::ConfigDWORDInfo info{ name, (DWORD)defaultValue, CLRConfig::LookupOptions::Default }; // Perform a CLRConfig look up on behalf of the JIT. return CLRConfig::GetConfigValue(info); @@ -37,7 +37,7 @@ const WCHAR* JitHost::getStringConfigValue(const WCHAR* name) WRAPPER_NO_CONTRACT; // Translate JIT call into runtime configuration query - CLRConfig::ConfigStringInfo info{ name, CLRConfig::EEConfig_default }; + CLRConfig::ConfigStringInfo info{ name, CLRConfig::LookupOptions::Default }; // Perform a CLRConfig look up on behalf of the JIT. return CLRConfig::GetConfigValue(info); From da23d5a6a45fc4d54a478fd3689789ef3c38ec26 Mon Sep 17 00:00:00 2001 From: Brian Robbins Date: Tue, 30 Mar 2021 21:28:50 -0700 Subject: [PATCH 58/98] Specify the Size of List Instances in InvokeTypeInfo (#50438) * Initialize List instances in InvokeTypeInfo with known sizes. * Switch from List to arrays. --- .../Diagnostics/Tracing/TraceLogging/EventPayload.cs | 10 +++++----- .../Diagnostics/Tracing/TraceLogging/InvokeTypeInfo.cs | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/EventPayload.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/EventPayload.cs index cd34723240c5..98cd97f567a5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/EventPayload.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/EventPayload.cs @@ -22,9 +22,9 @@ namespace System.Diagnostics.Tracing /// internal sealed class EventPayload : IDictionary { - internal EventPayload(List payloadNames, List payloadValues) + internal EventPayload(string[] payloadNames, object?[] payloadValues) { - Debug.Assert(payloadNames.Count == payloadValues.Count); + Debug.Assert(payloadNames.Length == payloadValues.Length); m_names = payloadNames; m_values = payloadValues; @@ -88,7 +88,7 @@ public bool ContainsKey(string key) return false; } - public int Count => m_names.Count; + public int Count => m_names.Length; public bool IsReadOnly => true; @@ -142,8 +142,8 @@ public bool TryGetValue(string key, [MaybeNullWhen(false)] out object? value) } #region private - private readonly List m_names; - private readonly List m_values; + private readonly string[] m_names; + private readonly object?[] m_values; #endregion } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/InvokeTypeInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/InvokeTypeInfo.cs index 0cc42deb4fde..1fc44ac48917 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/InvokeTypeInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/InvokeTypeInfo.cs @@ -77,13 +77,13 @@ public override void WriteData(PropertyValue value) { if (this.properties != null) { - var membersNames = new List(); - var membersValues = new List(); + var membersNames = new string[this.properties.Length]; + var membersValues = new object?[this.properties.Length]; for (int i = 0; i < this.properties.Length; i++) { object? propertyValue = properties[i].propertyInfo.GetValue(value); - membersNames.Add(properties[i].name); - membersValues.Add(properties[i].typeInfo.GetData(propertyValue)); + membersNames[i] = properties[i].name; + membersValues[i] = properties[i].typeInfo.GetData(propertyValue); } return new EventPayload(membersNames, membersValues); } From 09f075fff71e8ca0c910d19419a0af8a1179840b Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Tue, 30 Mar 2021 21:41:14 -0700 Subject: [PATCH 59/98] Resolve unknown platform name warnings (#50193) * Add targets as supported platforms * Update analyzer version * Update comment, remove '' != 'false' condition --- eng/Versions.props | 2 +- eng/targetframeworksuffix.props | 2 ++ eng/versioning.targets | 20 ++++++++++++++----- .../Diagnostics/Tracing/CounterGroup.cs | 2 ++ .../Diagnostics/Tracing/DiagnosticCounter.cs | 2 ++ .../Diagnostics/Tracing/EventCounter.cs | 2 ++ 6 files changed, 24 insertions(+), 6 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index 12105014900a..2e3ff3518a2f 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -45,7 +45,7 @@ - 6.0.0-preview3.21158.1 + 6.0.0-preview3.21168.1 3.9.0-5.final 3.9.0-5.final diff --git a/eng/targetframeworksuffix.props b/eng/targetframeworksuffix.props index 5926e5266de0..00d2acfd4607 100644 --- a/eng/targetframeworksuffix.props +++ b/eng/targetframeworksuffix.props @@ -9,6 +9,8 @@ $(TargetFrameworkSuffix) 1.0 $(TargetFrameworkSuffix),Version=$(TargetPlatformVersion) + + 0.0 diff --git a/eng/versioning.targets b/eng/versioning.targets index a55fe2407ece..cf0f497a1096 100644 --- a/eng/versioning.targets +++ b/eng/versioning.targets @@ -47,17 +47,27 @@ - - - - + + + + + + + <_unsupportedOSPlatforms Include="$(UnsupportedOSPlatforms)" /> - + + <_Parameter1>%(_unsupportedOSPlatforms.Identity) diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs index 10da258166b0..5d85b20e7b6b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs @@ -15,7 +15,9 @@ namespace Microsoft.Diagnostics.Tracing namespace System.Diagnostics.Tracing #endif { +#if NETCOREAPP [UnsupportedOSPlatform("browser")] +#endif internal sealed class CounterGroup { private readonly EventSource _eventSource; diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/DiagnosticCounter.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/DiagnosticCounter.cs index 16724c371c49..0c23cf99222b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/DiagnosticCounter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/DiagnosticCounter.cs @@ -20,7 +20,9 @@ namespace System.Diagnostics.Tracing /// DiagnosticCounter is an abstract class that serves as the parent class for various Counter* classes, /// namely EventCounter, PollingCounter, IncrementingEventCounter, and IncrementingPollingCounter. /// +#if NETCOREAPP [UnsupportedOSPlatform("browser")] +#endif public abstract class DiagnosticCounter : IDisposable { /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventCounter.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventCounter.cs index 52dac7973982..8a53d5690821 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventCounter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventCounter.cs @@ -24,7 +24,9 @@ namespace System.Diagnostics.Tracing /// See https://github.com/dotnet/runtime/blob/main/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestEventCounter.cs /// which shows tests, which are also useful in seeing actual use. /// +#if NETCOREAPP [UnsupportedOSPlatform("browser")] +#endif public partial class EventCounter : DiagnosticCounter { /// From f181620b082006567f55e656e509dac6f8a270f4 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Wed, 31 Mar 2021 01:12:47 -0400 Subject: [PATCH 60/98] [mono] Address comments from PR #50328. (#50462) * Use g_slist_concat (). * Add MemoryManagerLock to the lock tracer. * Remove some dead code. --- src/mono/mono/metadata/loader-internals.h | 6 +++++- src/mono/mono/metadata/lock-tracer.h | 1 + src/mono/mono/metadata/memory-manager.c | 4 ++-- src/mono/mono/metadata/object.c | 10 ---------- src/mono/mono/mini/interp/interp.c | 22 ++-------------------- 5 files changed, 10 insertions(+), 33 deletions(-) diff --git a/src/mono/mono/metadata/loader-internals.h b/src/mono/mono/metadata/loader-internals.h index 34f55769d7ca..e964c9bc09b6 100644 --- a/src/mono/mono/metadata/loader-internals.h +++ b/src/mono/mono/metadata/loader-internals.h @@ -94,7 +94,6 @@ struct _MonoMemoryManager { gboolean freeing; // If taking this with the loader lock, always take this second - // Currently unused, we take the domain lock instead MonoCoopMutex lock; // Private, don't access directly @@ -182,6 +181,11 @@ mono_alc_assemblies_lock (MonoAssemblyLoadContext *alc); void mono_alc_assemblies_unlock (MonoAssemblyLoadContext *alc); +/* + * This is below the loader lock in the locking hierarcy, + * so when taking this with the loader lock, always take + * this second. + */ void mono_alc_memory_managers_lock (MonoAssemblyLoadContext *alc); diff --git a/src/mono/mono/metadata/lock-tracer.h b/src/mono/mono/metadata/lock-tracer.h index 8a111fffc26c..301e4644c0d0 100644 --- a/src/mono/mono/metadata/lock-tracer.h +++ b/src/mono/mono/metadata/lock-tracer.h @@ -15,6 +15,7 @@ typedef enum { InvalidLock = 0, LoaderLock, ImageDataLock, + MemoryManagerLock, DomainAssembliesLock, DomainJitCodeHashLock, IcallLock, diff --git a/src/mono/mono/metadata/memory-manager.c b/src/mono/mono/metadata/memory-manager.c index 901e9452778b..84fb080e295b 100644 --- a/src/mono/mono/metadata/memory-manager.c +++ b/src/mono/mono/metadata/memory-manager.c @@ -217,13 +217,13 @@ mono_mem_manager_free_singleton (MonoSingletonMemoryManager *memory_manager, gbo void mono_mem_manager_lock (MonoMemoryManager *memory_manager) { - mono_coop_mutex_lock (&memory_manager->lock); + mono_locks_coop_acquire (&memory_manager->lock, MemoryManagerLock); } void mono_mem_manager_unlock (MonoMemoryManager *memory_manager) { - mono_coop_mutex_unlock (&memory_manager->lock); + mono_locks_coop_release (&memory_manager->lock, MemoryManagerLock); } static inline void diff --git a/src/mono/mono/metadata/object.c b/src/mono/mono/metadata/object.c index f90bb34e93cb..31990eac37b7 100644 --- a/src/mono/mono/metadata/object.c +++ b/src/mono/mono/metadata/object.c @@ -1621,16 +1621,6 @@ build_imt_slots (MonoClass *klass, MonoVTable *vt, gpointer* imt, GSList *extra_ vt->imt_collisions_bitmap |= imt_collisions_bitmap; } -static void -build_imt (MonoClass *klass, MonoVTable *vt, gpointer* imt, GSList *extra_interfaces) -{ - MONO_REQ_GC_NEUTRAL_MODE; - - mono_loader_lock (); - build_imt_slots (klass, vt, imt, extra_interfaces, -1); - mono_loader_unlock (); -} - /** * mono_vtable_build_imt_slot: * \param vtable virtual object table struct diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index daaae50705b4..25fcf942d1c5 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -632,25 +632,6 @@ typedef struct { InterpMethod *target_imethod; } InterpVTableEntry; -static inline GSList* -g_slist_append_node (GSList *list, GSList *new_list, gpointer data) -{ - GSList *last; - - new_list->data = data; - new_list->next = NULL; - - if (list) { - last = list; - while (last->next) - last = last->next; - last->next = new_list; - - return list; - } else - return new_list; -} - /* memory manager lock must be held */ static GSList* append_imethod (MonoMemoryManager *memory_manager, GSList *list, InterpMethod *imethod, InterpMethod *target_imethod) @@ -662,7 +643,8 @@ append_imethod (MonoMemoryManager *memory_manager, GSList *list, InterpMethod *i entry->imethod = imethod; entry->target_imethod = target_imethod; ret = mono_mem_manager_alloc0 (memory_manager, sizeof (GSList)); - ret = g_slist_append_node (list, ret, entry); + ret->data = entry; + ret = g_slist_concat (list, ret); return ret; } From 4f819108ca81965bcac0d9670471f5c6a9140d13 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 31 Mar 2021 08:42:18 +0200 Subject: [PATCH 61/98] Dont cache file length when handle has been exposed (#50424) --- .../FileStream/FileStreamConformanceTests.cs | 28 ++++++++++++++++++ .../Strategies/WindowsFileStreamStrategy.cs | 29 +++++++++++++++---- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.cs index 271cb2833c94..82b10a882c3d 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.cs @@ -133,6 +133,34 @@ public async Task NoDataIsLostWhenWritingToFile(ReadWriteMode mode) byte[] allBytes = File.ReadAllBytes(filePath); Assert.Equal(writtenBytes.ToArray(), allBytes); } + + [Theory] + [InlineData(FileAccess.Write)] + [InlineData(FileAccess.ReadWrite)] // FileAccess.Read does not allow for length manipulations + public async Task LengthIsNotCachedAfterHandleHasBeenExposed(FileAccess fileAccess) + { + using FileStream stream = (FileStream)await CreateStream(Array.Empty(), fileAccess); + using FileStream createdFromHandle = new FileStream(stream.SafeFileHandle, fileAccess); + + Assert.Equal(0, stream.Length); + Assert.Equal(0, createdFromHandle.Length); + + createdFromHandle.SetLength(1); + Assert.Equal(1, createdFromHandle.Length); + Assert.Equal(1, stream.Length); + + createdFromHandle.SetLength(2); + Assert.Equal(2, createdFromHandle.Length); + Assert.Equal(2, stream.Length); + + stream.SetLength(1); + Assert.Equal(1, stream.Length); + Assert.Equal(1, createdFromHandle.Length); + + stream.SetLength(2); + Assert.Equal(2, stream.Length); + Assert.Equal(2, createdFromHandle.Length); + } } public class UnbufferedSyncFileStreamStandaloneConformanceTests : FileStreamStandaloneConformanceTests diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/WindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/WindowsFileStreamStrategy.cs index 8c33d75aeebd..d42494c19086 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/WindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/WindowsFileStreamStrategy.cs @@ -4,7 +4,6 @@ using System.Diagnostics; using System.Threading.Tasks; using Microsoft.Win32.SafeHandles; -using System.Runtime.CompilerServices; namespace System.IO.Strategies { @@ -28,6 +27,7 @@ internal abstract class WindowsFileStreamStrategy : FileStreamStrategy protected long _filePosition; private long _appendStart; // When appending, prevent overwriting file. private long _length = -1; // When the file is locked for writes (_share <= FileShare.Read) cache file length in-memory, negative means that hasn't been fetched. + private bool _exposedHandle; // created from handle, or SafeFileHandle was used and the handle got exposed internal WindowsFileStreamStrategy(SafeFileHandle handle, FileAccess access, FileShare share) { @@ -37,6 +37,7 @@ internal WindowsFileStreamStrategy(SafeFileHandle handle, FileAccess access, Fil // but we can't as they're readonly. _access = access; _share = share; + _exposedHandle = true; // As the handle was passed in, we must set the handle field at the very end to // avoid the finalizer closing the handle when we throw errors. @@ -77,15 +78,29 @@ internal WindowsFileStreamStrategy(string path, FileMode mode, FileAccess access // When the file is locked for writes we can cache file length in memory // and avoid subsequent native calls which are expensive. - public unsafe sealed override long Length => _share > FileShare.Read ? - FileStreamHelpers.GetFileLength(_fileHandle, _path) : - _length < 0 ? _length = FileStreamHelpers.GetFileLength(_fileHandle, _path) : _length; + public unsafe sealed override long Length + { + get + { + if (_share > FileShare.Read || _exposedHandle) + { + return FileStreamHelpers.GetFileLength(_fileHandle, _path); + } + + if (_length < 0) + { + _length = FileStreamHelpers.GetFileLength(_fileHandle, _path); + } + + return _length; + } + } protected void UpdateLengthOnChangePosition() { // Do not update the cached length if the file is not locked // or if the length hasn't been fetched. - if (_share > FileShare.Read || _length < 0) + if (_share > FileShare.Read || _length < 0 || _exposedHandle) { Debug.Assert(_length < 0); return; @@ -120,6 +135,10 @@ internal sealed override SafeFileHandle SafeFileHandle // in memory position is out-of-sync with the actual file position. FileStreamHelpers.Seek(_fileHandle, _path, _filePosition, SeekOrigin.Begin); } + + _exposedHandle = true; + _length = -1; // invalidate cached length + return _fileHandle; } } From bce310d872c211049506f90149d498ab26ca6f81 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 31 Mar 2021 08:53:52 +0200 Subject: [PATCH 62/98] ensure MetadataReader fast path works with every FileStream on every OS (#50367) --- .../src/System.Reflection.Metadata.csproj | 11 +++++--- .../MemoryBlocks/StreamMemoryBlockProvider.cs | 6 +++-- .../FileStreamReadLightUp.netcoreapp.cs | 15 +++++++++++ .../FileStreamReadLightUp.netstandard1.1.cs | 26 +++++------------- ...> FileStreamReadLightUp.netstandard2.0.cs} | 27 ++++--------------- 5 files changed, 37 insertions(+), 48 deletions(-) create mode 100644 src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/FileStreamReadLightUp.netcoreapp.cs rename src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/{FileStreamReadLightUp.cs => FileStreamReadLightUp.netstandard2.0.cs} (61%) diff --git a/src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csproj b/src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csproj index 2417558b7857..e099ac354b03 100644 --- a/src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csproj +++ b/src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csproj @@ -1,4 +1,4 @@ - + true en-US @@ -15,9 +15,11 @@ + Link="Common\Interop\Windows\Interop.Libraries.cs" + Condition="'$(TargetFramework)' != '$(NetCoreAppCurrent)' "/> + Link="Common\Interop\Windows\kernel32\Interop.ReadFile_SafeHandle_IntPtr.cs" + Condition="'$(TargetFramework)' != '$(NetCoreAppCurrent)' "/> @@ -102,8 +104,9 @@ - + + diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/MemoryBlocks/StreamMemoryBlockProvider.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/MemoryBlocks/StreamMemoryBlockProvider.cs index 7581e90f0cb4..1c9f68da95e0 100644 --- a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/MemoryBlocks/StreamMemoryBlockProvider.cs +++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/MemoryBlocks/StreamMemoryBlockProvider.cs @@ -76,9 +76,11 @@ internal static unsafe NativeHeapMemoryBlock ReadMemoryBlockNoLock(Stream stream { stream.Seek(start, SeekOrigin.Begin); - if (!isFileStream || !FileStreamReadLightUp.TryReadFile(stream, block.Pointer, start, size)) + int bytesRead = 0; + + if (!isFileStream || (bytesRead = FileStreamReadLightUp.ReadFile(stream, block.Pointer, size)) != size) { - stream.CopyTo(block.Pointer, size); + stream.CopyTo(block.Pointer + bytesRead, size - bytesRead); } fault = false; diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/FileStreamReadLightUp.netcoreapp.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/FileStreamReadLightUp.netcoreapp.cs new file mode 100644 index 000000000000..aabc6739c715 --- /dev/null +++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/FileStreamReadLightUp.netcoreapp.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; + +namespace System.Reflection.Internal +{ + internal static class FileStreamReadLightUp + { + internal static bool IsFileStream(Stream stream) => stream is FileStream; + + internal static unsafe int ReadFile(Stream stream, byte* buffer, int size) + => stream.Read(new Span(buffer, size)); + } +} diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/FileStreamReadLightUp.netstandard1.1.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/FileStreamReadLightUp.netstandard1.1.cs index 1bcc69e7a54c..774def66573f 100644 --- a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/FileStreamReadLightUp.netstandard1.1.cs +++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/FileStreamReadLightUp.netstandard1.1.cs @@ -86,43 +86,29 @@ internal static SafeHandle GetSafeFileHandle(Stream stream) return handle; } - internal static unsafe bool TryReadFile(Stream stream, byte* buffer, long start, int size) + internal static unsafe int ReadFile(Stream stream, byte* buffer, int size) { if (readFileNotAvailable) { - return false; + return 0; } SafeHandle handle = GetSafeFileHandle(stream); if (handle == null) { - return false; + return 0; } - int result; - int bytesRead; - try { - result = Interop.Kernel32.ReadFile(handle, buffer, size, out bytesRead, IntPtr.Zero); + int result = Interop.Kernel32.ReadFile(handle, buffer, size, out int bytesRead, IntPtr.Zero); + return result == 0 ? 0 : bytesRead; } catch { readFileNotAvailable = true; - return false; + return 0; } - - if (result == 0 || bytesRead != size) - { - // We used to throw here, but this is where we land if the FileStream was - // opened with useAsync: true, which is currently the default on .NET Core. - // https://github.com/dotnet/corefx/pull/987 filed to look in to how best to - // handle this, but in the meantime, we'll fall back to the slower code path - // just as in the case where the native API is unavailable in the current platform. - return false; - } - - return true; } } } diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/FileStreamReadLightUp.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/FileStreamReadLightUp.netstandard2.0.cs similarity index 61% rename from src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/FileStreamReadLightUp.cs rename to src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/FileStreamReadLightUp.netstandard2.0.cs index f64be71469b1..2416a723f6c0 100644 --- a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/FileStreamReadLightUp.cs +++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/FileStreamReadLightUp.netstandard2.0.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; @@ -9,12 +8,7 @@ namespace System.Reflection.Internal { internal static class FileStreamReadLightUp { - private static bool IsReadFileAvailable => -#if NETCOREAPP - OperatingSystem.IsWindows(); -#else - Path.DirectorySeparatorChar == '\\'; -#endif + private static bool IsReadFileAvailable => Path.DirectorySeparatorChar == '\\'; internal static bool IsFileStream(Stream stream) => stream is FileStream; @@ -42,32 +36,21 @@ internal static class FileStreamReadLightUp return handle; } - internal static unsafe bool TryReadFile(Stream stream, byte* buffer, long start, int size) + internal static unsafe int ReadFile(Stream stream, byte* buffer, int size) { if (!IsReadFileAvailable) { - return false; + return 0; } SafeHandle? handle = GetSafeFileHandle(stream); if (handle == null) { - return false; + return 0; } int result = Interop.Kernel32.ReadFile(handle, buffer, size, out int bytesRead, IntPtr.Zero); - - if (result == 0 || bytesRead != size) - { - // We used to throw here, but this is where we land if the FileStream was - // opened with useAsync: true, which is currently the default on .NET Core. - // https://github.com/dotnet/corefx/pull/987 filed to look in to how best to - // handle this, but in the meantime, we'll fall back to the slower code path - // just as in the case where the native API is unavailable in the current platform. - return false; - } - - return true; + return result == 0 ? 0 : bytesRead; } } } From 76a50c68df8d1fdad33734a2d0bc74e01d644191 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Wed, 31 Mar 2021 03:06:12 -0400 Subject: [PATCH 63/98] [tests] Add Mono issue to CallConvMemberFunction tests (#50461) --- src/tests/issues.targets | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tests/issues.targets b/src/tests/issues.targets index b2658574d4bb..503c21a3d4ff 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -3282,13 +3282,13 @@ needs triage - needs triage + https://github.com/dotnet/runtime/issues/50440 - needs triage + https://github.com/dotnet/runtime/issues/50440 - needs triage + https://github.com/dotnet/runtime/issues/50440 https://github.com/dotnet/runtime/issues/44648 From 8cc37cb9d8405be0b15bdc8ae9cf2ddc31dbd998 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 31 Mar 2021 03:23:25 -0400 Subject: [PATCH 64/98] Rename Random.LegacyImpl (#50456) To be more specific about what it is. --- .../src/System.Private.CoreLib.Shared.projitems | 2 +- .../{Random.LegacyImpl.cs => Random.Net5CompatImpl.cs} | 6 +++--- src/libraries/System.Private.CoreLib/src/System/Random.cs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) rename src/libraries/System.Private.CoreLib/src/System/{Random.LegacyImpl.cs => Random.Net5CompatImpl.cs} (97%) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 4e86ee49effe..59c43653886e 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -499,7 +499,7 @@ - + diff --git a/src/libraries/System.Private.CoreLib/src/System/Random.LegacyImpl.cs b/src/libraries/System.Private.CoreLib/src/System/Random.Net5CompatImpl.cs similarity index 97% rename from src/libraries/System.Private.CoreLib/src/System/Random.LegacyImpl.cs rename to src/libraries/System.Private.CoreLib/src/System/Random.Net5CompatImpl.cs index eea6e4c9f3ff..250c84cb971a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Random.LegacyImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Random.Net5CompatImpl.cs @@ -16,7 +16,7 @@ public partial class Random /// of Knuth's subtractive random number generator algorithm. See https://github.com/dotnet/runtime/issues/23198 /// for a discussion of some of the modifications / discrepancies. /// - private sealed class LegacyImpl : ImplBase + private sealed class Net5CompatImpl : ImplBase { /// Thread-static instance used to seed any legacy implementations created with the default ctor. [ThreadStatic] @@ -29,11 +29,11 @@ private sealed class LegacyImpl : ImplBase private int _inext; private int _inextp; - public LegacyImpl(Random parent) : this(parent, (t_seedGenerator ??= new()).Next()) + public Net5CompatImpl(Random parent) : this(parent, (t_seedGenerator ??= new()).Next()) { } - public LegacyImpl(Random parent, int Seed) + public Net5CompatImpl(Random parent, int Seed) { _parent = parent; diff --git a/src/libraries/System.Private.CoreLib/src/System/Random.cs b/src/libraries/System.Private.CoreLib/src/System/Random.cs index daaa02e93a53..f725b4c73f21 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Random.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Random.cs @@ -24,7 +24,7 @@ public Random() => // With no seed specified, if this is the base type, we can implement this however we like. // If it's a derived type, for compat we respect the previous implementation, so that overrides // are called as they were previously. - _impl = GetType() == typeof(Random) ? new XoshiroImpl() : new LegacyImpl(this); + _impl = GetType() == typeof(Random) ? new XoshiroImpl() : new Net5CompatImpl(this); /// Initializes a new instance of the Random class, using the specified seed value. /// @@ -34,7 +34,7 @@ public Random() => public Random(int Seed) => // With a custom seed, for compat we respect the previous implementation so that the same sequence // previously output continues to be output. - _impl = new LegacyImpl(this, Seed); + _impl = new Net5CompatImpl(this, Seed); /// Returns a non-negative random integer. /// A 32-bit signed integer that is greater than or equal to 0 and less than . From cee9df8b3f866edb89140bbfa9a87878ef88338c Mon Sep 17 00:00:00 2001 From: Ilya Date: Wed, 31 Mar 2021 13:02:48 +0500 Subject: [PATCH 65/98] Add tracking a recursion depth in System.IO.FileSystem (#48148) * Add tracking a depth recursion in System.IO.FileSystem * Update src/libraries/System.IO.FileSystem/src/System/IO/EnumerationOptions.cs Co-authored-by: Carlos Sanchez <1175054+carlossanlop@users.noreply.github.com> * Fix docs * Address feedbacks * Fix typo * Add MaxRecursionDepth value check * Fix net48 build: Add string to MS.IO.Redist resx file * Fix test and initialization. * Address feedback Co-authored-by: Carlos Sanchez <1175054+carlossanlop@users.noreply.github.com> Co-authored-by: David Cantu --- .../src/Resources/Strings.resx | 5 +- .../ref/System.IO.FileSystem.cs | 1 + .../Enumeration/FileSystemEnumerator.Unix.cs | 10 ++-- .../FileSystemEnumerator.Windows.cs | 10 ++-- .../IO/Enumeration/FileSystemEnumerator.cs | 3 ++ .../src/System/IO/EnumerationOptions.cs | 23 +++++++++ .../tests/Enumeration/RecursionDepthTests.cs | 51 +++++++++++++++++++ .../tests/System.IO.FileSystem.Tests.csproj | 1 + 8 files changed, 93 insertions(+), 11 deletions(-) create mode 100644 src/libraries/System.IO.FileSystem/tests/Enumeration/RecursionDepthTests.cs diff --git a/src/libraries/Microsoft.IO.Redist/src/Resources/Strings.resx b/src/libraries/Microsoft.IO.Redist/src/Resources/Strings.resx index d5c13cece767..f84023f233d3 100644 --- a/src/libraries/Microsoft.IO.Redist/src/Resources/Strings.resx +++ b/src/libraries/Microsoft.IO.Redist/src/Resources/Strings.resx @@ -114,6 +114,9 @@ The path is empty. - Drive name must be a root directory (i.e. 'C:\') or a drive letter ('C'). + Drive name must be a root directory (i.e. 'C:\') or a drive letter ('C'). + + + Non-negative number required. diff --git a/src/libraries/System.IO.FileSystem/ref/System.IO.FileSystem.cs b/src/libraries/System.IO.FileSystem/ref/System.IO.FileSystem.cs index 64c2bbc30936..08121dc0b5b2 100644 --- a/src/libraries/System.IO.FileSystem/ref/System.IO.FileSystem.cs +++ b/src/libraries/System.IO.FileSystem/ref/System.IO.FileSystem.cs @@ -101,6 +101,7 @@ public EnumerationOptions() { } public bool IgnoreInaccessible { get { throw null; } set { } } public System.IO.MatchCasing MatchCasing { get { throw null; } set { } } public System.IO.MatchType MatchType { get { throw null; } set { } } + public int MaxRecursionDepth { get { throw null; } set { } } public bool RecurseSubdirectories { get { throw null; } set { } } public bool ReturnSpecialDirectories { get { throw null; } set { } } } diff --git a/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.Unix.cs b/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.Unix.cs index 95bb53f43ba8..ae179b015e07 100644 --- a/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.Unix.cs +++ b/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.Unix.cs @@ -22,7 +22,7 @@ public unsafe abstract partial class FileSystemEnumerator : CriticalFin private string? _currentPath; private IntPtr _directoryHandle; private bool _lastEntryFound; - private Queue? _pending; + private Queue<(string Path, int RemainingDepth)>? _pending; private Interop.Sys.DirectoryEntry _entry; private TResult? _current; @@ -144,12 +144,12 @@ public bool MoveNext() if (isDirectory && !isSpecialDirectory) { - if (_options.RecurseSubdirectories && ShouldRecurseIntoEntry(ref entry)) + if (_options.RecurseSubdirectories && _remainingRecursionDepth > 0 && ShouldRecurseIntoEntry(ref entry)) { // Recursion is on and the directory was accepted, Queue it if (_pending == null) - _pending = new Queue(); - _pending.Enqueue(Path.Join(_currentPath, entry.FileName)); + _pending = new Queue<(string Path, int RemainingDepth)>(); + _pending.Enqueue((Path.Join(_currentPath, entry.FileName), _remainingRecursionDepth - 1)); } } @@ -215,7 +215,7 @@ private bool DequeueNextDirectory() if (_pending == null || _pending.Count == 0) return false; - _currentPath = _pending.Dequeue(); + (_currentPath, _remainingRecursionDepth) = _pending.Dequeue(); _directoryHandle = CreateDirectoryHandle(_currentPath, ignoreNotFound: true); } diff --git a/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.Windows.cs b/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.Windows.cs index 32fd206ea897..a6ac2027beda 100644 --- a/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.Windows.cs +++ b/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.Windows.cs @@ -38,7 +38,7 @@ public unsafe abstract partial class FileSystemEnumerator : CriticalFin private IntPtr _directoryHandle; private string? _currentPath; private bool _lastEntryFound; - private Queue<(IntPtr Handle, string Path)>? _pending; + private Queue<(IntPtr Handle, string Path, int RemainingDepth)>? _pending; private void Init() { @@ -161,7 +161,7 @@ public bool MoveNext() if (!_options.ReturnSpecialDirectories) continue; } - else if (_options.RecurseSubdirectories && ShouldRecurseIntoEntry(ref entry)) + else if (_options.RecurseSubdirectories && _remainingRecursionDepth > 0 && ShouldRecurseIntoEntry(ref entry)) { // Recursion is on and the directory was accepted, Queue it string subDirectory = Path.Join(_currentPath.AsSpan(), _entry->FileName); @@ -171,8 +171,8 @@ public bool MoveNext() try { if (_pending == null) - _pending = new Queue<(IntPtr, string)>(); - _pending.Enqueue((subDirectoryHandle, subDirectory)); + _pending = new Queue<(IntPtr, string, int)>(); + _pending.Enqueue((subDirectoryHandle, subDirectory, _remainingRecursionDepth - 1)); } catch { @@ -209,7 +209,7 @@ private bool DequeueNextDirectory() if (_pending == null || _pending.Count == 0) return false; - (_directoryHandle, _currentPath) = _pending.Dequeue(); + (_directoryHandle, _currentPath, _remainingRecursionDepth) = _pending.Dequeue(); return true; } diff --git a/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.cs b/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.cs index b5168eb1ab05..5a626a85bf7f 100644 --- a/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.cs +++ b/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.cs @@ -14,6 +14,8 @@ namespace System.IO.Enumeration { public unsafe abstract partial class FileSystemEnumerator : CriticalFinalizerObject, IEnumerator { + private int _remainingRecursionDepth; + /// /// Encapsulates a find operation. /// @@ -37,6 +39,7 @@ internal FileSystemEnumerator(string directory, bool isNormalized, EnumerationOp string path = isNormalized ? directory : Path.GetFullPath(directory); _rootDirectory = Path.TrimEndingDirectorySeparator(path); _options = options ?? EnumerationOptions.Default; + _remainingRecursionDepth = _options.MaxRecursionDepth; Init(); } diff --git a/src/libraries/System.IO.FileSystem/src/System/IO/EnumerationOptions.cs b/src/libraries/System.IO.FileSystem/src/System/IO/EnumerationOptions.cs index ca6b0350a8e3..e3d6ad15b20d 100644 --- a/src/libraries/System.IO.FileSystem/src/System/IO/EnumerationOptions.cs +++ b/src/libraries/System.IO.FileSystem/src/System/IO/EnumerationOptions.cs @@ -12,6 +12,10 @@ namespace System.IO { public class EnumerationOptions { + private int _maxRecursionDepth; + + internal const int DefaultMaxRecursionDepth = int.MaxValue; + /// /// For internal use. These are the options we want to use if calling the existing Directory/File APIs where you don't /// explicitly specify EnumerationOptions. @@ -34,6 +38,7 @@ public EnumerationOptions() { IgnoreInaccessible = true; AttributesToSkip = FileAttributes.Hidden | FileAttributes.System; + MaxRecursionDepth = DefaultMaxRecursionDepth; } /// @@ -96,6 +101,24 @@ internal static EnumerationOptions FromSearchOption(SearchOption searchOption) /// public MatchCasing MatchCasing { get; set; } + /// Gets or sets a value that indicates the maximum directory depth to recurse while enumerating, when is set to . + /// A number that represents the maximum directory depth to recurse while enumerating. The default value is . + /// If is set to a negative number, the default value is used. + /// If is set to zero, enumeration returns the contents of the initial directory. + public int MaxRecursionDepth + { + get => _maxRecursionDepth; + set + { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); + } + + _maxRecursionDepth = value; + } + } + /// /// Set to true to return "." and ".." directory entries. /// diff --git a/src/libraries/System.IO.FileSystem/tests/Enumeration/RecursionDepthTests.cs b/src/libraries/System.IO.FileSystem/tests/Enumeration/RecursionDepthTests.cs new file mode 100644 index 000000000000..5ac0e6165340 --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/Enumeration/RecursionDepthTests.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.IO.Enumeration; +using System.Linq; +using Xunit; + +namespace System.IO.Tests.Enumeration +{ + public class RecursionDepthTests : FileSystemTest + { + public static IEnumerable GetEntryNames(string directory, int depth) + { + return new FileSystemEnumerable( + directory, + (ref FileSystemEntry entry) => entry.FileName.ToString(), + new EnumerationOptions() { RecurseSubdirectories = true, MaxRecursionDepth = depth }); + } + + [Theory, + InlineData(0, 2), + InlineData(1, 4), + InlineData(2, 5), + InlineData(3, 5), + InlineData(int.MaxValue, 5) + ] + public void EnumerateDirectory_WithSpecifedRecursionDepth(int depth, int expectedCount) + { + DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath()); + DirectoryInfo testSubdirectory1 = Directory.CreateDirectory(Path.Combine(testDirectory.FullName, "Subdirectory1")); + DirectoryInfo testSubdirectory2 = Directory.CreateDirectory(Path.Combine(testSubdirectory1.FullName, "Subdirectory2")); + FileInfo fileOne = new FileInfo(Path.Combine(testDirectory.FullName, "fileone.htm")); + FileInfo fileTwo = new FileInfo(Path.Combine(testSubdirectory1.FullName, "filetwo.html")); + FileInfo fileThree = new FileInfo(Path.Combine(testSubdirectory2.FullName, "filethree.doc")); + + fileOne.Create().Dispose(); + fileTwo.Create().Dispose(); + fileThree.Create().Dispose(); + + string[] results = GetEntryNames(testDirectory.FullName, depth).ToArray(); + Assert.Equal(expectedCount, results.Length); + } + + [Fact] + public void NegativeRecursionDepth_ThrowsArgumentOutOfRangeException() + { + Assert.Throws(() => new EnumerationOptions() { MaxRecursionDepth = -1 }); + } + } +} diff --git a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj index 7a81b2e7b1f3..97b50363adb7 100644 --- a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj +++ b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj @@ -32,6 +32,7 @@ + From 26a05128f37e62f95144cda21c180c7ddea37b20 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Wed, 31 Mar 2021 11:32:05 +0300 Subject: [PATCH 66/98] [interp] Fix tracing (#50415) --- src/mono/mono/mini/interp/interp.c | 20 +++++++++++--------- src/mono/mono/mini/interp/mintops.def | 1 + src/mono/mono/mini/interp/transform.c | 16 +++++----------- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 25fcf942d1c5..78f24a129f9b 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -6459,15 +6459,17 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs MINT_IN_BREAK; } - MINT_IN_CASE(MINT_PROF_EXIT) { - guint16 flag = ip [2]; + MINT_IN_CASE(MINT_PROF_EXIT) + MINT_IN_CASE(MINT_PROF_EXIT_VOID) { + gboolean is_void = ip [0] == MINT_PROF_EXIT_VOID; + guint16 flag = is_void ? ip [1] : ip [2]; // Set retval - int i32 = READ32 (ip + 3); - if (i32 == -1) { - } else if (i32) { - memmove (frame->retval, locals + ip [1], i32); - } else { - frame->retval [0] = LOCAL_VAR (ip [1], stackval); + if (!is_void) { + int i32 = READ32 (ip + 3); + if (i32) + memmove (frame->retval, locals + ip [1], i32); + else + frame->retval [0] = LOCAL_VAR (ip [1], stackval); } if ((flag & TRACING_FLAG) || ((flag & PROFILING_FLAG) && MONO_PROFILER_ENABLED (method_leave) && @@ -6475,7 +6477,7 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs MonoProfilerCallContext *prof_ctx = g_new0 (MonoProfilerCallContext, 1); prof_ctx->interp_frame = frame; prof_ctx->method = frame->imethod->method; - if (i32 != -1) + if (!is_void) prof_ctx->return_value = frame->retval; if (flag & TRACING_FLAG) mono_trace_leave_method (frame->imethod->method, frame->imethod->jinfo, prof_ctx); diff --git a/src/mono/mono/mini/interp/mintops.def b/src/mono/mono/mini/interp/mintops.def index b14bd646520d..b373bb5e2b6e 100644 --- a/src/mono/mono/mini/interp/mintops.def +++ b/src/mono/mono/mini/interp/mintops.def @@ -730,6 +730,7 @@ OPDEF(MINT_TANHF, "tanhf", 3, 1, 1, MintOpNoArgs) OPDEF(MINT_PROF_ENTER, "prof_enter", 2, 0, 0, MintOpShortInt) OPDEF(MINT_PROF_EXIT, "prof_exit", 5, 0, 1, MintOpShortAndInt) +OPDEF(MINT_PROF_EXIT_VOID, "prof_exit_void", 2, 0, 0, MintOpNoArgs) OPDEF(MINT_PROF_COVERAGE_STORE, "prof_coverage_store", 5, 0, 0, MintOpLongInt) OPDEF(MINT_INTRINS_ENUM_HASFLAG, "intrins_enum_hasflag", 5, 1, 2, MintOpClassToken) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 0501d06cba07..29df42cebaa0 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -4861,16 +4861,13 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, exit_profiling |= PROFILING_FLAG; if (exit_profiling) { /* This does the return as well */ - interp_add_ins (td, MINT_PROF_EXIT); - if (ult->type == MONO_TYPE_VOID) { - vt_size = -1; - interp_ins_set_sreg (td->last_ins, -1); - } else { + gboolean is_void = ult->type == MONO_TYPE_VOID; + interp_add_ins (td, is_void ? MINT_PROF_EXIT_VOID : MINT_PROF_EXIT); + td->last_ins->data [0] = exit_profiling; + if (!is_void) { interp_ins_set_sreg (td->last_ins, td->sp [0].local); + WRITE32_INS (td->last_ins, 1, &vt_size); } - - td->last_ins->data [0] = exit_profiling; - WRITE32_INS (td->last_ins, 1, &vt_size); } else { if (vt_size == 0) { if (ult->type == MONO_TYPE_VOID) { @@ -8217,9 +8214,6 @@ interp_cprop (TransformData *td) dump_interp_inst (ins); for (int i = 0; i < num_sregs; i++) { - // FIXME MINT_PROF_EXIT when void - if (sregs [i] == -1) - continue; if (sregs [i] == MINT_CALL_ARGS_SREG) { int *call_args = ins->info.call_args; if (call_args) { From 76b7dd66a3074c01ec72d1fd412c7c0a2256b4eb Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 31 Mar 2021 13:01:20 +0200 Subject: [PATCH 67/98] [FileStream] Rename "Legacy" to "Net5Compat" (#50470) * rename Legacy => Net5Compat (System.Private.CoreLib) * rename Legacy => Net5Compat (test projects) --- .../tests/TestUtilities/System/PlatformDetection.cs | 4 ++-- .../System.IO.FileSystem/System.IO.FileSystem.sln | 2 +- .../tests/FileStream/SafeFileHandle.cs | 6 +++--- .../tests/FileStream/WriteAsync.cs | 2 +- .../Net5CompatSwitchTests.cs} | 4 ++-- .../System.IO.FileSystem.Net5Compat.Tests.csproj} | 2 +- .../runtimeconfig.template.json | 0 src/libraries/System.IO/System.IO.sln | 2 +- .../System.IO.Net5Compat.Tests.csproj} | 2 +- .../runtimeconfig.template.json | 0 .../src/System.Private.CoreLib.Shared.projitems | 10 +++++----- .../IO/Strategies/FileStreamCompletionSource.Win32.cs | 4 ++-- .../src/System/IO/Strategies/FileStreamHelpers.Unix.cs | 4 ++-- .../System/IO/Strategies/FileStreamHelpers.Windows.cs | 10 +++++----- .../src/System/IO/Strategies/FileStreamHelpers.cs | 6 +++--- ...OSX.cs => Net5CompatFileStreamStrategy.Lock.OSX.cs} | 2 +- ...ix.cs => Net5CompatFileStreamStrategy.Lock.Unix.cs} | 2 +- ...gy.Unix.cs => Net5CompatFileStreamStrategy.Unix.cs} | 8 ++++---- ...dows.cs => Net5CompatFileStreamStrategy.Windows.cs} | 2 +- ...reamStrategy.cs => Net5CompatFileStreamStrategy.cs} | 10 +++++----- 20 files changed, 41 insertions(+), 41 deletions(-) rename src/libraries/System.IO.FileSystem/tests/{LegacyTests/LegacySwitchTests.cs => Net5CompatTests/Net5CompatSwitchTests.cs} (85%) rename src/libraries/System.IO.FileSystem/tests/{LegacyTests/System.IO.FileSystem.Legacy.Tests.csproj => Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj} (92%) rename src/libraries/System.IO.FileSystem/tests/{LegacyTests => Net5CompatTests}/runtimeconfig.template.json (100%) rename src/libraries/System.IO/tests/{LegacyTests/System.IO.Legacy.Tests.csproj => Net5CompatTests/System.IO.Net5Compat.Tests.csproj} (92%) rename src/libraries/System.IO/tests/{LegacyTests => Net5CompatTests}/runtimeconfig.template.json (100%) rename src/libraries/System.Private.CoreLib/src/System/IO/Strategies/{LegacyFileStreamStrategy.Lock.OSX.cs => Net5CompatFileStreamStrategy.Lock.OSX.cs} (86%) rename src/libraries/System.Private.CoreLib/src/System/IO/Strategies/{LegacyFileStreamStrategy.Lock.Unix.cs => Net5CompatFileStreamStrategy.Lock.Unix.cs} (93%) rename src/libraries/System.Private.CoreLib/src/System/IO/Strategies/{LegacyFileStreamStrategy.Unix.cs => Net5CompatFileStreamStrategy.Unix.cs} (99%) rename src/libraries/System.Private.CoreLib/src/System/IO/Strategies/{LegacyFileStreamStrategy.Windows.cs => Net5CompatFileStreamStrategy.Windows.cs} (99%) rename src/libraries/System.Private.CoreLib/src/System/IO/Strategies/{LegacyFileStreamStrategy.cs => Net5CompatFileStreamStrategy.cs} (97%) diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index ddb5ae2602df..75879136f329 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -237,9 +237,9 @@ private static Version GetICUVersion() version & 0xFF); } - private static readonly Lazy _legacyFileStream = new Lazy(() => GetStaticNonPublicBooleanPropertyValue("System.IO.FileStreamHelpers", "UseLegacyStrategy")); + private static readonly Lazy _net5CompatFileStream = new Lazy(() => GetStaticNonPublicBooleanPropertyValue("System.IO.FileStreamHelpers", "UseNet5CompatStrategy")); - public static bool IsLegacyFileStreamEnabled => _legacyFileStream.Value; + public static bool IsNet5CompatFileStreamEnabled => _net5CompatFileStream.Value; private static bool GetIsInContainer() { diff --git a/src/libraries/System.IO.FileSystem/System.IO.FileSystem.sln b/src/libraries/System.IO.FileSystem/System.IO.FileSystem.sln index 64eedb7687ce..93f9297296e4 100644 --- a/src/libraries/System.IO.FileSystem/System.IO.FileSystem.sln +++ b/src/libraries/System.IO.FileSystem/System.IO.FileSystem.sln @@ -25,7 +25,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{32A31E04-255 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D9FB1730-B750-4C0D-8D24-8C992DEB6034}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.IO.FileSystem.Legacy.Tests", "tests\LegacyTests\System.IO.FileSystem.Legacy.Tests.csproj", "{48E07F12-8597-40DE-8A37-CCBEB9D54012}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.IO.FileSystem.Net5Compat.Tests", "tests\Net5CompatTests\System.IO.FileSystem.Net5Compat.Tests.csproj", "{48E07F12-8597-40DE-8A37-CCBEB9D54012}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StreamConformanceTests", "..\Common\tests\StreamConformanceTests\StreamConformanceTests.csproj", "{FEF03BCC-509F-4646-9132-9DE27FA3DA6F}" EndProject diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/SafeFileHandle.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/SafeFileHandle.cs index c726519b0137..40064cd42c35 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/SafeFileHandle.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/SafeFileHandle.cs @@ -66,13 +66,13 @@ public void AccessFlushesFileClosesHandle() } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsLegacyFileStreamEnabled))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNet5CompatFileStreamEnabled))] public async Task ThrowWhenHandlePositionIsChanged_sync() { await ThrowWhenHandlePositionIsChanged(useAsync: false); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported), nameof(PlatformDetection.IsLegacyFileStreamEnabled))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported), nameof(PlatformDetection.IsNet5CompatFileStreamEnabled))] public async Task ThrowWhenHandlePositionIsChanged_async() { await ThrowWhenHandlePositionIsChanged(useAsync: true); @@ -110,7 +110,7 @@ private async Task ThrowWhenHandlePositionIsChanged(bool useAsync) && OperatingSystem.IsWindows() // ReadAsync which in this case (single byte written to buffer) calls FlushAsync is now 100% async // so it does not complete synchronously anymore - && PlatformDetection.IsLegacyFileStreamEnabled) + && PlatformDetection.IsNet5CompatFileStreamEnabled) { Assert.Throws(() => FSAssert.CompletesSynchronously(fs.ReadAsync(new byte[1], 0, 1))); } diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/WriteAsync.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/WriteAsync.cs index fb4c02f0a064..9e4db63e6fbc 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/WriteAsync.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/WriteAsync.cs @@ -227,7 +227,7 @@ public async Task ManyConcurrentWriteAsyncs_OuterLoop( // The side effect of this is that the Position of FileStream is not updated until // the lock is released by a previous operation. // So now all WriteAsync calls should be awaited before starting another async file operation. - if (PlatformDetection.IsLegacyFileStreamEnabled) + if (PlatformDetection.IsNet5CompatFileStreamEnabled) { Assert.Equal((i + 1) * writeSize, fs.Position); } diff --git a/src/libraries/System.IO.FileSystem/tests/LegacyTests/LegacySwitchTests.cs b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/Net5CompatSwitchTests.cs similarity index 85% rename from src/libraries/System.IO.FileSystem/tests/LegacyTests/LegacySwitchTests.cs rename to src/libraries/System.IO.FileSystem/tests/Net5CompatTests/Net5CompatSwitchTests.cs index c930cee89335..bb2d05086cf0 100644 --- a/src/libraries/System.IO.FileSystem/tests/LegacyTests/LegacySwitchTests.cs +++ b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/Net5CompatSwitchTests.cs @@ -6,7 +6,7 @@ namespace System.IO.Tests { - public class LegacySwitchTests + public class Net5CompatSwitchTests { [Fact] public static void LegacySwitchIsHonored() @@ -20,7 +20,7 @@ public static void LegacySwitchIsHonored() .GetField("_strategy", BindingFlags.NonPublic | BindingFlags.Instance) .GetValue(fileStream); - Assert.DoesNotContain(strategy.GetType().FullName, "Legacy"); + Assert.DoesNotContain("Net5Compat", strategy.GetType().FullName); } File.Delete(filePath); diff --git a/src/libraries/System.IO.FileSystem/tests/LegacyTests/System.IO.FileSystem.Legacy.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj similarity index 92% rename from src/libraries/System.IO.FileSystem/tests/LegacyTests/System.IO.FileSystem.Legacy.Tests.csproj rename to src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj index b1cf5ce99b0c..51daca942997 100644 --- a/src/libraries/System.IO.FileSystem/tests/LegacyTests/System.IO.FileSystem.Legacy.Tests.csproj +++ b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj @@ -2,7 +2,7 @@ true true - + $(NetCoreAppCurrent)-windows --working-dir=/test-dir diff --git a/src/libraries/System.IO.FileSystem/tests/LegacyTests/runtimeconfig.template.json b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/runtimeconfig.template.json similarity index 100% rename from src/libraries/System.IO.FileSystem/tests/LegacyTests/runtimeconfig.template.json rename to src/libraries/System.IO.FileSystem/tests/Net5CompatTests/runtimeconfig.template.json diff --git a/src/libraries/System.IO/System.IO.sln b/src/libraries/System.IO/System.IO.sln index 7d8ebc2c3da2..26a6942c7107 100644 --- a/src/libraries/System.IO/System.IO.sln +++ b/src/libraries/System.IO/System.IO.sln @@ -23,7 +23,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{9FDAA57A-696 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D9FD8082-D04C-4DA8-9F4C-261D1C65A6D3}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.IO.Legacy.Tests", "tests\LegacyTests\System.IO.Legacy.Tests.csproj", "{0217540D-FA86-41B3-9754-7BB5096ABA3E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.IO.Net5Compat.Tests", "tests\Net5CompatTests\System.IO.Net5Compat.Tests.csproj", "{0217540D-FA86-41B3-9754-7BB5096ABA3E}" EndProject Global GlobalSection(NestedProjects) = preSolution diff --git a/src/libraries/System.IO/tests/LegacyTests/System.IO.Legacy.Tests.csproj b/src/libraries/System.IO/tests/Net5CompatTests/System.IO.Net5Compat.Tests.csproj similarity index 92% rename from src/libraries/System.IO/tests/LegacyTests/System.IO.Legacy.Tests.csproj rename to src/libraries/System.IO/tests/Net5CompatTests/System.IO.Net5Compat.Tests.csproj index bfc33e36807b..321e2f485088 100644 --- a/src/libraries/System.IO/tests/LegacyTests/System.IO.Legacy.Tests.csproj +++ b/src/libraries/System.IO/tests/Net5CompatTests/System.IO.Net5Compat.Tests.csproj @@ -4,7 +4,7 @@ true true true - + $(NetCoreAppCurrent)-windows diff --git a/src/libraries/System.IO/tests/LegacyTests/runtimeconfig.template.json b/src/libraries/System.IO/tests/Net5CompatTests/runtimeconfig.template.json similarity index 100% rename from src/libraries/System.IO/tests/LegacyTests/runtimeconfig.template.json rename to src/libraries/System.IO/tests/Net5CompatTests/runtimeconfig.template.json diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 59c43653886e..0bd2bbe66858 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -428,7 +428,7 @@ - + @@ -1651,7 +1651,7 @@ - + @@ -1858,9 +1858,9 @@ - - - + + + diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamCompletionSource.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamCompletionSource.Win32.cs index c23f04dc64b1..679d62422a4a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamCompletionSource.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamCompletionSource.Win32.cs @@ -10,7 +10,7 @@ namespace System.IO.Strategies { - // to avoid code duplicaiton of FileStreamCompletionSource for LegacyFileStreamStrategy and AsyncWindowsFileStreamStrategy + // to avoid code duplicaiton of FileStreamCompletionSource for Net5CompatFileStreamStrategy and AsyncWindowsFileStreamStrategy // we have created the following interface that is a common contract for both of them internal interface IFileStreamCompletionSourceStrategy { @@ -237,7 +237,7 @@ public static FileStreamCompletionSource Create(IFileStreamCompletionSourceStrat // MemoryFileStreamCompletionSource, which Retains the memory, which will result in less pinning in the case // where the underlying memory is backed by pre-pinned buffers. return preallocatedOverlapped != null && MemoryMarshal.TryGetArray(memory, out ArraySegment buffer) - && preallocatedOverlapped.IsUserObject(buffer.Array) // preallocatedOverlapped is allocated when BufferedStream|LegacyFileStreamStrategy allocates the buffer + && preallocatedOverlapped.IsUserObject(buffer.Array) // preallocatedOverlapped is allocated when BufferedStream|Net5CompatFileStreamStrategy allocates the buffer ? new FileStreamCompletionSource(strategy, preallocatedOverlapped, numBufferedBytesRead, buffer.Array) : new MemoryFileStreamCompletionSource(strategy, numBufferedBytesRead, memory); } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Unix.cs index 9af81a5136d0..0d2c5df52be3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Unix.cs @@ -15,10 +15,10 @@ internal static partial class FileStreamHelpers { // in the future we are most probably going to introduce more strategies (io_uring etc) private static FileStreamStrategy ChooseStrategyCore(SafeFileHandle handle, FileAccess access, FileShare share, int bufferSize, bool isAsync) - => new LegacyFileStreamStrategy(handle, access, bufferSize, isAsync); + => new Net5CompatFileStreamStrategy(handle, access, bufferSize, isAsync); private static FileStreamStrategy ChooseStrategyCore(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) - => new LegacyFileStreamStrategy(path, mode, access, share, bufferSize, options); + => new Net5CompatFileStreamStrategy(path, mode, access, share, bufferSize, options); internal static SafeFileHandle OpenHandle(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs index 661e84b10f67..fd3218f6eb4c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs @@ -21,9 +21,9 @@ internal static partial class FileStreamHelpers private static FileStreamStrategy ChooseStrategyCore(SafeFileHandle handle, FileAccess access, FileShare share, int bufferSize, bool isAsync) { - if (UseLegacyStrategy) + if (UseNet5CompatStrategy) { - return new LegacyFileStreamStrategy(handle, access, bufferSize, isAsync); + return new Net5CompatFileStreamStrategy(handle, access, bufferSize, isAsync); } WindowsFileStreamStrategy strategy = isAsync @@ -35,9 +35,9 @@ private static FileStreamStrategy ChooseStrategyCore(SafeFileHandle handle, File private static FileStreamStrategy ChooseStrategyCore(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) { - if (UseLegacyStrategy) + if (UseNet5CompatStrategy) { - return new LegacyFileStreamStrategy(path, mode, access, share, bufferSize, options); + return new Net5CompatFileStreamStrategy(path, mode, access, share, bufferSize, options); } WindowsFileStreamStrategy strategy = (options & FileOptions.Asynchronous) != 0 @@ -558,7 +558,7 @@ internal static async Task AsyncModeCopyToAsync(SafeFileHandle handle, string? p } } - /// Used by AsyncWindowsFileStreamStrategy and LegacyFileStreamStrategy CopyToAsync to enable awaiting the result of an overlapped I/O operation with minimal overhead. + /// Used by AsyncWindowsFileStreamStrategy and Net5CompatFileStreamStrategy CopyToAsync to enable awaiting the result of an overlapped I/O operation with minimal overhead. private sealed unsafe class AsyncCopyToAwaitable : ICriticalNotifyCompletion { /// Sentinel object used to indicate that the I/O operation has completed before being awaited. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.cs index 62e344db6e4f..d64677b48efe 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.cs @@ -8,9 +8,9 @@ namespace System.IO.Strategies internal static partial class FileStreamHelpers { // It's enabled by default. We are going to change that once we fix #16354, #25905 and #24847. - internal static bool UseLegacyStrategy { get; } = GetLegacyFileStreamSetting(); + internal static bool UseNet5CompatStrategy { get; } = GetNet5CompatFileStreamSetting(); - private static bool GetLegacyFileStreamSetting() + private static bool GetNet5CompatFileStreamSetting() { if (AppContext.TryGetSwitch("System.IO.UseNet5CompatFileStream", out bool fileConfig)) { @@ -19,7 +19,7 @@ private static bool GetLegacyFileStreamSetting() string? envVar = Environment.GetEnvironmentVariable("DOTNET_SYSTEM_IO_USENET5COMPATFILESTREAM"); return envVar is null - ? true // legacy is currently enabled by default; + ? true // Net5Compat is currently enabled by default; : bool.IsTrueStringIgnoreCase(envVar) || envVar.Equals("1"); } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.Lock.OSX.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Lock.OSX.cs similarity index 86% rename from src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.Lock.OSX.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Lock.OSX.cs index 5d00486b26b0..d3ac0b7cf7f1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.Lock.OSX.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Lock.OSX.cs @@ -3,7 +3,7 @@ namespace System.IO.Strategies { - internal sealed partial class LegacyFileStreamStrategy : FileStreamStrategy + internal sealed partial class Net5CompatFileStreamStrategy : FileStreamStrategy { internal override void Lock(long position, long length) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.Lock.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Lock.Unix.cs similarity index 93% rename from src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.Lock.Unix.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Lock.Unix.cs index d6a534ffb259..20e065d6317f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.Lock.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Lock.Unix.cs @@ -3,7 +3,7 @@ namespace System.IO.Strategies { - internal sealed partial class LegacyFileStreamStrategy : FileStreamStrategy + internal sealed partial class Net5CompatFileStreamStrategy : FileStreamStrategy { /// Prevents other processes from reading from or writing to the FileStream. /// The beginning of the range to lock. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Unix.cs similarity index 99% rename from src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.Unix.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Unix.cs index a9614b1d5538..eb1cebc37a7f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Unix.cs @@ -11,7 +11,7 @@ namespace System.IO.Strategies { /// Provides an implementation of a file stream for Unix files. - internal sealed partial class LegacyFileStreamStrategy : FileStreamStrategy + internal sealed partial class Net5CompatFileStreamStrategy : FileStreamStrategy { /// File mode. private FileMode _mode; @@ -224,7 +224,7 @@ public override ValueTask DisposeAsync() // override may already exist on a derived type. if (_useAsyncIO && _writePos > 0) { - return new ValueTask(Task.Factory.StartNew(static s => ((LegacyFileStreamStrategy)s!).Dispose(), this, + return new ValueTask(Task.Factory.StartNew(static s => ((Net5CompatFileStreamStrategy)s!).Dispose(), this, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default)); } @@ -449,7 +449,7 @@ private unsafe int ReadNative(Span buffer) // whereas on Windows it may happen before the write has completed. Debug.Assert(t.Status == TaskStatus.RanToCompletion); - var thisRef = (LegacyFileStreamStrategy)s!; + var thisRef = (Net5CompatFileStreamStrategy)s!; Debug.Assert(thisRef._asyncState != null); try { @@ -610,7 +610,7 @@ private ValueTask WriteAsyncInternal(ReadOnlyMemory source, CancellationTo // whereas on Windows it may happen before the write has completed. Debug.Assert(t.Status == TaskStatus.RanToCompletion); - var thisRef = (LegacyFileStreamStrategy)s!; + var thisRef = (Net5CompatFileStreamStrategy)s!; Debug.Assert(thisRef._asyncState != null); try { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Windows.cs similarity index 99% rename from src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.Windows.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Windows.cs index e562f0c2e215..5ed9f5937aa2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Windows.cs @@ -36,7 +36,7 @@ namespace System.IO.Strategies { - internal sealed partial class LegacyFileStreamStrategy : FileStreamStrategy, IFileStreamCompletionSourceStrategy + internal sealed partial class Net5CompatFileStreamStrategy : FileStreamStrategy, IFileStreamCompletionSourceStrategy { private bool _canSeek; private bool _isPipe; // Whether to disable async buffering code. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.cs similarity index 97% rename from src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.cs index 6d06e9614056..03ca3533e717 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.cs @@ -9,8 +9,8 @@ namespace System.IO.Strategies { - // This type is partial so we can avoid code duplication between Windows and Unix Legacy implementations - internal sealed partial class LegacyFileStreamStrategy : FileStreamStrategy + // This type is partial so we can avoid code duplication between Windows and Unix Net5Compat implementations + internal sealed partial class Net5CompatFileStreamStrategy : FileStreamStrategy { private byte[]? _buffer; private readonly int _bufferLength; @@ -61,7 +61,7 @@ internal sealed partial class LegacyFileStreamStrategy : FileStreamStrategy /// Whether the file stream's handle has been exposed. private bool _exposedHandle; - internal LegacyFileStreamStrategy(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) + internal Net5CompatFileStreamStrategy(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) { _exposedHandle = true; _bufferLength = bufferSize; @@ -78,7 +78,7 @@ internal LegacyFileStreamStrategy(SafeFileHandle handle, FileAccess access, int _fileHandle = handle; } - internal LegacyFileStreamStrategy(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) + internal Net5CompatFileStreamStrategy(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) { string fullPath = Path.GetFullPath(path); @@ -105,7 +105,7 @@ internal LegacyFileStreamStrategy(string path, FileMode mode, FileAccess access, } } - ~LegacyFileStreamStrategy() => Dispose(false); // mandatory to Flush the write buffer + ~Net5CompatFileStreamStrategy() => Dispose(false); // mandatory to Flush the write buffer internal override void DisposeInternal(bool disposing) => Dispose(disposing); From a913740aac5353212a9b95283ae38b9964f18aa5 Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Wed, 31 Mar 2021 12:02:11 +0100 Subject: [PATCH 68/98] Fix typo in `string.Join` (#50467) Follow-up #44032. --- .../System.Private.CoreLib/src/System/String.Manipulation.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs b/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs index 690c26a7793e..efb7f5d50d79 100644 --- a/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs +++ b/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs @@ -578,9 +578,9 @@ private static string JoinCore(ReadOnlySpan separator, string?[] value, in public static string Join(string? separator, IEnumerable values) { - if (values is List valuesIList) + if (values is List valuesList) { - return JoinCore(separator.AsSpan(), CollectionsMarshal.AsSpan(valuesIList)); + return JoinCore(separator.AsSpan(), CollectionsMarshal.AsSpan(valuesList)); } if (values is string?[] valuesArray) From b47094da840f8b775fbadea5c5739022a951ac84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20Be=C5=88ovic?= Date: Wed, 31 Mar 2021 13:13:59 +0200 Subject: [PATCH 69/98] Allow bounded channel to be created with drop delegate (#50331) * Bounded channel can be created with drop delegate. - Add additional CreateBounded overload with delegate parameter that will be called when item is being dropped from channel - Added unit tests * Fix typo in comment. * Apply suggestions from code review Co-authored-by: Stephen Toub * Call drop delegate outside of lock statement. * Use overload of CreateBounded method instead of calling ctor directly. * Code review suggestions refactor. * Move Monitor.Enter before try and use local scoped parent variable everywhere. * Drop delegate should not be called while sync lock is held. Enqueuing of new item should be done while sync lock is being held. Added additional tests. * Rerun gitlab CI. * Do not run deadlock test for bounded channels if platform do not support threading. * Apply suggestions from code review Co-authored-by: Ivan Benovic Co-authored-by: Stephen Toub --- .../ref/System.Threading.Channels.cs | 1 + .../Threading/Channels/BoundedChannel.cs | 70 ++++-- .../src/System/Threading/Channels/Channel.cs | 16 +- .../tests/BoundedChannelTests.cs | 223 ++++++++++++++++++ 4 files changed, 289 insertions(+), 21 deletions(-) diff --git a/src/libraries/System.Threading.Channels/ref/System.Threading.Channels.cs b/src/libraries/System.Threading.Channels/ref/System.Threading.Channels.cs index 6422594f03a0..8c093689cb84 100644 --- a/src/libraries/System.Threading.Channels/ref/System.Threading.Channels.cs +++ b/src/libraries/System.Threading.Channels/ref/System.Threading.Channels.cs @@ -23,6 +23,7 @@ public static partial class Channel { public static System.Threading.Channels.Channel CreateBounded(int capacity) { throw null; } public static System.Threading.Channels.Channel CreateBounded(System.Threading.Channels.BoundedChannelOptions options) { throw null; } + public static System.Threading.Channels.Channel CreateBounded(BoundedChannelOptions options, Action? itemDropped) { throw null; } public static System.Threading.Channels.Channel CreateUnbounded() { throw null; } public static System.Threading.Channels.Channel CreateUnbounded(System.Threading.Channels.UnboundedChannelOptions options) { throw null; } } diff --git a/src/libraries/System.Threading.Channels/src/System/Threading/Channels/BoundedChannel.cs b/src/libraries/System.Threading.Channels/src/System/Threading/Channels/BoundedChannel.cs index c8e074649365..bc7b18fd3488 100644 --- a/src/libraries/System.Threading.Channels/src/System/Threading/Channels/BoundedChannel.cs +++ b/src/libraries/System.Threading.Channels/src/System/Threading/Channels/BoundedChannel.cs @@ -15,6 +15,8 @@ internal sealed class BoundedChannel : Channel, IDebugEnumerable { /// The mode used when the channel hits its bound. private readonly BoundedChannelFullMode _mode; + /// The delegate that will be invoked when the channel hits its bound and an item is dropped from the channel. + private readonly Action? _itemDropped; /// Task signaled when the channel has completed. private readonly TaskCompletionSource _completion; /// The maximum capacity of the channel. @@ -40,12 +42,14 @@ internal sealed class BoundedChannel : Channel, IDebugEnumerable /// The positive bounded capacity for the channel. /// The mode used when writing to a full channel. /// Whether to force continuations to be executed asynchronously. - internal BoundedChannel(int bufferedCapacity, BoundedChannelFullMode mode, bool runContinuationsAsynchronously) + /// Delegate that will be invoked when an item is dropped from the channel. See . + internal BoundedChannel(int bufferedCapacity, BoundedChannelFullMode mode, bool runContinuationsAsynchronously, Action? itemDropped) { Debug.Assert(bufferedCapacity > 0); _bufferedCapacity = bufferedCapacity; _mode = mode; _runContinuationsAsynchronously = runContinuationsAsynchronously; + _itemDropped = itemDropped; _completion = new TaskCompletionSource(runContinuationsAsynchronously ? TaskCreationOptions.RunContinuationsAsynchronously : TaskCreationOptions.None); Reader = new BoundedChannelReader(this); Writer = new BoundedChannelWriter(this); @@ -332,8 +336,12 @@ public override bool TryWrite(T item) AsyncOperation? waitingReadersTail = null; BoundedChannel parent = _parent; - lock (parent.SyncObj) + + bool releaseLock = false; + try { + Monitor.Enter(parent.SyncObj, ref releaseLock); + parent.AssertInvariants(); // If we're done writing, nothing more to do. @@ -393,24 +401,35 @@ public override bool TryWrite(T item) { // The channel is full. Just ignore the item being added // but say we added it. + Monitor.Exit(parent.SyncObj); + releaseLock = false; + parent._itemDropped?.Invoke(item); return true; } else { // The channel is full, and we're in a dropping mode. - // Drop either the oldest or the newest and write the new item. - if (parent._mode == BoundedChannelFullMode.DropNewest) - { - parent._items.DequeueTail(); - } - else - { + // Drop either the oldest or the newest + T droppedItem = parent._mode == BoundedChannelFullMode.DropNewest ? + parent._items.DequeueTail() : parent._items.DequeueHead(); - } + parent._items.EnqueueTail(item); + + Monitor.Exit(parent.SyncObj); + releaseLock = false; + parent._itemDropped?.Invoke(droppedItem); + return true; } } + finally + { + if (releaseLock) + { + Monitor.Exit(parent.SyncObj); + } + } // We either wrote the item already, or we're transferring it to the blocked reader we grabbed. if (blockedReader != null) @@ -492,8 +511,12 @@ public override ValueTask WriteAsync(T item, CancellationToken cancellationToken AsyncOperation? waitingReadersTail = null; BoundedChannel parent = _parent; - lock (parent.SyncObj) + + bool releaseLock = false; + try { + Monitor.Enter(parent.SyncObj, ref releaseLock); + parent.AssertInvariants(); // If we're done writing, trying to write is an error. @@ -569,24 +592,35 @@ public override ValueTask WriteAsync(T item, CancellationToken cancellationToken { // The channel is full and we're in ignore mode. // Ignore the item but say we accepted it. + Monitor.Exit(parent.SyncObj); + releaseLock = false; + parent._itemDropped?.Invoke(item); return default; } else { // The channel is full, and we're in a dropping mode. // Drop either the oldest or the newest and write the new item. - if (parent._mode == BoundedChannelFullMode.DropNewest) - { - parent._items.DequeueTail(); - } - else - { + T droppedItem = parent._mode == BoundedChannelFullMode.DropNewest ? + parent._items.DequeueTail() : parent._items.DequeueHead(); - } + parent._items.EnqueueTail(item); + + Monitor.Exit(parent.SyncObj); + releaseLock = false; + parent._itemDropped?.Invoke(droppedItem); + return default; } } + finally + { + if (releaseLock) + { + Monitor.Exit(parent.SyncObj); + } + } // We either wrote the item already, or we're transfering it to the blocked reader we grabbed. if (blockedReader != null) diff --git a/src/libraries/System.Threading.Channels/src/System/Threading/Channels/Channel.cs b/src/libraries/System.Threading.Channels/src/System/Threading/Channels/Channel.cs index f377993ec03d..156489276902 100644 --- a/src/libraries/System.Threading.Channels/src/System/Threading/Channels/Channel.cs +++ b/src/libraries/System.Threading.Channels/src/System/Threading/Channels/Channel.cs @@ -35,21 +35,31 @@ public static Channel CreateBounded(int capacity) throw new ArgumentOutOfRangeException(nameof(capacity)); } - return new BoundedChannel(capacity, BoundedChannelFullMode.Wait, runContinuationsAsynchronously: true); + return new BoundedChannel(capacity, BoundedChannelFullMode.Wait, runContinuationsAsynchronously: true, itemDropped: null); } - /// Creates a channel with the specified maximum capacity. + /// Creates a channel subject to the provided options. /// Specifies the type of data in the channel. /// Options that guide the behavior of the channel. /// The created channel. public static Channel CreateBounded(BoundedChannelOptions options) + { + return CreateBounded(options, itemDropped: null); + } + + /// Creates a channel subject to the provided options. + /// Specifies the type of data in the channel. + /// Options that guide the behavior of the channel. + /// Delegate that will be called when item is being dropped from channel. See . + /// The created channel. + public static Channel CreateBounded(BoundedChannelOptions options, Action? itemDropped) { if (options == null) { throw new ArgumentNullException(nameof(options)); } - return new BoundedChannel(options.Capacity, options.FullMode, !options.AllowSynchronousContinuations); + return new BoundedChannel(options.Capacity, options.FullMode, !options.AllowSynchronousContinuations, itemDropped); } } } diff --git a/src/libraries/System.Threading.Channels/tests/BoundedChannelTests.cs b/src/libraries/System.Threading.Channels/tests/BoundedChannelTests.cs index 4c8989d83c4d..6eb1d3fe8e73 100644 --- a/src/libraries/System.Threading.Channels/tests/BoundedChannelTests.cs +++ b/src/libraries/System.Threading.Channels/tests/BoundedChannelTests.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Microsoft.DotNet.XUnitExtensions; using Xunit; @@ -17,6 +19,17 @@ protected override Channel CreateFullChannel() return c; } + public static IEnumerable ChannelDropModes() + { + foreach (BoundedChannelFullMode mode in Enum.GetValues(typeof(BoundedChannelFullMode))) + { + if (mode != BoundedChannelFullMode.Wait) + { + yield return new object[] { mode }; + } + } + } + [Fact] public void Count_IncrementsDecrementsAsExpected() { @@ -249,6 +262,216 @@ public void WriteAsync_TryRead_Many_Ignore(int bufferedCapacity) Assert.Equal(0, result); } + [Fact] + public void DroppedDelegateNotCalledOnWaitMode_SyncWrites() + { + bool dropDelegateCalled = false; + + Channel c = Channel.CreateBounded(new BoundedChannelOptions(1) { FullMode = BoundedChannelFullMode.Wait }, + item => + { + dropDelegateCalled = true; + }); + + Assert.True(c.Writer.TryWrite(1)); + Assert.False(c.Writer.TryWrite(1)); + + Assert.False(dropDelegateCalled); + } + + [Fact] + public async Task DroppedDelegateNotCalledOnWaitMode_AsyncWrites() + { + bool dropDelegateCalled = false; + + Channel c = Channel.CreateBounded(new BoundedChannelOptions(1) { FullMode = BoundedChannelFullMode.Wait }, + item => + { + dropDelegateCalled = true; + }); + + // First async write should pass + await c.Writer.WriteAsync(1); + + // Second write should wait + var secondWriteTask = c.Writer.WriteAsync(2); + Assert.False(secondWriteTask.IsCompleted); + + // Read from channel to free up space + var readItem = await c.Reader.ReadAsync(); + // Second write should complete + await secondWriteTask; + + // No dropped delegate should be called + Assert.False(dropDelegateCalled); + } + + [Theory] + [MemberData(nameof(ChannelDropModes))] + public void DroppedDelegateIsNull_SyncWrites(BoundedChannelFullMode boundedChannelFullMode) + { + Channel c = Channel.CreateBounded(new BoundedChannelOptions(1) { FullMode = boundedChannelFullMode }, itemDropped: null); + + Assert.True(c.Writer.TryWrite(5)); + Assert.True(c.Writer.TryWrite(5)); + } + + [Theory] + [MemberData(nameof(ChannelDropModes))] + public async Task DroppedDelegateIsNull_AsyncWrites(BoundedChannelFullMode boundedChannelFullMode) + { + Channel c = Channel.CreateBounded(new BoundedChannelOptions(1) { FullMode = boundedChannelFullMode }, itemDropped: null); + + await c.Writer.WriteAsync(5); + await c.Writer.WriteAsync(5); + } + + [Theory] + [MemberData(nameof(ChannelDropModes))] + public void DroppedDelegateCalledOnChannelFull_SyncWrites(BoundedChannelFullMode boundedChannelFullMode) + { + var droppedItems = new HashSet(); + + void AddDroppedItem(int itemDropped) + { + Assert.True(droppedItems.Add(itemDropped)); + } + + const int channelCapacity = 10; + var c = Channel.CreateBounded(new BoundedChannelOptions(channelCapacity) + { + FullMode = boundedChannelFullMode + }, AddDroppedItem); + + for (int i = 0; i < channelCapacity; i++) + { + Assert.True(c.Writer.TryWrite(i)); + } + + // No dropped delegate should be called while channel is not full + Assert.Empty(droppedItems); + + for (int i = channelCapacity; i < channelCapacity + 10; i++) + { + Assert.True(c.Writer.TryWrite(i)); + } + + // Assert expected number of dropped items delegate calls + Assert.Equal(10, droppedItems.Count); + } + + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] + [MemberData(nameof(ChannelDropModes))] + public void DroppedDelegateCalledAfterLockReleased_SyncWrites(BoundedChannelFullMode boundedChannelFullMode) + { + Channel c = null; + bool dropDelegateCalled = false; + + c = Channel.CreateBounded(new BoundedChannelOptions(1) + { + FullMode = boundedChannelFullMode + }, (droppedItem) => + { + if (dropDelegateCalled) + { + // Prevent infinite callbacks being called + return; + } + + dropDelegateCalled = true; + + // Dropped delegate should not be called while holding the channel lock. + // Verify this by trying to write into the channel from different thread. + // If lock is held during callback, this should effecitvely cause deadlock. + var mres = new ManualResetEventSlim(); + ThreadPool.QueueUserWorkItem(delegate + { + c.Writer.TryWrite(3); + mres.Set(); + }); + + mres.Wait(); + }); + + Assert.True(c.Writer.TryWrite(1)); + Assert.True(c.Writer.TryWrite(2)); + + Assert.True(dropDelegateCalled); + } + + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] + [MemberData(nameof(ChannelDropModes))] + public async Task DroppedDelegateCalledAfterLockReleased_AsyncWrites(BoundedChannelFullMode boundedChannelFullMode) + { + Channel c = null; + bool dropDelegateCalled = false; + + c = Channel.CreateBounded(new BoundedChannelOptions(1) + { + FullMode = boundedChannelFullMode + }, (droppedItem) => + { + if (dropDelegateCalled) + { + // Prevent infinite callbacks being called + return; + } + + dropDelegateCalled = true; + + // Dropped delegate should not be called while holding the channel synchronisation lock. + // Verify this by trying to write into the channel from different thread. + // If lock is held during callback, this should effecitvely cause deadlock. + var mres = new ManualResetEventSlim(); + ThreadPool.QueueUserWorkItem(delegate + { + c.Writer.TryWrite(11); + mres.Set(); + }); + + mres.Wait(); + }); + + await c.Writer.WriteAsync(1); + await c.Writer.WriteAsync(2); + + Assert.True(dropDelegateCalled); + } + + [Theory] + [MemberData(nameof(ChannelDropModes))] + public async Task DroppedDelegateCalledOnChannelFull_AsyncWrites(BoundedChannelFullMode boundedChannelFullMode) + { + var droppedItems = new HashSet(); + + void AddDroppedItem(int itemDropped) + { + Assert.True(droppedItems.Add(itemDropped)); + } + + const int channelCapacity = 10; + var c = Channel.CreateBounded(new BoundedChannelOptions(channelCapacity) + { + FullMode = boundedChannelFullMode + }, AddDroppedItem); + + for (int i = 0; i < channelCapacity; i++) + { + await c.Writer.WriteAsync(i); + } + + // No dropped delegate should be called while channel is not full + Assert.Empty(droppedItems); + + for (int i = channelCapacity; i < channelCapacity + 10; i++) + { + await c.Writer.WriteAsync(i); + } + + // Assert expected number of dropped items delegate calls + Assert.Equal(10, droppedItems.Count); + } + [Fact] public async Task CancelPendingWrite_Reading_DataTransferredFromCorrectWriter() { From 2784aadb026463eb5b1189b912369f359a5008e3 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 31 Mar 2021 09:30:24 -0400 Subject: [PATCH 70/98] Add AsyncMethodBuilderOverride and PoolingAsyncValueTaskMethodBuilders (#50116) * Add AsyncMethodBuilderOverride and PoolingAsyncValueTaskMethodBuilders * Revise based on C# LDM changes to model * Fix API compat errors --- .../System.Private.CoreLib.Shared.projitems | 2 + .../AsyncMethodBuilderAttribute.cs | 5 +- .../AsyncValueTaskMethodBuilder.cs | 114 +--- .../AsyncValueTaskMethodBuilderT.cs | 433 +------------ .../PoolingAsyncValueTaskMethodBuilder.cs | 121 ++++ .../PoolingAsyncValueTaskMethodBuilderT.cs | 473 ++++++++++++++ .../System.Runtime/ref/System.Runtime.cs | 29 +- .../tests/AsyncValueTaskMethodBuilderTests.cs | 63 -- ...PoolingAsyncValueTaskMethodBuilderTests.cs | 579 ++++++++++++++++++ ...em.Threading.Tasks.Extensions.Tests.csproj | 1 + .../ApiCompatBaseline.PreviousNetCoreApp.txt | 3 +- ...patBaseline.netcoreapp.netstandardOnly.txt | 3 +- 12 files changed, 1242 insertions(+), 584 deletions(-) create mode 100644 src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/PoolingAsyncValueTaskMethodBuilder.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/PoolingAsyncValueTaskMethodBuilderT.cs create mode 100644 src/libraries/System.Threading.Tasks.Extensions/tests/PoolingAsyncValueTaskMethodBuilderTests.cs diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 0bd2bbe66858..ecdd5da8509f 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -693,6 +693,8 @@ + + diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncMethodBuilderAttribute.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncMethodBuilderAttribute.cs index 8c65d2f04173..026e43c24a45 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncMethodBuilderAttribute.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncMethodBuilderAttribute.cs @@ -5,9 +5,10 @@ namespace System.Runtime.CompilerServices { /// /// Indicates the type of the async method builder that should be used by a language compiler to - /// build the attributed type when used as the return type of an async method. + /// build the attributed async method or to build the attributed type when used as the return type + /// of an async method. /// - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Delegate | AttributeTargets.Enum, Inherited = false, AllowMultiple = false)] + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Delegate | AttributeTargets.Enum | AttributeTargets.Method, Inherited = false, AllowMultiple = false)] public sealed class AsyncMethodBuilderAttribute : Attribute { /// Initializes the . diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs index d725b51ca051..bab1cdf3376e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs @@ -3,9 +3,6 @@ using System.Runtime.InteropServices; using System.Threading.Tasks; -using Internal.Runtime.CompilerServices; - -using StateMachineBox = System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder.StateMachineBox; namespace System.Runtime.CompilerServices { @@ -13,23 +10,14 @@ namespace System.Runtime.CompilerServices [StructLayout(LayoutKind.Auto)] public struct AsyncValueTaskMethodBuilder { - /// true if we should use reusable boxes for async completions of ValueTask methods; false if we should use tasks. - /// - /// We rely on tiered compilation turning this into a const and doing dead code elimination to make checks on this efficient. - /// It's also required for safety that this value never changes once observed, as Unsafe.As casts are employed based on its value. - /// - internal static readonly bool s_valueTaskPoolingEnabled = GetPoolAsyncValueTasksSwitch(); - /// Maximum number of boxes that are allowed to be cached per state machine type. - internal static readonly int s_valueTaskPoolingCacheSize = GetPoolAsyncValueTasksLimitValue(); - /// Sentinel object used to indicate that the builder completed synchronously and successfully. - private static readonly object s_syncSuccessSentinel = AsyncValueTaskMethodBuilder.s_syncSuccessSentinel; + private static readonly Task s_syncSuccessSentinel = AsyncValueTaskMethodBuilder.s_syncSuccessSentinel; - /// The wrapped state machine box or task, based on the value of . + /// The wrapped task. /// /// If the operation completed synchronously and successfully, this will be . /// - private object? m_task; // Debugger depends on the exact name of this field. + private Task? m_task; // Debugger depends on the exact name of this field. /// Creates an instance of the struct. /// The initialized instance. @@ -39,8 +27,7 @@ public struct AsyncValueTaskMethodBuilder /// The type of the state machine. /// The state machine instance, passed by reference. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Start(ref TStateMachine stateMachine) - where TStateMachine : IAsyncStateMachine => + public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine => AsyncMethodBuilderCore.Start(ref stateMachine); /// Associates the builder with the specified state machine. @@ -55,29 +42,16 @@ public void SetResult() { m_task = s_syncSuccessSentinel; } - else if (s_valueTaskPoolingEnabled) - { - Unsafe.As(m_task).SetResult(default); - } else { - AsyncTaskMethodBuilder.SetExistingTaskResult(Unsafe.As>(m_task), default); + AsyncTaskMethodBuilder.SetExistingTaskResult(m_task, default); } } /// Marks the task as failed and binds the specified exception to the task. /// The exception to bind to the task. - public void SetException(Exception exception) - { - if (s_valueTaskPoolingEnabled) - { - AsyncValueTaskMethodBuilder.SetException(exception, ref Unsafe.As(ref m_task)); - } - else - { - AsyncTaskMethodBuilder.SetException(exception, ref Unsafe.As?>(ref m_task)); - } - } + public void SetException(Exception exception) => + AsyncTaskMethodBuilder.SetException(exception, ref m_task); /// Gets the task for this builder. public ValueTask Task @@ -94,27 +68,11 @@ public ValueTask Task // or it should be completing asynchronously, in which case AwaitUnsafeOnCompleted would have similarly // initialized m_task to a state machine object. However, if the type is used manually (not via // compiler-generated code) and accesses Task directly, we force it to be initialized. Things will then - // "work" but in a degraded mode, as we don't know the TStateMachine type here, and thus we use a box around - // the interface instead. + // "work" but in a degraded mode, as we don't know the TStateMachine type here, and thus we use a normal + // task object instead. - if (s_valueTaskPoolingEnabled) - { - var box = Unsafe.As(m_task); - if (box is null) - { - m_task = box = AsyncValueTaskMethodBuilder.CreateWeaklyTypedStateMachineBox(); - } - return new ValueTask(box, box.Version); - } - else - { - var task = Unsafe.As?>(m_task); - if (task is null) - { - m_task = task = new Task(); // base task used rather than box to minimize size when used as manual promise - } - return new ValueTask(task); - } + Task? task = m_task ??= new Task(); // base task used rather than box to minimize size when used as manual promise + return new ValueTask(task); } } @@ -125,17 +83,8 @@ public ValueTask Task /// The state machine. public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion - where TStateMachine : IAsyncStateMachine - { - if (s_valueTaskPoolingEnabled) - { - AsyncValueTaskMethodBuilder.AwaitOnCompleted(ref awaiter, ref stateMachine, ref Unsafe.As(ref m_task)); - } - else - { - AsyncTaskMethodBuilder.AwaitOnCompleted(ref awaiter, ref stateMachine, ref Unsafe.As?>(ref m_task)); - } - } + where TStateMachine : IAsyncStateMachine => + AsyncTaskMethodBuilder.AwaitOnCompleted(ref awaiter, ref stateMachine, ref m_task); /// Schedules the state machine to proceed to the next action when the specified awaiter completes. /// The type of the awaiter. @@ -145,17 +94,8 @@ public void AwaitOnCompleted(ref TAwaiter awaiter, ref [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion - where TStateMachine : IAsyncStateMachine - { - if (s_valueTaskPoolingEnabled) - { - AsyncValueTaskMethodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine, ref Unsafe.As(ref m_task)); - } - else - { - AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine, ref Unsafe.As?>(ref m_task)); - } - } + where TStateMachine : IAsyncStateMachine => + AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine, ref m_task); /// /// Gets an object that may be used to uniquely identify this builder to the debugger. @@ -165,28 +105,6 @@ public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter /// It must only be used by the debugger and tracing purposes, and only in a single-threaded manner /// when no other threads are in the middle of accessing this or other members that lazily initialize the box. /// - internal object ObjectIdForDebugger - { - get - { - if (m_task is null) - { - m_task = s_valueTaskPoolingEnabled ? (object) - AsyncValueTaskMethodBuilder.CreateWeaklyTypedStateMachineBox() : - AsyncTaskMethodBuilder.CreateWeaklyTypedStateMachineBox(); - } - - return m_task; - } - } - - private static bool GetPoolAsyncValueTasksSwitch() => - Environment.GetEnvironmentVariable("DOTNET_SYSTEM_THREADING_POOLASYNCVALUETASKS") is string value && - (bool.IsTrueStringIgnoreCase(value) || value == "1"); - - private static int GetPoolAsyncValueTasksLimitValue() => - int.TryParse(Environment.GetEnvironmentVariable("DOTNET_SYSTEM_THREADING_POOLASYNCVALUETASKSLIMIT"), out int result) && result > 0 ? - result : - Environment.ProcessorCount * 4; // arbitrary default value + internal object ObjectIdForDebugger => m_task ??= AsyncTaskMethodBuilder.CreateWeaklyTypedStateMachineBox(); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilderT.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilderT.cs index 856feb8d95a9..62499d54e761 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilderT.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilderT.cs @@ -1,13 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -using System.Threading; using System.Threading.Tasks; -using System.Threading.Tasks.Sources; -using Internal.Runtime.CompilerServices; namespace System.Runtime.CompilerServices { @@ -22,12 +17,10 @@ public struct AsyncValueTaskMethodBuilder /// is valid for the mode in which we're operating. As such, it's cached on the generic builder per TResult /// rather than having one sentinel instance for all types. /// - internal static readonly object s_syncSuccessSentinel = AsyncValueTaskMethodBuilder.s_valueTaskPoolingEnabled ? (object) - new SyncSuccessSentinelStateMachineBox() : - new Task(default(TResult)!); + internal static readonly Task s_syncSuccessSentinel = new Task(default(TResult)!); - /// The wrapped state machine or task. If the operation completed synchronously and successfully, this will be a sentinel object compared by reference identity. - private object? m_task; // Debugger depends on the exact name of this field. + /// The wrapped task. If the operation completed synchronously and successfully, this will be a sentinel object compared by reference identity. + private Task? m_task; // Debugger depends on the exact name of this field. /// The result for this builder if it's completed synchronously, in which case will be . private TResult _result; @@ -56,39 +49,16 @@ public void SetResult(TResult result) _result = result; m_task = s_syncSuccessSentinel; } - else if (AsyncValueTaskMethodBuilder.s_valueTaskPoolingEnabled) - { - Unsafe.As(m_task).SetResult(result); - } else { - AsyncTaskMethodBuilder.SetExistingTaskResult(Unsafe.As>(m_task), result); + AsyncTaskMethodBuilder.SetExistingTaskResult(m_task, result); } } /// Marks the value task as failed and binds the specified exception to the value task. /// The exception to bind to the value task. - public void SetException(Exception exception) - { - if (AsyncValueTaskMethodBuilder.s_valueTaskPoolingEnabled) - { - SetException(exception, ref Unsafe.As(ref m_task)); - } - else - { - AsyncTaskMethodBuilder.SetException(exception, ref Unsafe.As?>(ref m_task)); - } - } - - internal static void SetException(Exception exception, [NotNull] ref StateMachineBox? boxFieldRef) - { - if (exception is null) - { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.exception); - } - - (boxFieldRef ??= CreateWeaklyTypedStateMachineBox()).SetException(exception); - } + public void SetException(Exception exception) => + AsyncTaskMethodBuilder.SetException(exception, ref m_task); /// Gets the value task for this builder. public ValueTask Task @@ -105,27 +75,11 @@ public ValueTask Task // or it should be completing asynchronously, in which case AwaitUnsafeOnCompleted would have similarly // initialized m_task to a state machine object. However, if the type is used manually (not via // compiler-generated code) and accesses Task directly, we force it to be initialized. Things will then - // "work" but in a degraded mode, as we don't know the TStateMachine type here, and thus we use a box around - // the interface instead. + // "work" but in a degraded mode, as we don't know the TStateMachine type here, and thus we use a + // normal task object instead. - if (AsyncValueTaskMethodBuilder.s_valueTaskPoolingEnabled) - { - var box = Unsafe.As(m_task); - if (box is null) - { - m_task = box = CreateWeaklyTypedStateMachineBox(); - } - return new ValueTask(box, box.Version); - } - else - { - var task = Unsafe.As?>(m_task); - if (task is null) - { - m_task = task = new Task(); // base task used rather than box to minimize size when used as manual promise - } - return new ValueTask(task); - } + Task? task = m_task ??= new Task(); // base task used rather than box to minimize size when used as manual promise + return new ValueTask(task); } } @@ -136,32 +90,8 @@ public ValueTask Task /// The state machine. public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion - where TStateMachine : IAsyncStateMachine - { - if (AsyncValueTaskMethodBuilder.s_valueTaskPoolingEnabled) - { - AwaitOnCompleted(ref awaiter, ref stateMachine, ref Unsafe.As(ref m_task)); - } - else - { - AsyncTaskMethodBuilder.AwaitOnCompleted(ref awaiter, ref stateMachine, ref Unsafe.As?>(ref m_task)); - } - } - - internal static void AwaitOnCompleted( - ref TAwaiter awaiter, ref TStateMachine stateMachine, ref StateMachineBox? box) - where TAwaiter : INotifyCompletion - where TStateMachine : IAsyncStateMachine - { - try - { - awaiter.OnCompleted(GetStateMachineBox(ref stateMachine, ref box).MoveNextAction); - } - catch (Exception e) - { - System.Threading.Tasks.Task.ThrowAsync(e, targetContext: null); - } - } + where TStateMachine : IAsyncStateMachine => + AsyncTaskMethodBuilder.AwaitOnCompleted(ref awaiter, ref stateMachine, ref m_task); /// Schedules the state machine to proceed to the next action when the specified awaiter completes. /// The type of the awaiter. @@ -171,106 +101,8 @@ internal static void AwaitOnCompleted( [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion - where TStateMachine : IAsyncStateMachine - { - if (AsyncValueTaskMethodBuilder.s_valueTaskPoolingEnabled) - { - AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine, ref Unsafe.As(ref m_task)); - } - else - { - AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine, ref Unsafe.As?>(ref m_task)); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void AwaitUnsafeOnCompleted( - ref TAwaiter awaiter, ref TStateMachine stateMachine, [NotNull] ref StateMachineBox? boxRef) - where TAwaiter : ICriticalNotifyCompletion - where TStateMachine : IAsyncStateMachine - { - IAsyncStateMachineBox box = GetStateMachineBox(ref stateMachine, ref boxRef); - AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted(ref awaiter, box); - } - - /// Gets the "boxed" state machine object. - /// Specifies the type of the async state machine. - /// The state machine. - /// A reference to the field containing the initialized state machine box. - /// The "boxed" state machine. - private static IAsyncStateMachineBox GetStateMachineBox( - ref TStateMachine stateMachine, - [NotNull] ref StateMachineBox? boxFieldRef) - where TStateMachine : IAsyncStateMachine - { - ExecutionContext? currentContext = ExecutionContext.Capture(); - - // Check first for the most common case: not the first yield in an async method. - // In this case, the first yield will have already "boxed" the state machine in - // a strongly-typed manner into an AsyncStateMachineBox. It will already contain - // the state machine as well as a MoveNextDelegate and a context. The only thing - // we might need to do is update the context if that's changed since it was stored. - if (boxFieldRef is StateMachineBox stronglyTypedBox) - { - if (stronglyTypedBox.Context != currentContext) - { - stronglyTypedBox.Context = currentContext; - } - - return stronglyTypedBox; - } - - // The least common case: we have a weakly-typed boxed. This results if the debugger - // or some other use of reflection accesses a property like ObjectIdForDebugger. In - // such situations, we need to get an object to represent the builder, but we don't yet - // know the type of the state machine, and thus can't use TStateMachine. Instead, we - // use the IAsyncStateMachine interface, which all TStateMachines implement. This will - // result in a boxing allocation when storing the TStateMachine if it's a struct, but - // this only happens in active debugging scenarios where such performance impact doesn't - // matter. - if (boxFieldRef is StateMachineBox weaklyTypedBox) - { - // If this is the first await, we won't yet have a state machine, so store it. - if (weaklyTypedBox.StateMachine is null) - { - Debugger.NotifyOfCrossThreadDependency(); // same explanation as with usage below - weaklyTypedBox.StateMachine = stateMachine; - } - - // Update the context. This only happens with a debugger, so no need to spend - // extra IL checking for equality before doing the assignment. - weaklyTypedBox.Context = currentContext; - return weaklyTypedBox; - } - - // Alert a listening debugger that we can't make forward progress unless it slips threads. - // If we don't do this, and a method that uses "await foo;" is invoked through funceval, - // we could end up hooking up a callback to push forward the async method's state machine, - // the debugger would then abort the funceval after it takes too long, and then continuing - // execution could result in another callback being hooked up. At that point we have - // multiple callbacks registered to push the state machine, which could result in bad behavior. - Debugger.NotifyOfCrossThreadDependency(); - - // At this point, m_task should really be null, in which case we want to create the box. - // However, in a variety of debugger-related (erroneous) situations, it might be non-null, - // e.g. if the Task property is examined in a Watch window, forcing it to be lazily-intialized - // as a Task rather than as an ValueTaskStateMachineBox. The worst that happens in such - // cases is we lose the ability to properly step in the debugger, as the debugger uses that - // object's identity to track this specific builder/state machine. As such, we proceed to - // overwrite whatever's there anyway, even if it's non-null. - var box = StateMachineBox.GetOrCreateBox(); - boxFieldRef = box; // important: this must be done before storing stateMachine into box.StateMachine! - box.StateMachine = stateMachine; - box.Context = currentContext; - - return box; - } - - /// - /// Creates a box object for use when a non-standard access pattern is employed, e.g. when Task - /// is evaluated in the debugger prior to the async method yielding for the first time. - /// - internal static StateMachineBox CreateWeaklyTypedStateMachineBox() => new StateMachineBox(); + where TStateMachine : IAsyncStateMachine => + AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine, ref m_task); /// /// Gets an object that may be used to uniquely identify this builder to the debugger. @@ -280,241 +112,6 @@ private static IAsyncStateMachineBox GetStateMachineBox( /// It must only be used by the debugger and tracing purposes, and only in a single-threaded manner /// when no other threads are in the middle of accessing this or other members that lazily initialize the box. /// - internal object ObjectIdForDebugger - { - get - { - if (m_task is null) - { - m_task = AsyncValueTaskMethodBuilder.s_valueTaskPoolingEnabled ? (object) - CreateWeaklyTypedStateMachineBox() : - AsyncTaskMethodBuilder.CreateWeaklyTypedStateMachineBox(); - } - - return m_task; - } - } - - /// The base type for all value task box reusable box objects, regardless of state machine type. - internal abstract class StateMachineBox : - IValueTaskSource, IValueTaskSource - { - /// A delegate to the MoveNext method. - protected Action? _moveNextAction; - /// Captured ExecutionContext with which to invoke MoveNext. - public ExecutionContext? Context; - /// Implementation for IValueTaskSource interfaces. - protected ManualResetValueTaskSourceCore _valueTaskSource; - - /// Completes the box with a result. - /// The result. - public void SetResult(TResult result) => - _valueTaskSource.SetResult(result); - - /// Completes the box with an error. - /// The exception. - public void SetException(Exception error) => - _valueTaskSource.SetException(error); - - /// Gets the status of the box. - public ValueTaskSourceStatus GetStatus(short token) => _valueTaskSource.GetStatus(token); - - /// Schedules the continuation action for this box. - public void OnCompleted(Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) => - _valueTaskSource.OnCompleted(continuation, state, token, flags); - - /// Gets the current version number of the box. - public short Version => _valueTaskSource.Version; - - /// Implemented by derived type. - TResult IValueTaskSource.GetResult(short token) => throw NotImplemented.ByDesign; - - /// Implemented by derived type. - void IValueTaskSource.GetResult(short token) => throw NotImplemented.ByDesign; - } - - private sealed class SyncSuccessSentinelStateMachineBox : StateMachineBox - { - public SyncSuccessSentinelStateMachineBox() => SetResult(default!); - } - - /// Provides a strongly-typed box object based on the specific state machine type in use. - private sealed class StateMachineBox : - StateMachineBox, - IValueTaskSource, IValueTaskSource, IAsyncStateMachineBox, IThreadPoolWorkItem - where TStateMachine : IAsyncStateMachine - { - /// Delegate used to invoke on an ExecutionContext when passed an instance of this box type. - private static readonly ContextCallback s_callback = ExecutionContextCallback; - /// Lock used to protected the shared cache of boxes. - /// The code that uses this assumes a runtime without thread aborts. - private static int s_cacheLock; - /// Singly-linked list cache of boxes. - private static StateMachineBox? s_cache; - /// The number of items stored in . - private static int s_cacheSize; - - // TODO: - // AsyncTaskMethodBuilder logs about the state machine box lifecycle; AsyncValueTaskMethodBuilder currently - // does not when it employs these pooled boxes. That logging is based on Task IDs, which we lack here. - // We could use the box's Version, but that is very likely to conflict with the IDs of other tasks in the system. - // For now, we don't log, but should we choose to we'll probably want to store an int ID on the state machine box, - // and initialize it an ID from Task's generator. - - /// If this box is stored in the cache, the next box in the cache. - private StateMachineBox? _next; - /// The state machine itself. - public TStateMachine? StateMachine; - - /// Gets a box object to use for an operation. This may be a reused, pooled object, or it may be new. - [MethodImpl(MethodImplOptions.AggressiveInlining)] // only one caller - internal static StateMachineBox GetOrCreateBox() - { - // Try to acquire the lock to access the cache. If there's any contention, don't use the cache. - if (Interlocked.CompareExchange(ref s_cacheLock, 1, 0) == 0) - { - // If there are any instances cached, take one from the cache stack and use it. - StateMachineBox? box = s_cache; - if (!(box is null)) - { - s_cache = box._next; - box._next = null; - s_cacheSize--; - Debug.Assert(s_cacheSize >= 0, "Expected the cache size to be non-negative."); - - // Release the lock and return the box. - Volatile.Write(ref s_cacheLock, 0); - return box; - } - - // No objects were cached. We'll just create a new instance. - Debug.Assert(s_cacheSize == 0, "Expected cache size to be 0."); - - // Release the lock. - Volatile.Write(ref s_cacheLock, 0); - } - - // Couldn't quickly get a cached instance, so create a new instance. - return new StateMachineBox(); - } - - private void ReturnOrDropBox() - { - Debug.Assert(_next is null, "Expected box to not be part of cached list."); - - // Clear out the state machine and associated context to avoid keeping arbitrary state referenced by - // lifted locals. We want to do this regardless of whether we end up caching the box or not, in case - // the caller keeps the box alive for an arbitrary period of time. - ClearStateUponCompletion(); - - // Reset the MRVTSC. We can either do this here, in which case we may be paying the (small) overhead - // to reset the box even if we're going to drop it, or we could do it while holding the lock, in which - // case we'll only reset it if necessary but causing the lock to be held for longer, thereby causing - // more contention. For now at least, we do it outside of the lock. (This must not be done after - // the lock is released, since at that point the instance could already be in use elsewhere.) - // We also want to increment the version number even if we're going to drop it, to maximize the chances - // that incorrectly double-awaiting a ValueTask will produce an error. - _valueTaskSource.Reset(); - - // If reusing the object would result in potentially wrapping around its version number, just throw it away. - // This provides a modicum of additional safety when ValueTasks are misused (helping to avoid the case where - // a ValueTask is illegally re-awaited and happens to do so at exactly 2^16 uses later on this exact same instance), - // at the expense of potentially incurring an additional allocation every 65K uses. - if ((ushort)_valueTaskSource.Version == ushort.MaxValue) - { - return; - } - - // Try to acquire the cache lock. If there's any contention, or if the cache is full, we just throw away the object. - if (Interlocked.CompareExchange(ref s_cacheLock, 1, 0) == 0) - { - if (s_cacheSize < AsyncValueTaskMethodBuilder.s_valueTaskPoolingCacheSize) - { - // Push the box onto the cache stack for subsequent reuse. - _next = s_cache; - s_cache = this; - s_cacheSize++; - Debug.Assert(s_cacheSize > 0 && s_cacheSize <= AsyncValueTaskMethodBuilder.s_valueTaskPoolingCacheSize, "Expected cache size to be within bounds."); - } - - // Release the lock. - Volatile.Write(ref s_cacheLock, 0); - } - } - - /// - /// Clear out the state machine and associated context to avoid keeping arbitrary state referenced by lifted locals. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ClearStateUponCompletion() - { - StateMachine = default; - Context = default; - } - - /// - /// Used to initialize s_callback above. We don't use a lambda for this on purpose: a lambda would - /// introduce a new generic type behind the scenes that comes with a hefty size penalty in AOT builds. - /// - private static void ExecutionContextCallback(object? s) - { - // Only used privately to pass directly to EC.Run - Debug.Assert(s is StateMachineBox); - Unsafe.As>(s).StateMachine!.MoveNext(); - } - - /// A delegate to the method. - public Action MoveNextAction => _moveNextAction ??= new Action(MoveNext); - - /// Invoked to run MoveNext when this instance is executed from the thread pool. - void IThreadPoolWorkItem.Execute() => MoveNext(); - - /// Calls MoveNext on - public void MoveNext() - { - ExecutionContext? context = Context; - - if (context is null) - { - Debug.Assert(!(StateMachine is null)); - StateMachine.MoveNext(); - } - else - { - ExecutionContext.RunInternal(context, s_callback, this); - } - } - - /// Get the result of the operation. - TResult IValueTaskSource.GetResult(short token) - { - try - { - return _valueTaskSource.GetResult(token); - } - finally - { - // Reuse this instance if possible, otherwise clear and drop it. - ReturnOrDropBox(); - } - } - - /// Get the result of the operation. - void IValueTaskSource.GetResult(short token) - { - try - { - _valueTaskSource.GetResult(token); - } - finally - { - // Reuse this instance if possible, otherwise clear and drop it. - ReturnOrDropBox(); - } - } - - /// Gets the state machine as a boxed object. This should only be used for debugging purposes. - IAsyncStateMachine IAsyncStateMachineBox.GetStateMachineObject() => StateMachine!; // likely boxes, only use for debugging - } + internal object ObjectIdForDebugger => m_task ??= AsyncTaskMethodBuilder.CreateWeaklyTypedStateMachineBox(); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/PoolingAsyncValueTaskMethodBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/PoolingAsyncValueTaskMethodBuilder.cs new file mode 100644 index 000000000000..84996a0e062f --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/PoolingAsyncValueTaskMethodBuilder.cs @@ -0,0 +1,121 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +using StateMachineBox = System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder.StateMachineBox; + +namespace System.Runtime.CompilerServices +{ + /// Represents a builder for asynchronous methods that return a . + [StructLayout(LayoutKind.Auto)] + public struct PoolingAsyncValueTaskMethodBuilder + { + /// Maximum number of boxes that are allowed to be cached per state machine type. + internal static readonly int s_valueTaskPoolingCacheSize = + int.TryParse(Environment.GetEnvironmentVariable("DOTNET_SYSTEM_THREADING_POOLINGASYNCVALUETASKSCACHESIZE"), NumberStyles.Integer, CultureInfo.InvariantCulture, out int result) && result > 0 ? + result : + Environment.ProcessorCount * 4; // arbitrary default value + + /// Sentinel object used to indicate that the builder completed synchronously and successfully. + private static readonly StateMachineBox s_syncSuccessSentinel = PoolingAsyncValueTaskMethodBuilder.s_syncSuccessSentinel; + + /// The wrapped state machine box. + /// + /// If the operation completed synchronously and successfully, this will be . + /// + private StateMachineBox? m_task; // Debugger depends on the exact name of this field. + + /// Creates an instance of the struct. + /// The initialized instance. + public static PoolingAsyncValueTaskMethodBuilder Create() => default; + + /// Begins running the builder with the associated state machine. + /// The type of the state machine. + /// The state machine instance, passed by reference. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Start(ref TStateMachine stateMachine) + where TStateMachine : IAsyncStateMachine => + AsyncMethodBuilderCore.Start(ref stateMachine); + + /// Associates the builder with the specified state machine. + /// The state machine instance to associate with the builder. + public void SetStateMachine(IAsyncStateMachine stateMachine) => + AsyncMethodBuilderCore.SetStateMachine(stateMachine, task: null); + + /// Marks the task as successfully completed. + public void SetResult() + { + if (m_task is null) + { + m_task = s_syncSuccessSentinel; + } + else + { + m_task.SetResult(default); + } + } + + /// Marks the task as failed and binds the specified exception to the task. + /// The exception to bind to the task. + public void SetException(Exception exception) => + PoolingAsyncValueTaskMethodBuilder.SetException(exception, ref m_task); + + /// Gets the task for this builder. + public ValueTask Task + { + get + { + if (m_task == s_syncSuccessSentinel) + { + return default; + } + + // With normal access paterns, m_task should always be non-null here: the async method should have + // either completed synchronously, in which case SetResult would have set m_task to a non-null object, + // or it should be completing asynchronously, in which case AwaitUnsafeOnCompleted would have similarly + // initialized m_task to a state machine object. However, if the type is used manually (not via + // compiler-generated code) and accesses Task directly, we force it to be initialized. Things will then + // "work" but in a degraded mode, as we don't know the TStateMachine type here, and thus we use a box around + // the interface instead. + + StateMachineBox? box = m_task ??= PoolingAsyncValueTaskMethodBuilder.CreateWeaklyTypedStateMachineBox(); + return new ValueTask(box, box.Version); + } + } + + /// Schedules the state machine to proceed to the next action when the specified awaiter completes. + /// The type of the awaiter. + /// The type of the state machine. + /// The awaiter. + /// The state machine. + public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : INotifyCompletion + where TStateMachine : IAsyncStateMachine => + PoolingAsyncValueTaskMethodBuilder.AwaitOnCompleted(ref awaiter, ref stateMachine, ref m_task); + + /// Schedules the state machine to proceed to the next action when the specified awaiter completes. + /// The type of the awaiter. + /// The type of the state machine. + /// The awaiter. + /// The state machine. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : ICriticalNotifyCompletion + where TStateMachine : IAsyncStateMachine => + PoolingAsyncValueTaskMethodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine, ref m_task); + + /// + /// Gets an object that may be used to uniquely identify this builder to the debugger. + /// + /// + /// This property lazily instantiates the ID in a non-thread-safe manner. + /// It must only be used by the debugger and tracing purposes, and only in a single-threaded manner + /// when no other threads are in the middle of accessing this or other members that lazily initialize the box. + /// + internal object ObjectIdForDebugger => + m_task ??= PoolingAsyncValueTaskMethodBuilder.CreateWeaklyTypedStateMachineBox(); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/PoolingAsyncValueTaskMethodBuilderT.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/PoolingAsyncValueTaskMethodBuilderT.cs new file mode 100644 index 000000000000..02d15bb92c3b --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/PoolingAsyncValueTaskMethodBuilderT.cs @@ -0,0 +1,473 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using System.Threading.Tasks.Sources; +using Internal.Runtime.CompilerServices; + +namespace System.Runtime.CompilerServices +{ + /// Represents a builder for asynchronous methods that returns a . + /// The type of the result. + [StructLayout(LayoutKind.Auto)] + public struct PoolingAsyncValueTaskMethodBuilder + { + /// Sentinel object used to indicate that the builder completed synchronously and successfully. + /// + /// To avoid memory safety issues even in the face of invalid race conditions, we ensure that the type of this object + /// is valid for the mode in which we're operating. As such, it's cached on the generic builder per TResult + /// rather than having one sentinel instance for all types. + /// + internal static readonly StateMachineBox s_syncSuccessSentinel = new SyncSuccessSentinelStateMachineBox(); + + /// The wrapped state machine or task. If the operation completed synchronously and successfully, this will be a sentinel object compared by reference identity. + private StateMachineBox? m_task; // Debugger depends on the exact name of this field. + /// The result for this builder if it's completed synchronously, in which case will be . + private TResult _result; + + /// Creates an instance of the struct. + /// The initialized instance. + public static PoolingAsyncValueTaskMethodBuilder Create() => default; + + /// Begins running the builder with the associated state machine. + /// The type of the state machine. + /// The state machine instance, passed by reference. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine => + AsyncMethodBuilderCore.Start(ref stateMachine); + + /// Associates the builder with the specified state machine. + /// The state machine instance to associate with the builder. + public void SetStateMachine(IAsyncStateMachine stateMachine) => + AsyncMethodBuilderCore.SetStateMachine(stateMachine, task: null); + + /// Marks the value task as successfully completed. + /// The result to use to complete the value task. + public void SetResult(TResult result) + { + if (m_task is null) + { + _result = result; + m_task = s_syncSuccessSentinel; + } + else + { + m_task.SetResult(result); + } + } + + /// Marks the value task as failed and binds the specified exception to the value task. + /// The exception to bind to the value task. + public void SetException(Exception exception) => + SetException(exception, ref m_task); + + internal static void SetException(Exception exception, [NotNull] ref StateMachineBox? boxFieldRef) + { + if (exception is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.exception); + } + + (boxFieldRef ??= CreateWeaklyTypedStateMachineBox()).SetException(exception); + } + + /// Gets the value task for this builder. + public ValueTask Task + { + get + { + if (m_task == s_syncSuccessSentinel) + { + return new ValueTask(_result); + } + + // With normal access paterns, m_task should always be non-null here: the async method should have + // either completed synchronously, in which case SetResult would have set m_task to a non-null object, + // or it should be completing asynchronously, in which case AwaitUnsafeOnCompleted would have similarly + // initialized m_task to a state machine object. However, if the type is used manually (not via + // compiler-generated code) and accesses Task directly, we force it to be initialized. Things will then + // "work" but in a degraded mode, as we don't know the TStateMachine type here, and thus we use a box around + // the interface instead. + + PoolingAsyncValueTaskMethodBuilder.StateMachineBox? box = m_task ??= CreateWeaklyTypedStateMachineBox(); + return new ValueTask(box, box.Version); + } + } + + /// Schedules the state machine to proceed to the next action when the specified awaiter completes. + /// The type of the awaiter. + /// The type of the state machine. + /// the awaiter + /// The state machine. + public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : INotifyCompletion + where TStateMachine : IAsyncStateMachine => + AwaitOnCompleted(ref awaiter, ref stateMachine, ref m_task); + + internal static void AwaitOnCompleted( + ref TAwaiter awaiter, ref TStateMachine stateMachine, ref StateMachineBox? box) + where TAwaiter : INotifyCompletion + where TStateMachine : IAsyncStateMachine + { + try + { + awaiter.OnCompleted(GetStateMachineBox(ref stateMachine, ref box).MoveNextAction); + } + catch (Exception e) + { + System.Threading.Tasks.Task.ThrowAsync(e, targetContext: null); + } + } + + /// Schedules the state machine to proceed to the next action when the specified awaiter completes. + /// The type of the awaiter. + /// The type of the state machine. + /// the awaiter + /// The state machine. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : ICriticalNotifyCompletion + where TStateMachine : IAsyncStateMachine => + AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine, ref m_task); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void AwaitUnsafeOnCompleted( + ref TAwaiter awaiter, ref TStateMachine stateMachine, [NotNull] ref StateMachineBox? boxRef) + where TAwaiter : ICriticalNotifyCompletion + where TStateMachine : IAsyncStateMachine + { + IAsyncStateMachineBox box = GetStateMachineBox(ref stateMachine, ref boxRef); + AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted(ref awaiter, box); + } + + /// Gets the "boxed" state machine object. + /// Specifies the type of the async state machine. + /// The state machine. + /// A reference to the field containing the initialized state machine box. + /// The "boxed" state machine. + private static IAsyncStateMachineBox GetStateMachineBox( + ref TStateMachine stateMachine, + [NotNull] ref StateMachineBox? boxFieldRef) + where TStateMachine : IAsyncStateMachine + { + ExecutionContext? currentContext = ExecutionContext.Capture(); + + // Check first for the most common case: not the first yield in an async method. + // In this case, the first yield will have already "boxed" the state machine in + // a strongly-typed manner into an AsyncStateMachineBox. It will already contain + // the state machine as well as a MoveNextDelegate and a context. The only thing + // we might need to do is update the context if that's changed since it was stored. + if (boxFieldRef is StateMachineBox stronglyTypedBox) + { + if (stronglyTypedBox.Context != currentContext) + { + stronglyTypedBox.Context = currentContext; + } + + return stronglyTypedBox; + } + + // The least common case: we have a weakly-typed boxed. This results if the debugger + // or some other use of reflection accesses a property like ObjectIdForDebugger. In + // such situations, we need to get an object to represent the builder, but we don't yet + // know the type of the state machine, and thus can't use TStateMachine. Instead, we + // use the IAsyncStateMachine interface, which all TStateMachines implement. This will + // result in a boxing allocation when storing the TStateMachine if it's a struct, but + // this only happens in active debugging scenarios where such performance impact doesn't + // matter. + if (boxFieldRef is StateMachineBox weaklyTypedBox) + { + // If this is the first await, we won't yet have a state machine, so store it. + if (weaklyTypedBox.StateMachine is null) + { + Debugger.NotifyOfCrossThreadDependency(); // same explanation as with usage below + weaklyTypedBox.StateMachine = stateMachine; + } + + // Update the context. This only happens with a debugger, so no need to spend + // extra IL checking for equality before doing the assignment. + weaklyTypedBox.Context = currentContext; + return weaklyTypedBox; + } + + // Alert a listening debugger that we can't make forward progress unless it slips threads. + // If we don't do this, and a method that uses "await foo;" is invoked through funceval, + // we could end up hooking up a callback to push forward the async method's state machine, + // the debugger would then abort the funceval after it takes too long, and then continuing + // execution could result in another callback being hooked up. At that point we have + // multiple callbacks registered to push the state machine, which could result in bad behavior. + Debugger.NotifyOfCrossThreadDependency(); + + // At this point, m_task should really be null, in which case we want to create the box. + // However, in a variety of debugger-related (erroneous) situations, it might be non-null, + // e.g. if the Task property is examined in a Watch window, forcing it to be lazily-intialized + // as a Task rather than as an ValueTaskStateMachineBox. The worst that happens in such + // cases is we lose the ability to properly step in the debugger, as the debugger uses that + // object's identity to track this specific builder/state machine. As such, we proceed to + // overwrite whatever's there anyway, even if it's non-null. + StateMachineBox box = StateMachineBox.GetOrCreateBox(); + boxFieldRef = box; // important: this must be done before storing stateMachine into box.StateMachine! + box.StateMachine = stateMachine; + box.Context = currentContext; + + return box; + } + + /// + /// Creates a box object for use when a non-standard access pattern is employed, e.g. when Task + /// is evaluated in the debugger prior to the async method yielding for the first time. + /// + internal static StateMachineBox CreateWeaklyTypedStateMachineBox() => new StateMachineBox(); + + /// + /// Gets an object that may be used to uniquely identify this builder to the debugger. + /// + /// + /// This property lazily instantiates the ID in a non-thread-safe manner. + /// It must only be used by the debugger and tracing purposes, and only in a single-threaded manner + /// when no other threads are in the middle of accessing this or other members that lazily initialize the box. + /// + internal object ObjectIdForDebugger => m_task ??= CreateWeaklyTypedStateMachineBox(); + + /// The base type for all value task box reusable box objects, regardless of state machine type. + internal abstract class StateMachineBox : IValueTaskSource, IValueTaskSource + { + /// A delegate to the MoveNext method. + protected Action? _moveNextAction; + /// Captured ExecutionContext with which to invoke MoveNext. + public ExecutionContext? Context; + /// Implementation for IValueTaskSource interfaces. + protected ManualResetValueTaskSourceCore _valueTaskSource; + + /// Completes the box with a result. + /// The result. + public void SetResult(TResult result) => + _valueTaskSource.SetResult(result); + + /// Completes the box with an error. + /// The exception. + public void SetException(Exception error) => + _valueTaskSource.SetException(error); + + /// Gets the status of the box. + public ValueTaskSourceStatus GetStatus(short token) => _valueTaskSource.GetStatus(token); + + /// Schedules the continuation action for this box. + public void OnCompleted(Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) => + _valueTaskSource.OnCompleted(continuation, state, token, flags); + + /// Gets the current version number of the box. + public short Version => _valueTaskSource.Version; + + /// Implemented by derived type. + TResult IValueTaskSource.GetResult(short token) => throw NotImplemented.ByDesign; + + /// Implemented by derived type. + void IValueTaskSource.GetResult(short token) => throw NotImplemented.ByDesign; + } + + /// Type used as a singleton to indicate synchronous success for an async method. + private sealed class SyncSuccessSentinelStateMachineBox : StateMachineBox + { + public SyncSuccessSentinelStateMachineBox() => SetResult(default!); + } + + /// Provides a strongly-typed box object based on the specific state machine type in use. + private sealed class StateMachineBox : + StateMachineBox, + IValueTaskSource, IValueTaskSource, IAsyncStateMachineBox, IThreadPoolWorkItem + where TStateMachine : IAsyncStateMachine + { + /// Delegate used to invoke on an ExecutionContext when passed an instance of this box type. + private static readonly ContextCallback s_callback = ExecutionContextCallback; + /// Thread-local cache of boxes. This currently only ever stores one. + [ThreadStatic] + private static StateMachineBox? t_tlsCache; + /// Lock used to protected the shared cache of boxes. 1 == held, 0 == not held. + /// The code that uses this assumes a runtime without thread aborts. + private static int s_cacheLock; + /// Singly-linked list cache of boxes. + private static StateMachineBox? s_cache; + /// The number of items stored in . + private static int s_cacheSize; + + /// If this box is stored in the cache, the next box in the cache. + private StateMachineBox? _next; + /// The state machine itself. + public TStateMachine? StateMachine; + + /// Gets a box object to use for an operation. This may be a reused, pooled object, or it may be new. + [MethodImpl(MethodImplOptions.AggressiveInlining)] // only one caller + internal static StateMachineBox GetOrCreateBox() + { + StateMachineBox? box; + + // First see if the thread-static cache of at most one box has one. + box = t_tlsCache; + if (box is not null) + { + t_tlsCache = null; + return box; + } + + // Try to acquire the lock to access the cache. If there's any contention, don't use the cache. + if (s_cache is not null && // hot read just to see if there's any point paying for the interlocked + Interlocked.Exchange(ref s_cacheLock, 1) == 0) + { + // If there are any instances cached, take one from the cache stack and use it. + box = s_cache; + if (box is not null) + { + s_cache = box._next; + box._next = null; + s_cacheSize--; + Debug.Assert(s_cacheSize >= 0, "Expected the cache size to be non-negative."); + + // Release the lock and return the box. + Volatile.Write(ref s_cacheLock, 0); + return box; + } + + // No objects were cached. We'll just create a new instance. + Debug.Assert(s_cacheSize == 0, "Expected cache size to be 0."); + + // Release the lock. + Volatile.Write(ref s_cacheLock, 0); + } + + // Couldn't quickly get a cached instance, so create a new instance. + return new StateMachineBox(); + } + + /// Returns this instance to the cache, or drops it if the cache is full or this instance shouldn't be cached. + private void ReturnOrDropBox() + { + Debug.Assert(_next is null, "Expected box to not be part of cached list."); + + // Clear out the state machine and associated context to avoid keeping arbitrary state referenced by + // lifted locals. We want to do this regardless of whether we end up caching the box or not, in case + // the caller keeps the box alive for an arbitrary period of time. + ClearStateUponCompletion(); + + // Reset the MRVTSC. We can either do this here, in which case we may be paying the (small) overhead + // to reset the box even if we're going to drop it, or we could do it while holding the lock, in which + // case we'll only reset it if necessary but causing the lock to be held for longer, thereby causing + // more contention. For now at least, we do it outside of the lock. (This must not be done after + // the lock is released, since at that point the instance could already be in use elsewhere.) + // We also want to increment the version number even if we're going to drop it, to maximize the chances + // that incorrectly double-awaiting a ValueTask will produce an error. + _valueTaskSource.Reset(); + + // If reusing the object would result in potentially wrapping around its version number, just throw it away. + // This provides a modicum of additional safety when ValueTasks are misused (helping to avoid the case where + // a ValueTask is illegally re-awaited and happens to do so at exactly 2^16 uses later on this exact same instance), + // at the expense of potentially incurring an additional allocation every 65K uses. + if ((ushort)_valueTaskSource.Version == ushort.MaxValue) + { + return; + } + + // If the thread static cache is empty, store this into it and bail. + if (t_tlsCache is null) + { + t_tlsCache = this; + return; + } + + // Try to acquire the cache lock. If there's any contention, or if the cache is full, we just throw away the object. + if (Interlocked.Exchange(ref s_cacheLock, 1) == 0) + { + if (s_cacheSize < PoolingAsyncValueTaskMethodBuilder.s_valueTaskPoolingCacheSize) + { + // Push the box onto the cache stack for subsequent reuse. + _next = s_cache; + s_cache = this; + s_cacheSize++; + Debug.Assert(s_cacheSize > 0 && s_cacheSize <= PoolingAsyncValueTaskMethodBuilder.s_valueTaskPoolingCacheSize, "Expected cache size to be within bounds."); + } + + // Release the lock. + Volatile.Write(ref s_cacheLock, 0); + } + } + + /// + /// Clear out the state machine and associated context to avoid keeping arbitrary state referenced by lifted locals. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ClearStateUponCompletion() + { + StateMachine = default; + Context = default; + } + + /// + /// Used to initialize s_callback above. We don't use a lambda for this on purpose: a lambda would + /// introduce a new generic type behind the scenes that comes with a hefty size penalty in AOT builds. + /// + private static void ExecutionContextCallback(object? s) + { + // Only used privately to pass directly to EC.Run + Debug.Assert(s is StateMachineBox); + Unsafe.As>(s).StateMachine!.MoveNext(); + } + + /// A delegate to the method. + public Action MoveNextAction => _moveNextAction ??= new Action(MoveNext); + + /// Invoked to run MoveNext when this instance is executed from the thread pool. + void IThreadPoolWorkItem.Execute() => MoveNext(); + + /// Calls MoveNext on + public void MoveNext() + { + ExecutionContext? context = Context; + + if (context is null) + { + Debug.Assert(!(StateMachine is null)); + StateMachine.MoveNext(); + } + else + { + ExecutionContext.RunInternal(context, s_callback, this); + } + } + + /// Get the result of the operation. + TResult IValueTaskSource.GetResult(short token) + { + try + { + return _valueTaskSource.GetResult(token); + } + finally + { + // Reuse this instance if possible, otherwise clear and drop it. + ReturnOrDropBox(); + } + } + + /// Get the result of the operation. + void IValueTaskSource.GetResult(short token) + { + try + { + _valueTaskSource.GetResult(token); + } + finally + { + // Reuse this instance if possible, otherwise clear and drop it. + ReturnOrDropBox(); + } + } + + /// Gets the state machine as a boxed object. This should only be used for debugging purposes. + IAsyncStateMachine IAsyncStateMachineBox.GetStateMachineObject() => StateMachine!; // likely boxes, only use for debugging + } + } +} diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 5e3c282aff8a..303016c56243 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -9114,7 +9114,7 @@ public sealed partial class AsyncIteratorStateMachineAttribute : System.Runtime. { public AsyncIteratorStateMachineAttribute(System.Type stateMachineType) : base (default(System.Type)) { } } - [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Delegate | System.AttributeTargets.Enum | System.AttributeTargets.Interface | System.AttributeTargets.Struct, Inherited=false, AllowMultiple=false)] + [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Delegate | System.AttributeTargets.Enum | System.AttributeTargets.Interface | System.AttributeTargets.Method | System.AttributeTargets.Struct, Inherited=false, AllowMultiple=false)] public sealed partial class AsyncMethodBuilderAttribute : System.Attribute { public AsyncMethodBuilderAttribute(System.Type builderType) { } @@ -9524,6 +9524,33 @@ public sealed partial class ModuleInitializerAttribute : System.Attribute { public ModuleInitializerAttribute() { } } + public partial struct PoolingAsyncValueTaskMethodBuilder + { + private object _dummy; + private int _dummyPrimitive; + public System.Threading.Tasks.ValueTask Task { get { throw null; } } + public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : System.Runtime.CompilerServices.INotifyCompletion where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine { } + public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine { } + public static System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder Create() { throw null; } + public void SetException(System.Exception exception) { } + public void SetResult() { } + public void SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine stateMachine) { } + public void Start(ref TStateMachine stateMachine) where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine { } + } + public partial struct PoolingAsyncValueTaskMethodBuilder + { + private TResult _result; + private object _dummy; + private int _dummyPrimitive; + public System.Threading.Tasks.ValueTask Task { get { throw null; } } + public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : System.Runtime.CompilerServices.INotifyCompletion where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine { } + public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine { } + public static System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder Create() { throw null; } + public void SetException(System.Exception exception) { } + public void SetResult(TResult result) { } + public void SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine stateMachine) { } + public void Start(ref TStateMachine stateMachine) where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine { } + } [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false, Inherited=false)] public sealed partial class PreserveBaseOverridesAttribute : System.Attribute { diff --git a/src/libraries/System.Threading.Tasks.Extensions/tests/AsyncValueTaskMethodBuilderTests.cs b/src/libraries/System.Threading.Tasks.Extensions/tests/AsyncValueTaskMethodBuilderTests.cs index d929d30d22d6..e515a1ff0a7e 100644 --- a/src/libraries/System.Threading.Tasks.Extensions/tests/AsyncValueTaskMethodBuilderTests.cs +++ b/src/libraries/System.Threading.Tasks.Extensions/tests/AsyncValueTaskMethodBuilderTests.cs @@ -555,69 +555,6 @@ static async ValueTask ValueTaskAsync(int i) })); } - [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - [InlineData("1", null)] - [InlineData("true", null)] - [InlineData("true", "1")] - [InlineData("true", "100")] - [InlineData("false", null)] - [InlineData("false", "100")] - public void PoolingAsyncValueTasksBuilder_ObjectsPooled(string poolingEnvVar, string limitEnvVar) - { - // Use RemoteExecutor to launch a process with the right environment variables set - var psi = new ProcessStartInfo(); - psi.Environment.Add("DOTNET_SYSTEM_THREADING_POOLASYNCVALUETASKS", poolingEnvVar); - if (limitEnvVar != null) - { - psi.Environment.Add("DOTNET_SYSTEM_THREADING_POOLASYNCVALUETASKSLIMIT", limitEnvVar); - } - - RemoteExecutor.Invoke(async expectReuse => - { - var boxes = new ConcurrentQueue(); - var valueTasks = new ValueTask[10]; - int total = 0; - - // Invoke a bunch of ValueTask methods, some in parallel, - // and track a) their results and b) what boxing object is used. - for (int rep = 0; rep < 3; rep++) - { - for (int i = 0; i < valueTasks.Length; i++) - { - valueTasks[i] = ComputeAsync(i + 1, boxes); - } - foreach (ValueTask vt in valueTasks) - { - total += await vt; - } - } - - // Make sure we got the right total, and that if we expected pooling, - // we at least pooled one object. - Assert.Equal(330, total); - if (expectReuse == "1" || expectReuse == "true") - { - Assert.InRange(boxes.Distinct().Count(), 1, boxes.Count - 1); - } - }, (poolingEnvVar == "1" || poolingEnvVar == "true").ToString(), new RemoteInvokeOptions() { StartInfo = psi }).Dispose(); - - static async ValueTask ComputeAsync(int input, ConcurrentQueue boxes) - { - await RecursiveValueTaskAsync(3, boxes); - return input * 2; - } - - static async ValueTask RecursiveValueTaskAsync(int depth, ConcurrentQueue boxes) - { - boxes.Enqueue(await GetStateMachineData.FetchAsync()); - if (depth > 0) - { - await Task.Delay(1); - await RecursiveValueTaskAsync(depth - 1, boxes); - } - } - } - private struct DelegateStateMachine : IAsyncStateMachine { internal Action MoveNextDelegate; diff --git a/src/libraries/System.Threading.Tasks.Extensions/tests/PoolingAsyncValueTaskMethodBuilderTests.cs b/src/libraries/System.Threading.Tasks.Extensions/tests/PoolingAsyncValueTaskMethodBuilderTests.cs new file mode 100644 index 000000000000..c2e83881ce5b --- /dev/null +++ b/src/libraries/System.Threading.Tasks.Extensions/tests/PoolingAsyncValueTaskMethodBuilderTests.cs @@ -0,0 +1,579 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading.Tasks.Sources.Tests; +using Microsoft.DotNet.RemoteExecutor; +using Xunit; + +namespace System.Threading.Tasks.Tests +{ + public class PoolingAsyncValueTaskMethodBuilderTests + { + [Fact] + public void Create_ReturnsDefaultInstance() // implementation detail being verified + { + Assert.Equal(default, PoolingAsyncValueTaskMethodBuilder.Create()); + Assert.Equal(default, PoolingAsyncValueTaskMethodBuilder.Create()); + } + + [Fact] + public void NonGeneric_SetResult_BeforeAccessTask_ValueTaskIsDefault() + { + PoolingAsyncValueTaskMethodBuilder b = PoolingAsyncValueTaskMethodBuilder.Create(); + + b.SetResult(); + + Assert.Equal(default, b.Task); + } + + [Fact] + public void Generic_SetResult_BeforeAccessTask_ValueTaskContainsValue() + { + PoolingAsyncValueTaskMethodBuilder b = PoolingAsyncValueTaskMethodBuilder.Create(); + + b.SetResult(42); + + ValueTask vt = b.Task; + Assert.Equal(vt, b.Task); + Assert.Equal(new ValueTask(42), vt); + } + + [Fact] + public void NonGeneric_SetResult_AfterAccessTask_ValueTaskContainsValue() + { + PoolingAsyncValueTaskMethodBuilder b = PoolingAsyncValueTaskMethodBuilder.Create(); + + ValueTask vt = b.Task; + Assert.NotEqual(default, vt); + Assert.Equal(vt, b.Task); + + b.SetResult(); + + Assert.Equal(vt, b.Task); + Assert.True(vt.IsCompletedSuccessfully); + } + + [Fact] + public void Generic_SetResult_AfterAccessTask_ValueTaskContainsValue() + { + PoolingAsyncValueTaskMethodBuilder b = PoolingAsyncValueTaskMethodBuilder.Create(); + + ValueTask vt = b.Task; + Assert.NotEqual(default, vt); + Assert.Equal(vt, b.Task); + + b.SetResult(42); + + Assert.Equal(vt, b.Task); + Assert.True(vt.IsCompletedSuccessfully); + Assert.Equal(42, vt.Result); + } + + [Fact] + public void NonGeneric_SetException_BeforeAccessTask_FaultsTask() + { + PoolingAsyncValueTaskMethodBuilder b = PoolingAsyncValueTaskMethodBuilder.Create(); + + var e = new FormatException(); + b.SetException(e); + + ValueTask vt = b.Task; + Assert.Equal(vt, b.Task); + Assert.True(vt.IsFaulted); + Assert.Same(e, Assert.Throws(() => vt.GetAwaiter().GetResult())); + } + + [Fact] + public void Generic_SetException_BeforeAccessTask_FaultsTask() + { + PoolingAsyncValueTaskMethodBuilder b = PoolingAsyncValueTaskMethodBuilder.Create(); + + var e = new FormatException(); + b.SetException(e); + + ValueTask vt = b.Task; + Assert.Equal(vt, b.Task); + Assert.True(vt.IsFaulted); + Assert.Same(e, Assert.Throws(() => vt.GetAwaiter().GetResult())); + } + + [Fact] + public void NonGeneric_SetException_AfterAccessTask_FaultsTask() + { + PoolingAsyncValueTaskMethodBuilder b = PoolingAsyncValueTaskMethodBuilder.Create(); + + ValueTask vt = b.Task; + Assert.Equal(vt, b.Task); + + var e = new FormatException(); + b.SetException(e); + + Assert.Equal(vt, b.Task); + Assert.True(vt.IsFaulted); + Assert.Same(e, Assert.Throws(() => vt.GetAwaiter().GetResult())); + } + + [Fact] + public void Generic_SetException_AfterAccessTask_FaultsTask() + { + PoolingAsyncValueTaskMethodBuilder b = PoolingAsyncValueTaskMethodBuilder.Create(); + + ValueTask vt = b.Task; + Assert.Equal(vt, b.Task); + + var e = new FormatException(); + b.SetException(e); + + Assert.Equal(vt, b.Task); + Assert.True(vt.IsFaulted); + Assert.Same(e, Assert.Throws(() => vt.GetAwaiter().GetResult())); + } + + [Fact] + public void NonGeneric_SetException_OperationCanceledException_CancelsTask() + { + PoolingAsyncValueTaskMethodBuilder b = PoolingAsyncValueTaskMethodBuilder.Create(); + + ValueTask vt = b.Task; + Assert.Equal(vt, b.Task); + + var e = new OperationCanceledException(); + b.SetException(e); + + Assert.Equal(vt, b.Task); + Assert.True(vt.IsCanceled); + Assert.Same(e, Assert.Throws(() => vt.GetAwaiter().GetResult())); + } + + [Fact] + public void Generic_SetException_OperationCanceledException_CancelsTask() + { + PoolingAsyncValueTaskMethodBuilder b = PoolingAsyncValueTaskMethodBuilder.Create(); + + ValueTask vt = b.Task; + Assert.Equal(vt, b.Task); + + var e = new OperationCanceledException(); + b.SetException(e); + + Assert.Equal(vt, b.Task); + Assert.True(vt.IsCanceled); + Assert.Same(e, Assert.Throws(() => vt.GetAwaiter().GetResult())); + } + + [Fact] + public void NonGeneric_SetExceptionWithNullException_Throws() + { + PoolingAsyncValueTaskMethodBuilder b = PoolingAsyncValueTaskMethodBuilder.Create(); + AssertExtensions.Throws("exception", () => b.SetException(null)); + } + + [Fact] + public void Generic_SetExceptionWithNullException_Throws() + { + PoolingAsyncValueTaskMethodBuilder b = PoolingAsyncValueTaskMethodBuilder.Create(); + AssertExtensions.Throws("exception", () => b.SetException(null)); + } + + [Fact] + public void NonGeneric_Start_InvokesMoveNext() + { + PoolingAsyncValueTaskMethodBuilder b = PoolingAsyncValueTaskMethodBuilder.Create(); + + int invokes = 0; + var dsm = new DelegateStateMachine { MoveNextDelegate = () => invokes++ }; + b.Start(ref dsm); + + Assert.Equal(1, invokes); + } + + [Fact] + public void Generic_Start_InvokesMoveNext() + { + PoolingAsyncValueTaskMethodBuilder b = PoolingAsyncValueTaskMethodBuilder.Create(); + + int invokes = 0; + var dsm = new DelegateStateMachine { MoveNextDelegate = () => invokes++ }; + b.Start(ref dsm); + + Assert.Equal(1, invokes); + } + + [Theory] + [InlineData(1, false)] + [InlineData(2, false)] + [InlineData(1, true)] + [InlineData(2, true)] + public void NonGeneric_AwaitOnCompleted_ForcesTaskCreation(int numAwaits, bool awaitUnsafe) + { + PoolingAsyncValueTaskMethodBuilder b = PoolingAsyncValueTaskMethodBuilder.Create(); + + var dsm = new DelegateStateMachine(); + TaskAwaiter t = new TaskCompletionSource().Task.GetAwaiter(); + + Assert.InRange(numAwaits, 1, int.MaxValue); + for (int i = 1; i <= numAwaits; i++) + { + if (awaitUnsafe) + { + b.AwaitUnsafeOnCompleted(ref t, ref dsm); + } + else + { + b.AwaitOnCompleted(ref t, ref dsm); + } + } + + b.SetResult(); + + ValueTask vt = b.Task; + Assert.NotEqual(default, vt); + Assert.True(vt.IsCompletedSuccessfully); + } + + [Theory] + [InlineData(1, false)] + [InlineData(2, false)] + [InlineData(1, true)] + [InlineData(2, true)] + public void Generic_AwaitOnCompleted_ForcesTaskCreation(int numAwaits, bool awaitUnsafe) + { + PoolingAsyncValueTaskMethodBuilder b = PoolingAsyncValueTaskMethodBuilder.Create(); + + var dsm = new DelegateStateMachine(); + TaskAwaiter t = new TaskCompletionSource().Task.GetAwaiter(); + + Assert.InRange(numAwaits, 1, int.MaxValue); + for (int i = 1; i <= numAwaits; i++) + { + if (awaitUnsafe) + { + b.AwaitUnsafeOnCompleted(ref t, ref dsm); + } + else + { + b.AwaitOnCompleted(ref t, ref dsm); + } + } + + b.SetResult(42); + + ValueTask vt = b.Task; + Assert.NotEqual(default, vt); + Assert.True(vt.IsCompletedSuccessfully); + Assert.Equal(42, vt.Result); + } + + [Fact] + public void NonGeneric_SetStateMachine_InvalidArgument_ThrowsException() + { + PoolingAsyncValueTaskMethodBuilder b = PoolingAsyncValueTaskMethodBuilder.Create(); + AssertExtensions.Throws("stateMachine", () => b.SetStateMachine(null)); + } + + [Fact] + public void Generic_SetStateMachine_InvalidArgument_ThrowsException() + { + PoolingAsyncValueTaskMethodBuilder b = PoolingAsyncValueTaskMethodBuilder.Create(); + AssertExtensions.Throws("stateMachine", () => b.SetStateMachine(null)); + } + + [Fact] + public void NonGeneric_Start_ExecutionContextChangesInMoveNextDontFlowOut() + { + var al = new AsyncLocal { Value = 0 }; + int calls = 0; + + var dsm = new DelegateStateMachine + { + MoveNextDelegate = () => + { + al.Value++; + calls++; + } + }; + + dsm.MoveNext(); + Assert.Equal(1, al.Value); + Assert.Equal(1, calls); + + dsm.MoveNext(); + Assert.Equal(2, al.Value); + Assert.Equal(2, calls); + + PoolingAsyncValueTaskMethodBuilder b = PoolingAsyncValueTaskMethodBuilder.Create(); + b.Start(ref dsm); + Assert.Equal(2, al.Value); // change should not be visible + Assert.Equal(3, calls); + + // Make sure we've not caused the Task to be allocated + b.SetResult(); + Assert.Equal(default, b.Task); + } + + [Fact] + public void Generic_Start_ExecutionContextChangesInMoveNextDontFlowOut() + { + var al = new AsyncLocal { Value = 0 }; + int calls = 0; + + var dsm = new DelegateStateMachine + { + MoveNextDelegate = () => + { + al.Value++; + calls++; + } + }; + + dsm.MoveNext(); + Assert.Equal(1, al.Value); + Assert.Equal(1, calls); + + dsm.MoveNext(); + Assert.Equal(2, al.Value); + Assert.Equal(2, calls); + + PoolingAsyncValueTaskMethodBuilder b = PoolingAsyncValueTaskMethodBuilder.Create(); + b.Start(ref dsm); + Assert.Equal(2, al.Value); // change should not be visible + Assert.Equal(3, calls); + + // Make sure we've not caused the Task to be allocated + b.SetResult(42); + Assert.Equal(new ValueTask(42), b.Task); + } + + [ActiveIssue("https://github.com/dotnet/roslyn/issues/51999")] + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + [InlineData(10)] + public static async Task NonGeneric_UsedWithAsyncMethod_CompletesSuccessfully(int yields) + { + StrongBox result; + + result = new StrongBox(); + await ValueTaskReturningAsyncMethod(42, result); + Assert.Equal(42 + yields, result.Value); + + result = new StrongBox(); + await ValueTaskReturningAsyncMethod(84, result); + Assert.Equal(84 + yields, result.Value); + + [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] + async ValueTask ValueTaskReturningAsyncMethod(int result, StrongBox output) + { + for (int i = 0; i < yields; i++) + { + await Task.Yield(); + result++; + } + output.Value = result; + } + } + + [ActiveIssue("https://github.com/dotnet/roslyn/issues/51999")] + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + [InlineData(10)] + public static async Task Generic_UsedWithAsyncMethod_CompletesSuccessfully(int yields) + { + Assert.Equal(42 + yields, await ValueTaskReturningAsyncMethod(42)); + Assert.Equal(84 + yields, await ValueTaskReturningAsyncMethod(84)); + + [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] + async ValueTask ValueTaskReturningAsyncMethod(int result) + { + for (int i = 0; i < yields; i++) + { + await Task.Yield(); + result++; + } + return result; + } + } + + [ActiveIssue("https://github.com/dotnet/roslyn/issues/51999")] + [Fact] + public static async Task AwaitTasksAndValueTasks_InTaskAndValueTaskMethods() + { + for (int i = 0; i < 2; i++) + { + await ValueTaskReturningMethod(); + Assert.Equal(18, await ValueTaskInt32ReturningMethod()); + } + + [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] + async ValueTask ValueTaskReturningMethod() + { + for (int i = 0; i < 3; i++) + { + // Complete + await Task.CompletedTask; + await Task.FromResult(42); + await new ValueTask(); + await Assert.ThrowsAsync(async () => await new ValueTask(Task.FromException(new FormatException()))); + await Assert.ThrowsAsync(async () => await new ValueTask(ManualResetValueTaskSourceFactory.Completed(0, new FormatException()), 0)); + Assert.Equal(42, await new ValueTask(42)); + Assert.Equal(42, await new ValueTask(Task.FromResult(42))); + Assert.Equal(42, await new ValueTask(ManualResetValueTaskSourceFactory.Completed(42, null), 0)); + await Assert.ThrowsAsync(async () => await new ValueTask(Task.FromException(new FormatException()))); + await Assert.ThrowsAsync(async () => await new ValueTask(ManualResetValueTaskSourceFactory.Completed(0, new FormatException()), 0)); + + // Incomplete + await Assert.ThrowsAsync(async () => await new ValueTask(Task.Delay(1).ContinueWith(_ => throw new FormatException()))); + await Assert.ThrowsAsync(async () => await new ValueTask(ManualResetValueTaskSourceFactory.Delay(1, 0, new FormatException()), 0)); + Assert.Equal(42, await new ValueTask(Task.Delay(1).ContinueWith(_ => 42))); + Assert.Equal(42, await new ValueTask(ManualResetValueTaskSourceFactory.Delay(1, 42, null), 0)); + await Assert.ThrowsAsync(async () => await new ValueTask(Task.Delay(1).ContinueWith(_ => throw new FormatException()))); + await Assert.ThrowsAsync(async () => await new ValueTask(ManualResetValueTaskSourceFactory.Delay(1, 0, new FormatException()), 0)); + await Task.Yield(); + } + } + + [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] + async ValueTask ValueTaskInt32ReturningMethod() + { + for (int i = 0; i < 3; i++) + { + // Complete + await Task.CompletedTask; + await Task.FromResult(42); + await new ValueTask(); + await Assert.ThrowsAsync(async () => await new ValueTask(Task.FromException(new FormatException()))); + await Assert.ThrowsAsync(async () => await new ValueTask(ManualResetValueTaskSourceFactory.Completed(0, new FormatException()), 0)); + Assert.Equal(42, await new ValueTask(42)); + Assert.Equal(42, await new ValueTask(Task.FromResult(42))); + Assert.Equal(42, await new ValueTask(ManualResetValueTaskSourceFactory.Completed(42, null), 0)); + await Assert.ThrowsAsync(async () => await new ValueTask(Task.FromException(new FormatException()))); + await Assert.ThrowsAsync(async () => await new ValueTask(ManualResetValueTaskSourceFactory.Completed(0, new FormatException()), 0)); + + // Incomplete + await Assert.ThrowsAsync(async () => await new ValueTask(Task.Delay(1).ContinueWith(_ => throw new FormatException()))); + await Assert.ThrowsAsync(async () => await new ValueTask(ManualResetValueTaskSourceFactory.Delay(1, 0, new FormatException()), 0)); + Assert.Equal(42, await new ValueTask(Task.Delay(1).ContinueWith(_ => 42))); + Assert.Equal(42, await new ValueTask(ManualResetValueTaskSourceFactory.Delay(1, 42, null), 0)); + await Assert.ThrowsAsync(async () => await new ValueTask(Task.Delay(1).ContinueWith(_ => throw new FormatException()))); + await Assert.ThrowsAsync(async () => await new ValueTask(ManualResetValueTaskSourceFactory.Delay(1, 0, new FormatException()), 0)); + await Task.Yield(); + } + return 18; + } + } + + [ActiveIssue("https://github.com/dotnet/roslyn/issues/51999")] + [Fact] + public async Task NonGeneric_ConcurrentBuilders_WorkCorrectly() + { + await Task.WhenAll(Enumerable.Range(0, Environment.ProcessorCount).Select(async _ => + { + for (int i = 0; i < 10; i++) + { + await ValueTaskAsync(); + + [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] + static async ValueTask ValueTaskAsync() + { + await Task.Delay(1); + } + } + })); + } + + [ActiveIssue("https://github.com/dotnet/roslyn/issues/51999")] + [Fact] + public async Task Generic_ConcurrentBuilders_WorkCorrectly() + { + await Task.WhenAll(Enumerable.Range(0, Environment.ProcessorCount).Select(async _ => + { + for (int i = 0; i < 10; i++) + { + Assert.Equal(42 + i, await ValueTaskAsync(i)); + + [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] + static async ValueTask ValueTaskAsync(int i) + { + await Task.Delay(1); + return 42 + i; + } + } + })); + } + + [ActiveIssue("https://github.com/dotnet/roslyn/issues/51999")] + [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [InlineData(null)] + [InlineData("1")] + [InlineData("100")] + public void PoolingAsyncValueTasksBuilder_ObjectsPooled(string limitEnvVar) + { + // Use RemoteExecutor to launch a process with the right environment variables set + var psi = new ProcessStartInfo(); + if (limitEnvVar != null) + { + psi.Environment.Add("DOTNET_SYSTEM_THREADING_POOLINGASYNCVALUETASKSCACHESIZE", limitEnvVar); + } + + RemoteExecutor.Invoke(async () => + { + var boxes = new ConcurrentQueue(); + var valueTasks = new ValueTask[10]; + int total = 0; + + // Invoke a bunch of ValueTask methods, some in parallel, + // and track a) their results and b) what boxing object is used. + for (int rep = 0; rep < 3; rep++) + { + for (int i = 0; i < valueTasks.Length; i++) + { + valueTasks[i] = ComputeAsync(i + 1, boxes); + } + foreach (ValueTask vt in valueTasks) + { + total += await vt; + } + } + + // Make sure we got the right total, and that if we expected pooling, + // we at least pooled one object. + Assert.Equal(330, total); + Assert.InRange(boxes.Distinct().Count(), 1, boxes.Count - 1); + }, new RemoteInvokeOptions() { StartInfo = psi }).Dispose(); + + [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] + static async ValueTask ComputeAsync(int input, ConcurrentQueue boxes) + { + await RecursiveValueTaskAsync(3, boxes); + return input * 2; + } + + [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] + static async ValueTask RecursiveValueTaskAsync(int depth, ConcurrentQueue boxes) + { + boxes.Enqueue(await GetStateMachineData.FetchAsync()); + if (depth > 0) + { + await Task.Delay(1); + await RecursiveValueTaskAsync(depth - 1, boxes); + } + } + } + + private struct DelegateStateMachine : IAsyncStateMachine + { + internal Action MoveNextDelegate; + public void MoveNext() => MoveNextDelegate?.Invoke(); + + public void SetStateMachine(IAsyncStateMachine stateMachine) { } + } + } +} diff --git a/src/libraries/System.Threading.Tasks.Extensions/tests/System.Threading.Tasks.Extensions.Tests.csproj b/src/libraries/System.Threading.Tasks.Extensions/tests/System.Threading.Tasks.Extensions.Tests.csproj index 6a7215d2b5cb..3cf17c52a2fc 100644 --- a/src/libraries/System.Threading.Tasks.Extensions/tests/System.Threading.Tasks.Extensions.Tests.csproj +++ b/src/libraries/System.Threading.Tasks.Extensions/tests/System.Threading.Tasks.Extensions.Tests.csproj @@ -8,6 +8,7 @@ + diff --git a/src/libraries/shims/ApiCompatBaseline.PreviousNetCoreApp.txt b/src/libraries/shims/ApiCompatBaseline.PreviousNetCoreApp.txt index c9cd20297935..401f0a5f0ba1 100644 --- a/src/libraries/shims/ApiCompatBaseline.PreviousNetCoreApp.txt +++ b/src/libraries/shims/ApiCompatBaseline.PreviousNetCoreApp.txt @@ -23,4 +23,5 @@ CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'System.Ru CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' changed from '[AttributeUsageAttribute(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Enum | AttributeTargets.Event | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Module | AttributeTargets.Property | AttributeTargets.Struct, AllowMultiple=true, Inherited=false)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Enum | AttributeTargets.Event | AttributeTargets.Field | AttributeTargets.Interface | AttributeTargets.Method | AttributeTargets.Module | AttributeTargets.Property | AttributeTargets.Struct, AllowMultiple=true, Inherited=false)]' in the implementation. Compat issues with assembly System.Security.Cryptography.Algorithms: CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Security.Cryptography.CryptoConfig' in the contract but not the implementation. -Total Issues: 15 +CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'System.Runtime.CompilerServices.AsyncMethodBuilderAttribute' changed from '[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Delegate | AttributeTargets.Enum | AttributeTargets.Interface | AttributeTargets.Struct, Inherited=false, AllowMultiple=false)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Delegate | AttributeTargets.Enum | AttributeTargets.Interface | AttributeTargets.Method | AttributeTargets.Struct, Inherited=false, AllowMultiple=false)]' in the implementation. +Total Issues: 16 diff --git a/src/libraries/shims/ApiCompatBaseline.netcoreapp.netstandardOnly.txt b/src/libraries/shims/ApiCompatBaseline.netcoreapp.netstandardOnly.txt index b5e47f7d9647..2510e64f1e5c 100644 --- a/src/libraries/shims/ApiCompatBaseline.netcoreapp.netstandardOnly.txt +++ b/src/libraries/shims/ApiCompatBaseline.netcoreapp.netstandardOnly.txt @@ -195,4 +195,5 @@ CannotRemoveAttribute : Attribute 'System.ComponentModel.BrowsableAttribute' exi CannotRemoveAttribute : Attribute 'System.ComponentModel.CategoryAttribute' exists on 'System.Timers.Timer.Elapsed' in the contract but not the implementation. CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'System.Xml.Serialization.XmlAnyAttributeAttribute' changed from '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple=false)]' in the implementation. CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'System.Xml.Serialization.XmlNamespaceDeclarationsAttribute' changed from '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple=false)]' in the implementation. -Total Issues: 196 +CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'System.Runtime.CompilerServices.AsyncMethodBuilderAttribute' changed from '[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Delegate | AttributeTargets.Enum | AttributeTargets.Interface | AttributeTargets.Struct, Inherited=false, AllowMultiple=false)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Delegate | AttributeTargets.Enum | AttributeTargets.Interface | AttributeTargets.Method | AttributeTargets.Struct, Inherited=false, AllowMultiple=false)]' in the implementation. +Total Issues: 198 From 72e41f9c89a0874352ad013dd0534e986a8819dc Mon Sep 17 00:00:00 2001 From: Dong-Heon Jung Date: Thu, 1 Apr 2021 00:42:59 +0900 Subject: [PATCH 71/98] Fix a crash in WriteMethodProfileDataLogFile (#50473) pEmitter is not assigned in https://github.com/dotnet/runtime/pull/50242 --- src/coreclr/vm/ceeload.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/ceeload.cpp b/src/coreclr/vm/ceeload.cpp index 12c12f41b0b2..f84714c9ce67 100644 --- a/src/coreclr/vm/ceeload.cpp +++ b/src/coreclr/vm/ceeload.cpp @@ -11855,7 +11855,7 @@ HRESULT Module::WriteMethodProfileDataLogFile(bool cleanup) { if (GetAssembly()->IsInstrumented() && (m_pProfilingBlobTable != NULL) && (m_tokenProfileData != NULL)) { - NewHolder pEmitter; + NewHolder pEmitter(new ProfileEmitter()); // Get this ahead of time - metadata access may be logged, which will // take the m_tokenProfileData->crst, which we take a couple lines below From b24c458f1a421af46c95da557b54b12e5e170636 Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Wed, 31 Mar 2021 11:13:49 -0500 Subject: [PATCH 72/98] Rename CreateInstanceT_StructWithoutDefaultConstructor_ThrowsMissingMethodException test (#50191) This doesn't throw an exception, so the test shouldn't be named "ThrowsMissingMethodException" --- .../System.Runtime/tests/System/ActivatorTests.Generic.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime/tests/System/ActivatorTests.Generic.cs b/src/libraries/System.Runtime/tests/System/ActivatorTests.Generic.cs index 2ba5c521ae87..1593cd0936ed 100644 --- a/src/libraries/System.Runtime/tests/System/ActivatorTests.Generic.cs +++ b/src/libraries/System.Runtime/tests/System/ActivatorTests.Generic.cs @@ -53,7 +53,7 @@ public void CreateInstanceT_StructWithPrivateDefaultConstructor_ThrowsMissingMet Assert.Throws(() => Activator.CreateInstance()); [Fact] - public void CreateInstanceT_StructWithoutDefaultConstructor_ThrowsMissingMethodException() => + public void CreateInstanceT_StructWithoutDefaultConstructor_InvokesConstructor() => Activator.CreateInstance(); [Fact] From 1ce7460578dad70bf5b2d141d21698c39a33f052 Mon Sep 17 00:00:00 2001 From: Fan Yang <52458914+fanyang-mono@users.noreply.github.com> Date: Wed, 31 Mar 2021 13:03:27 -0400 Subject: [PATCH 73/98] Update testing-android.md (#50484) --- docs/workflow/testing/libraries/testing-android.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/workflow/testing/libraries/testing-android.md b/docs/workflow/testing/libraries/testing-android.md index 596d7851f1a8..08ad7316814b 100644 --- a/docs/workflow/testing/libraries/testing-android.md +++ b/docs/workflow/testing/libraries/testing-android.md @@ -45,6 +45,7 @@ curl https://dl.google.com/android/repository/commandlinetools-${HOST_OS_SHORT}- mkdir ${ANDROID_SDK_ROOT} && unzip ~/asdk.zip -d ${ANDROID_SDK_ROOT}/cmdline-tools && rm -rf ~/asdk.zip yes | ${ANDROID_SDK_ROOT}/cmdline-tools/tools/bin/sdkmanager --sdk_root=${ANDROID_SDK_ROOT} --licenses ${ANDROID_SDK_ROOT}/cmdline-tools/tools/bin/sdkmanager --sdk_root=${ANDROID_SDK_ROOT} "platform-tools" "platforms;android-${SDK_API_LEVEL}" "build-tools;${SDK_BUILD_TOOLS}" +``` ## Building Libs and Tests for Android From ba5323eadfe22de78cbffd66f5580e9efc45a257 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Wed, 31 Mar 2021 13:21:16 -0400 Subject: [PATCH 74/98] [mono] Fix the handling of jit icall wrappers in mono_aot_method_hash (), the name might be different at runtime if DISABLE_JIT is set. (#50482) --- src/mono/mono/mini/aot-compiler.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/mono/mono/mini/aot-compiler.c b/src/mono/mono/mini/aot-compiler.c index c75493a74030..c602e4a13a22 100644 --- a/src/mono/mono/mini/aot-compiler.c +++ b/src/mono/mono/mini/aot-compiler.c @@ -10574,7 +10574,11 @@ mono_aot_method_hash (MonoMethod *method) hashes [0] = mono_metadata_str_hash (m_class_get_name (klass)); hashes [1] = mono_metadata_str_hash (m_class_get_name_space (klass)); } - hashes [2] = mono_metadata_str_hash (method->name); + if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE && mono_marshal_get_wrapper_info (method)->subtype == WRAPPER_SUBTYPE_ICALL_WRAPPER) + /* The name might not be set correctly if DISABLE_JIT is set */ + hashes [2] = mono_marshal_get_wrapper_info (method)->d.icall.jit_icall_id; + else + hashes [2] = mono_metadata_str_hash (method->name); hashes [3] = method->wrapper_type; hashes [4] = mono_aot_type_hash (sig->ret); hindex = 5; From bb4acfb7ef97454fc5a5018cdcc7377e55499e8a Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Wed, 31 Mar 2021 10:30:24 -0700 Subject: [PATCH 75/98] Add IsWellKnownStringComparer methods (#50312) --- .../src/System/StringComparer.cs | 109 +++++++++++++++++ .../System.Runtime/ref/System.Runtime.cs | 2 + .../tests/System/StringComparerTests.cs | 113 ++++++++++++++++++ 3 files changed, 224 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/StringComparer.cs b/src/libraries/System.Private.CoreLib/src/System/StringComparer.cs index 15a1055607ac..ce22da699c92 100644 --- a/src/libraries/System.Private.CoreLib/src/System/StringComparer.cs +++ b/src/libraries/System.Private.CoreLib/src/System/StringComparer.cs @@ -62,6 +62,102 @@ public static StringComparer Create(CultureInfo culture, CompareOptions options) return new CultureAwareComparer(culture, options); } + /// + /// Determines whether the specified is a well-known ordinal string comparer. + /// + /// The comparer to query. + /// When this method returns, contains a value stating whether + /// is case-insensitive. Set to if this method returns . + /// + /// if is a well-known ordinal string comparer; + /// otherwise, . + /// + /// + /// A "well-known ordinal comparer" describes a comparer which behaves identically to + /// when passed to or . + /// For example, is a well-known ordinal comparer because + /// a given as a constructor + /// argument will behave identically to a given + /// as a constructor argument. If is on method exit, + /// then behaves identically to when passed to the + /// constructor of such a collection. + /// + public static bool IsWellKnownOrdinalComparer(IEqualityComparer? comparer, out bool ignoreCase) + { + if (comparer is IInternalStringEqualityComparer internalStringComparer) + { + comparer = internalStringComparer.GetUnderlyingEqualityComparer(); // unwrap if necessary + } + + switch (comparer) + { + case StringComparer stringComparer: + return stringComparer.IsWellKnownOrdinalComparerCore(out ignoreCase); + case GenericEqualityComparer: + // special-case EqualityComparer.Default, which is Ordinal-equivalent + ignoreCase = false; + return true; + default: + // unknown comparer + ignoreCase = default; + return false; + } + } + + private protected virtual bool IsWellKnownOrdinalComparerCore(out bool ignoreCase) + { + // unless specialized comparer overrides this, we're not a well-known ordinal comparer + ignoreCase = default; + return false; + } + + /// + /// Determines whether the specified is a well-known culture-aware string comparer. + /// + /// The comparer to query. + /// When this method returns, contains a value indicating which was used + /// to create . Set to if this method returns . + /// When this method returns, contains a value indicating which was used + /// to create . Set to if this method returns . + /// whether + /// + /// if is a well-known culture-aware string comparer; + /// otherwise, . + /// + /// + /// A "well-known culture-aware comparer" describes a comparer which is tied to a specific using + /// some defined . To create a instance wrapped around a + /// and , use . + /// This method returns when given and other non-linguistic comparers as input. + /// + public static bool IsWellKnownCultureAwareComparer(IEqualityComparer? comparer, [NotNullWhen(true)] out CompareInfo? compareInfo, out CompareOptions compareOptions) + { + if (comparer is IInternalStringEqualityComparer internalStringComparer) + { + comparer = internalStringComparer.GetUnderlyingEqualityComparer(); // unwrap if necessary + } + + if (comparer is StringComparer stringComparer) + { + return stringComparer.IsWellKnownCultureAwareComparerCore(out compareInfo, out compareOptions); + } + else + { + // unknown comparer + compareInfo = default; + compareOptions = default; + return false; + } + } + + private protected virtual bool IsWellKnownCultureAwareComparerCore([NotNullWhen(true)] out CompareInfo? compareInfo, out CompareOptions compareOptions) + { + // unless specialized comparer overrides this, we're not a well-known culture-aware comparer + compareInfo = default; + compareOptions = default; + return false; + } + public int Compare(object? x, object? y) { if (x == y) return 0; @@ -202,6 +298,13 @@ public void GetObjectData(SerializationInfo info, StreamingContext context) info.AddValue("_options", _options); info.AddValue("_ignoreCase", (_options & CompareOptions.IgnoreCase) != 0); } + + private protected override bool IsWellKnownCultureAwareComparerCore([NotNullWhen(true)] out CompareInfo? compareInfo, out CompareOptions compareOptions) + { + compareInfo = _compareInfo; + compareOptions = _options; + return true; + } } [Serializable] @@ -280,6 +383,12 @@ public override int GetHashCode() int hashCode = nameof(OrdinalComparer).GetHashCode(); return _ignoreCase ? (~hashCode) : hashCode; } + + private protected override bool IsWellKnownOrdinalComparerCore(out bool ignoreCase) + { + ignoreCase = _ignoreCase; + return true; + } } [Serializable] diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 303016c56243..97f212162c9d 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -3699,6 +3699,8 @@ protected StringComparer() { } public static System.StringComparer FromComparison(System.StringComparison comparisonType) { throw null; } public int GetHashCode(object obj) { throw null; } public abstract int GetHashCode(string obj); + public static bool IsWellKnownCultureAwareComparer(System.Collections.Generic.IEqualityComparer? comparer, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Globalization.CompareInfo? compareInfo, out System.Globalization.CompareOptions compareOptions) { throw null; } + public static bool IsWellKnownOrdinalComparer(System.Collections.Generic.IEqualityComparer? comparer, out bool ignoreCase) { throw null; } } public enum StringComparison { diff --git a/src/libraries/System.Runtime/tests/System/StringComparerTests.cs b/src/libraries/System.Runtime/tests/System/StringComparerTests.cs index 364ecdfb2602..3027c870855b 100644 --- a/src/libraries/System.Runtime/tests/System/StringComparerTests.cs +++ b/src/libraries/System.Runtime/tests/System/StringComparerTests.cs @@ -1,7 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using System.Globalization; +using System.Reflection; using Xunit; namespace System.Tests @@ -135,5 +137,116 @@ public void CreateCultureOptions_CreatesValidComparer() Assert.Equal(1, c.Compare("42", null)); Assert.Throws(() => c.Compare(42, "84")); } + + [Fact] + public void IsWellKnownOrdinalComparer_TestCases() + { + CompareInfo ci_enUS = CompareInfo.GetCompareInfo("en-US"); + + // First, instantiate and test the comparers directly + + RunTest(null, false, false); + RunTest(EqualityComparer.Default, true, false); // EC.Default is Ordinal-equivalent + RunTest(EqualityComparer.Default, false, false); // EC.Default isn't a string comparer + RunTest(StringComparer.Ordinal, true, false); + RunTest(StringComparer.OrdinalIgnoreCase, true, true); + RunTest(StringComparer.InvariantCulture, false, false); // not ordinal + RunTest(StringComparer.InvariantCultureIgnoreCase, false, false); // not ordinal + RunTest(GetNonRandomizedComparer("WrappedAroundDefaultComparer"), true, false); // EC.Default is Ordinal-equivalent + RunTest(GetNonRandomizedComparer("WrappedAroundStringComparerOrdinal"), true, false); + RunTest(GetNonRandomizedComparer("WrappedAroundStringComparerOrdinalIgnoreCase"), true, true); + RunTest(new CustomStringComparer(), false, false); // not an inbox comparer + RunTest(ci_enUS.GetStringComparer(CompareOptions.None), false, false); // linguistic + RunTest(ci_enUS.GetStringComparer(CompareOptions.Ordinal), true, false); + RunTest(ci_enUS.GetStringComparer(CompareOptions.OrdinalIgnoreCase), true, true); + + // Then, make sure that this API works with common collection types + + RunTest(new Dictionary().Comparer, true, false); + RunTest(new Dictionary(StringComparer.Ordinal).Comparer, true, false); + RunTest(new Dictionary(StringComparer.OrdinalIgnoreCase).Comparer, true, true); + RunTest(new Dictionary(StringComparer.InvariantCulture).Comparer, false, false); + RunTest(new Dictionary(StringComparer.InvariantCultureIgnoreCase).Comparer, false, false); + + RunTest(new HashSet().Comparer, true, false); + RunTest(new HashSet(StringComparer.Ordinal).Comparer, true, false); + RunTest(new HashSet(StringComparer.OrdinalIgnoreCase).Comparer, true, true); + RunTest(new HashSet(StringComparer.InvariantCulture).Comparer, false, false); + RunTest(new HashSet(StringComparer.InvariantCultureIgnoreCase).Comparer, false, false); + + static void RunTest(IEqualityComparer comparer, bool expectedIsOrdinal, bool expectedIgnoreCase) + { + Assert.Equal(expectedIsOrdinal, StringComparer.IsWellKnownOrdinalComparer(comparer, out bool actualIgnoreCase)); + Assert.Equal(expectedIgnoreCase, actualIgnoreCase); + } + } + + [Fact] + public void IsWellKnownCultureAwareComparer_TestCases() + { + CompareInfo ci_enUS = CompareInfo.GetCompareInfo("en-US"); + CompareInfo ci_inv = CultureInfo.InvariantCulture.CompareInfo; + + // First, instantiate and test the comparers directly + + RunTest(null, null, default); + RunTest(EqualityComparer.Default, null, default); // EC.Default is not culture-aware + RunTest(EqualityComparer.Default, null, default); // EC.Default isn't a string comparer + RunTest(StringComparer.Ordinal, null, default); + RunTest(StringComparer.OrdinalIgnoreCase, null, default); + RunTest(StringComparer.InvariantCulture, ci_inv, CompareOptions.None); + RunTest(StringComparer.InvariantCultureIgnoreCase, ci_inv, CompareOptions.IgnoreCase); + RunTest(GetNonRandomizedComparer("WrappedAroundDefaultComparer"), null, default); // EC.Default is Ordinal-equivalent + RunTest(GetNonRandomizedComparer("WrappedAroundStringComparerOrdinal"), null, default); + RunTest(GetNonRandomizedComparer("WrappedAroundStringComparerOrdinalIgnoreCase"), null, default); + RunTest(new CustomStringComparer(), null, default); // not an inbox comparer + RunTest(ci_enUS.GetStringComparer(CompareOptions.None), ci_enUS, CompareOptions.None); + RunTest(ci_enUS.GetStringComparer(CompareOptions.IgnoreCase | CompareOptions.IgnoreKanaType), ci_enUS, CompareOptions.IgnoreCase | CompareOptions.IgnoreKanaType); + RunTest(ci_enUS.GetStringComparer(CompareOptions.Ordinal), null, default); // not linguistic + RunTest(ci_enUS.GetStringComparer(CompareOptions.OrdinalIgnoreCase), null, default); // not linguistic + RunTest(StringComparer.Create(CultureInfo.InvariantCulture, false), ci_inv, CompareOptions.None); + RunTest(StringComparer.Create(CultureInfo.InvariantCulture, true), ci_inv, CompareOptions.IgnoreCase); + RunTest(StringComparer.Create(CultureInfo.InvariantCulture, CompareOptions.IgnoreSymbols), ci_inv, CompareOptions.IgnoreSymbols); + + // Then, make sure that this API works with common collection types + + RunTest(new Dictionary().Comparer, null, default); + RunTest(new Dictionary(StringComparer.Ordinal).Comparer, null, default); + RunTest(new Dictionary(StringComparer.OrdinalIgnoreCase).Comparer, null, default); + RunTest(new Dictionary(StringComparer.InvariantCulture).Comparer, ci_inv, CompareOptions.None); + RunTest(new Dictionary(StringComparer.InvariantCultureIgnoreCase).Comparer, ci_inv, CompareOptions.IgnoreCase); + + RunTest(new HashSet().Comparer, null, default); + RunTest(new HashSet(StringComparer.Ordinal).Comparer, null, default); + RunTest(new HashSet(StringComparer.OrdinalIgnoreCase).Comparer, null, default); + RunTest(new HashSet(StringComparer.InvariantCulture).Comparer, ci_inv, CompareOptions.None); + RunTest(new HashSet(StringComparer.InvariantCultureIgnoreCase).Comparer, ci_inv, CompareOptions.IgnoreCase); + + static void RunTest(IEqualityComparer comparer, CompareInfo expectedCompareInfo, CompareOptions expectedCompareOptions) + { + bool actualReturnValue = StringComparer.IsWellKnownCultureAwareComparer(comparer, out CompareInfo actualCompareInfo, out CompareOptions actualCompareOptions); + Assert.Equal(expectedCompareInfo != null, actualReturnValue); + Assert.Equal(expectedCompareInfo, actualCompareInfo); + Assert.Equal(expectedCompareOptions, actualCompareOptions); + } + } + + private static IEqualityComparer GetNonRandomizedComparer(string name) + { + Type nonRandomizedComparerType = typeof(StringComparer).Assembly.GetType("System.Collections.Generic.NonRandomizedStringEqualityComparer"); + Assert.NotNull(nonRandomizedComparerType); + + FieldInfo fi = nonRandomizedComparerType.GetField(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); + Assert.NotNull(fi); + + return (IEqualityComparer)fi.GetValue(null); + } + + private class CustomStringComparer : StringComparer + { + public override int Compare(string x, string y) => throw new NotImplementedException(); + public override bool Equals(string x, string y) => throw new NotImplementedException(); + public override int GetHashCode(string obj) => throw new NotImplementedException(); + } } } From 94bb5d2f5c64e2059a64fc76680ae6c2246392a2 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Wed, 31 Mar 2021 10:41:10 -0700 Subject: [PATCH 76/98] Support private SuperPMI MCH file stores (#50268) * Support private SuperPMI MCH file stores In addition to the default Azure Storage SuperPMI location, support private data stores via the new `-private_store` argument to `replay` and `asmdiffs`. Private stores are typically file system locations, or, on Windows, UNC paths. There already exists the `-mch_files` argument, which allows you to use any specified set of MCH files. However, when specifying `-mch_files`, only those files are used and the normal stores are ignored. The private store mechanism is more convenient in some scenarios, as it treats the specified private stores equivalently to the default Azure Storage store. In addition, both private stores and `-mch_files` handle downloading ZIP files, un-ZIPping, and caching their contents. Finally, a new top-level command `upload-private` is introduced to help create private stores by specifying a set of MCH files to use. It ZIP compresses the files to upload. * Fix `-base_jit_path` If `-base_jit_path` is specified on the command line, ensure it is a full path before using it. --- src/coreclr/scripts/superpmi.py | 378 ++++++++++++++++++++++++-------- 1 file changed, 289 insertions(+), 89 deletions(-) diff --git a/src/coreclr/scripts/superpmi.py b/src/coreclr/scripts/superpmi.py index 2f956b1214e7..9c4b670de34d 100755 --- a/src/coreclr/scripts/superpmi.py +++ b/src/coreclr/scripts/superpmi.py @@ -93,6 +93,10 @@ Upload a collection to SuperPMI Azure storage. """ +upload_private_description = """\ +Upload a collection to a local file system path. +""" + download_description = """\ Download collections from SuperPMI Azure storage. Normally, collections are automatically downloaded to a local cache @@ -162,6 +166,11 @@ the Azure Storage MCH file store. UNC paths will be downloaded and cached locally. """ +private_store_help = """\ +Specify the path to one or more private SuperPMI data stores. Default: use the semicolon separated +value of the SUPERPMI_PRIVATE_STORE environment variable, if it exists. +""" + filter_help = """\ Specify one or more filters to restrict the set of MCH files to download or use from the local cache. A filter is a simple case-insensitive substring search against the MCH file path. If multiple filter @@ -219,7 +228,7 @@ core_root_parser.add_argument("-log_file", help=log_file_help) core_root_parser.add_argument("-spmi_location", help=spmi_location_help) -# Create a set of arguments common to target specification. Used for replay, upload, download, list-collections. +# Create a set of arguments common to target specification. Used for replay, upload, upload-private, download, list-collections. target_parser = argparse.ArgumentParser(add_help=False) @@ -277,6 +286,7 @@ replay_common_parser.add_argument("-product_location", help=product_location_help) replay_common_parser.add_argument("--force_download", action="store_true", help=force_download_help) replay_common_parser.add_argument("-jit_ee_version", help=jit_ee_version_help) +replay_common_parser.add_argument("-private_store", action="append", help=private_store_help) # subparser for replay replay_parser = subparsers.add_parser("replay", description=replay_description, parents=[core_root_parser, target_parser, superpmi_common_parser, replay_common_parser]) @@ -306,6 +316,14 @@ upload_parser.add_argument("-jit_ee_version", help=jit_ee_version_help) upload_parser.add_argument("--skip_cleanup", action="store_true", help=skip_cleanup_help) +# subparser for upload-private +upload_private_parser = subparsers.add_parser("upload-private", description=upload_private_description, parents=[core_root_parser, target_parser]) + +upload_private_parser.add_argument("-mch_files", metavar="MCH_FILE", required=True, nargs='+', help=upload_mch_files_help) +upload_private_parser.add_argument("-private_store", required=True, help="Target directory root of the private store in which to place the files.") +upload_private_parser.add_argument("-jit_ee_version", help=jit_ee_version_help) +upload_private_parser.add_argument("--skip_cleanup", action="store_true", help=skip_cleanup_help) + # subparser for download download_parser = subparsers.add_parser("download", description=download_description, parents=[core_root_parser, target_parser]) @@ -314,6 +332,7 @@ download_parser.add_argument("--skip_cleanup", action="store_true", help=skip_cleanup_help) download_parser.add_argument("--force_download", action="store_true", help=force_download_help) download_parser.add_argument("-mch_files", metavar="MCH_FILE", nargs='+', help=replay_mch_files_help) +download_parser.add_argument("-private_store", action="append", help=private_store_help) # subparser for list-collections list_collections_parser = subparsers.add_parser("list-collections", description=list_collections_description, parents=[core_root_parser, target_parser]) @@ -705,7 +724,7 @@ def create_artifacts_base_name(coreclr_args, mch_file): Use the MCH file base name as the main part of the directory name, removing the trailing ".mch", if any. - + If there is a tag specified (for asm diffs), prepend the tag. Args: @@ -723,6 +742,20 @@ def create_artifacts_base_name(coreclr_args, mch_file): return artifacts_base_name +def is_url(path): + """ Return True if this looks like a URL + + Args: + path (str) : name to check + + Returns: + True it it looks like an URL, False otherwise. + """ + # Probably could use urllib.parse to be more precise. + # If it doesn't look like an URL, treat it like a file, possibly a UNC file. + return path.lower().startswith("http:") or path.lower().startswith("https:") + + ################################################################################ # Helper classes ################################################################################ @@ -2293,7 +2326,7 @@ def determine_jit_ee_version(coreclr_args): NOTE: When using mcs, we need to run the tool. So we need a version that will run. If a user specifies an "-arch" argument that creates a Core_Root path that won't run, like an arm32 Core_Root on an - x64 machine, this won't work. This could happen if doing "upload" or "list-collections" on + x64 machine, this won't work. This could happen if doing "upload", "upload-private", or "list-collections" on collections from a machine that didn't create the native collections. We should create a "native" Core_Root and use that in case there are "cross-arch" scenarios. @@ -2465,35 +2498,27 @@ def list_superpmi_collections_container(path_filter=lambda unused: True): return list_superpmi_collections_container_via_rest_api(path_filter) -def process_mch_files_arg(coreclr_args): - """ Process the -mch_files argument. If the argument is empty, then download files from Azure Storage. - If the argument is non-empty, check it for UNC paths and download/cache those files, replacing - them with a reference to the newly cached local paths (this is on Windows only). +def process_local_mch_files(coreclr_args, mch_files, mch_cache_dir): + """ Process the MCH files to use. Args: coreclr_args (CoreclrArguments): parsed args + mch_files (list): list of MCH files locations. Normally, this comes from the `-mch_files` argument, but it can + also come from the `private_store` argument. It can be a list of files or directories or both. + mch_cache_dir (str): the directory to cache any downloads. Returns: - nothing - - coreclr_args.mch_files is updated - + list of full paths of locally cached MCH files to use """ - if coreclr_args.mch_files is None: - coreclr_args.mch_files = download_mch(coreclr_args, include_baseline_jit=True) - return - # Create the cache location. Note that we'll create it even if we end up not copying anything. - default_mch_root_dir = os.path.join(coreclr_args.spmi_location, "mch") - default_mch_dir = os.path.join(default_mch_root_dir, "{}.{}.{}".format(coreclr_args.jit_ee_version, coreclr_args.target_os, coreclr_args.mch_arch)) - if not os.path.isdir(default_mch_dir): - os.makedirs(default_mch_dir) + if not os.path.isdir(mch_cache_dir): + os.makedirs(mch_cache_dir) # Process the mch_files list. Download and cache UNC and HTTP files. urls = [] local_mch_files = [] - for item in coreclr_args.mch_files: + for item in mch_files: # On Windows only, see if any of the mch_files are UNC paths (i.e., "\\server\share\..."). # If so, download and cache all the files found there to our usual local cache location, to avoid future network access. if coreclr_args.host_os == "windows" and item.startswith("\\\\"): @@ -2501,30 +2526,30 @@ def process_mch_files_arg(coreclr_args): # This happens naturally if a directory is passed and we search for all .mch and .mct files in that directory. mch_file = os.path.abspath(item) if os.path.isfile(mch_file) and mch_file.endswith(".mch"): - files = [ mch_file ] + urls.append(mch_file) mct_file = mch_file + ".mct" if os.path.isfile(mct_file): - files.append(mct_file) + urls.append(mct_file) else: - files = get_files_from_path(mch_file, match_func=lambda path: any(path.endswith(extension) for extension in [".mch", ".mct"])) - - for file in files: - # Download file to cache, and report that as the file to use. - cache_file = os.path.join(default_mch_dir, os.path.basename(file)) - logging.info("Cache %s => %s", file, cache_file) - local_mch_file = shutil.copy2(file, cache_file) - local_mch_files.append(local_mch_file) + urls += get_files_from_path(mch_file, match_func=lambda path: any(path.lower().endswith(extension) for extension in [".mch", ".mct", ".zip"])) elif item.lower().startswith("http:") or item.lower().startswith("https:"): # probably could use urllib.parse to be more precise urls.append(item) else: # Doesn't appear to be a UNC path (on Windows) or a URL, so just use it as-is. local_mch_files.append(item) + # Now apply any filtering we've been asked to do. + def filter_local_path(path): + path = path.lower() + return (coreclr_args.filter is None) or any((filter_item.lower() in path) for filter_item in coreclr_args.filter) + + urls = [url for url in urls if filter_local_path(url)] + # Download all the urls at once, and add the local cache filenames to our accumulated list of local file names. if len(urls) != 0: - local_mch_files += download_urls(urls, default_mch_dir) + local_mch_files += download_files(urls, mch_cache_dir) - # Special case: walk the URLs list list and for every ".mch" or ".mch.zip" file, check to see that either the associated ".mct" file is already + # Special case: walk the URLs list and for every ".mch" or ".mch.zip" file, check to see that either the associated ".mct" file is already # in the list, or add it to a new list to attempt to download (but don't fail the download if it doesn't exist). mct_urls = [] for url in urls: @@ -2533,36 +2558,76 @@ def process_mch_files_arg(coreclr_args): if mct_url not in urls: mct_urls.append(mct_url) if len(mct_urls) != 0: - local_mch_files += download_urls(mct_urls, default_mch_dir, fail_if_not_found=False) + local_mch_files += download_files(mct_urls, mch_cache_dir, fail_if_not_found=False) - coreclr_args.mch_files = local_mch_files + # Even though we might have downloaded MCT files, only return the set of MCH files. + local_mch_files = [file for file in local_mch_files if any(file.lower().endswith(extension) for extension in [".mch"])] + return local_mch_files -def download_mch(coreclr_args, include_baseline_jit=False): - """ Download the mch files. This can be called to re-download files and - overwrite them in the target location. + +def process_mch_files_arg(coreclr_args): + """ Process the -mch_files argument. If the argument is not specified, then download files + from Azure Storage and any specified private MCH stores. + + Any files on UNC (i.e., "\\server\share" paths on Windows) or Azure Storage stores, + even if specified via the `-mch_files` argument, will be downloaded and cached locally, + replacing the paths with a reference to the newly cached local paths. + + If the `-mch_files` argument is specified, files are always either used directly or copied and + cached locally. These will be the only files used. + + If the `-mch_files` argument is not specified, and there exists a cache, then only files already + in the cache are used and no MCH stores are consulted, unless the `--force_download` option is + specified, in which case normal MCH store processing is done. This behavior is to avoid + touching the network unless required. Args: coreclr_args (CoreclrArguments): parsed args - include_baseline_jit (bool): If True, also download the baseline jit Returns: - list containing the directory to which the files were downloaded - + list of local full paths of MCH files or directories to use """ - default_mch_root_dir = os.path.join(coreclr_args.spmi_location, "mch") - default_mch_dir = os.path.join(default_mch_root_dir, "{}.{}.{}".format(coreclr_args.jit_ee_version, coreclr_args.target_os, coreclr_args.mch_arch)) + mch_cache_dir = os.path.join(coreclr_args.spmi_location, "mch", "{}.{}.{}".format(coreclr_args.jit_ee_version, coreclr_args.target_os, coreclr_args.mch_arch)) - if os.path.isdir(default_mch_dir) and not coreclr_args.force_download: + # If an `-mch_files` argument was given, then use exactly that set of files. + if coreclr_args.mch_files is not None: + return process_local_mch_files(coreclr_args, coreclr_args.mch_files, mch_cache_dir) + + # Otherwise, use both Azure Storage, and optionally, private stores. + # See if the cache directory already exists. If so, we just use it (unless `--force_download` is passed). + + if os.path.isdir(mch_cache_dir) and not coreclr_args.force_download: # The cache directory is already there, and "--force_download" was passed, so just # assume it's got what we want. # NOTE: a different solution might be to verify that everything we would download is # already in the cache, and simply not download if it is. However, that would # require hitting the network, and currently once you've cached these, you # don't need to do that. - logging.info("Found download cache directory \"%s\" and --force_download not set; skipping download", default_mch_dir) - return [ default_mch_dir ] + logging.info("Found download cache directory \"%s\" and --force_download not set; skipping download", mch_cache_dir) + return [ mch_cache_dir ] + + local_mch_paths = download_mch_from_azure(coreclr_args, mch_cache_dir) + + # Add the private store files + if coreclr_args.private_store is not None: + local_mch_paths += process_local_mch_files(coreclr_args, coreclr_args.private_store, mch_cache_dir) + + return local_mch_paths + + +def download_mch_from_azure(coreclr_args, target_dir): + """ Download the mch files. This can be called to re-download files and + overwrite them in the target location. + + Args: + coreclr_args (CoreclrArguments): parsed args + target_dir (str): target directory to download the files + + Returns: + list containing the local path of files downloaded + """ blob_filter_string = "{}/{}/{}/".format(coreclr_args.jit_ee_version, coreclr_args.target_os, coreclr_args.mch_arch).lower() @@ -2573,50 +2638,58 @@ def download_mch(coreclr_args, include_baseline_jit=False): # If there are filters, only download those matching files. def filter_superpmi_collections(path): path = path.lower() - if "clrjit" in path and not include_baseline_jit: - return False return path.startswith(blob_filter_string) and ((coreclr_args.filter is None) or any((filter_item.lower() in path) for filter_item in coreclr_args.filter)) paths = list_superpmi_collections_container(filter_superpmi_collections) if paths is None or len(paths) == 0: - print("No MCH files to download from {}".format(blob_filter_string)) + print("No Azure Storage MCH files to download from {}".format(blob_filter_string)) return [] blob_url_prefix = "{}/{}/".format(az_blob_storage_superpmi_container_uri, az_collections_root_folder) urls = [blob_url_prefix + path for path in paths] - download_urls(urls, default_mch_dir) - return [ default_mch_dir ] + return download_files(urls, target_dir) -def download_urls(urls, target_dir, verbose=True, fail_if_not_found=True): - """ Download a set of files, specified as URLs, to a target directory. - If the URLs are to .ZIP files, then uncompress them and copy all contents - to the target directory. +def download_files(paths, target_dir, verbose=True, fail_if_not_found=True): + """ Download a set of files, specified as URLs or paths (such as Windows UNC paths), + to a target directory. If a file is a .ZIP file, then uncompress the file and + copy all its contents to the target directory. Args: - urls (list): the URLs to download - target_dir (str): target directory where files are copied. Directory must exist + paths (list): the URLs and paths to download + target_dir (str): target directory where files are copied. + verbse (bool): if True, do verbose logging. fail_if_not_found (bool): if True, fail if a download fails due to file not found (HTTP error 404). Otherwise, ignore the failure. Returns: - list of local filenames of downloaded files + list of full paths of local filenames of downloaded files in the target directory """ + if len(paths) == 0: + logging.warning("No files specified to download") + return None + if verbose: logging.info("Downloading:") - for url in urls: - logging.info(" %s", url) + for item_path in paths: + logging.info(" %s", item_path) + + # Create the target directory now, if it doesn't already exist. + target_dir = os.path.abspath(target_dir) + if not os.path.isdir(target_dir): + os.makedirs(target_dir) - local_files = [] + local_paths = [] # In case we'll need a temp directory for ZIP file processing, create it first. with TempDir() as temp_location: - for url in urls: - item_name = url.split("/")[-1] + for item_path in paths: + is_item_url = is_url(item_path) + item_name = item_path.split("/")[-1] if is_item_url else os.path.basename(item_path) - if url.lower().endswith(".zip"): + if item_path.lower().endswith(".zip"): # Delete everything in the temp_location (from previous iterations of this loop, so previous URL downloads). temp_location_items = [os.path.join(temp_location, item) for item in os.listdir(temp_location)] for item in temp_location_items: @@ -2626,9 +2699,15 @@ def download_urls(urls, target_dir, verbose=True, fail_if_not_found=True): os.remove(item) download_path = os.path.join(temp_location, item_name) - ok = download_one_url(url, download_path, fail_if_not_found) - if not ok: - continue + if is_item_url: + ok = download_one_url(item_path, download_path, fail_if_not_found) + if not ok: + continue + else: + if fail_if_not_found or os.path.isfile(item_path): + if verbose: + logging.info("Download: %s -> %s", item_path, download_path) + shutil.copy2(item_path, download_path) if verbose: logging.info("Uncompress %s", download_path) @@ -2636,26 +2715,28 @@ def download_urls(urls, target_dir, verbose=True, fail_if_not_found=True): file_handle.extractall(temp_location) # Copy everything that was extracted to the target directory. - if not os.path.isdir(target_dir): - os.makedirs(target_dir) items = [ os.path.join(temp_location, item) for item in os.listdir(temp_location) if not item.endswith(".zip") ] for item in items: target_path = os.path.join(target_dir, os.path.basename(item)) if verbose: logging.info("Copy %s -> %s", item, target_path) shutil.copy2(item, target_dir) - local_files.append(target_path) + local_paths.append(target_path) else: # Not a zip file; download directory to target directory - if not os.path.isdir(target_dir): - os.makedirs(target_dir) download_path = os.path.join(target_dir, item_name) - ok = download_one_url(url, download_path, fail_if_not_found) - if not ok: - continue - local_files.append(download_path) + if is_item_url: + ok = download_one_url(item_path, download_path, fail_if_not_found) + if not ok: + continue + else: + if fail_if_not_found or os.path.isfile(item_path): + if verbose: + logging.info("Download: %s -> %s", item_path, download_path) + shutil.copy2(item_path, download_path) + local_paths.append(download_path) - return local_files + return local_paths def upload_mch(coreclr_args): @@ -2727,6 +2808,63 @@ def upload_blob(file, blob_name): logging.info("Uploaded {:n} bytes".format(total_bytes_uploaded)) +def upload_private_mch(coreclr_args): + """ Upload a set of MCH files. Each MCH file is first ZIP compressed to save data space and upload/download time. + + Args: + coreclr_args (CoreclrArguments): parsed args + """ + + files = [] + for item in coreclr_args.mch_files: + files += get_files_from_path(item, match_func=lambda path: any(path.endswith(extension) for extension in [".mch"])) + + files_to_upload = [] + # Special case: walk the files list and for every ".mch" file, check to see that either the associated ".mct" file is already + # in the list, or add it if the ".mct" file exists. + for file in files.copy(): + if file.endswith(".mch") and os.stat(file).st_size > 0: + files_to_upload.append(file) + mct_file = file + ".mct" + if os.path.isfile(mct_file) and os.stat(mct_file).st_size > 0: + files_to_upload.append(mct_file) + + logging.info("Uploading:") + for item in files_to_upload: + logging.info(" %s", item) + + file_folder_name = os.path.join(coreclr_args.private_store, coreclr_args.jit_ee_version, coreclr_args.target_os, coreclr_args.mch_arch) + if not os.path.isdir(file_folder_name): + os.makedirs(file_folder_name) + + total_bytes_uploaded = 0 + + with TempDir() as temp_location: + for file in files_to_upload: + # Zip compress the file we will upload + zip_name = os.path.basename(file) + ".zip" + zip_path = os.path.join(temp_location, zip_name) + logging.info("Compress %s -> %s", file, zip_path) + with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zip_file: + zip_file.write(file, os.path.basename(file)) + + original_stat_result = os.stat(file) + zip_stat_result = os.stat(zip_path) + logging.info("Compressed {:n} to {:n} bytes".format(original_stat_result.st_size, zip_stat_result.st_size)) + total_bytes_uploaded += zip_stat_result.st_size + + target_path = os.path.join(file_folder_name, zip_name) + logging.info("Uploading: %s (%s) -> %s", file, zip_path, target_path) + + if os.path.exists(target_path): + logging.warning("Warning: replacing existing file '%s'!", target_path) + os.remove(target_path) + + shutil.copy2(zip_path, target_path) + + logging.info("Uploaded {:n} bytes".format(total_bytes_uploaded)) + + def list_collections_command(coreclr_args): """ List the SuperPMI collections in Azure Storage @@ -2845,28 +2983,30 @@ def merge_mch(coreclr_args): return True -def get_mch_files_for_replay(coreclr_args): - """ Given the argument `mch_files`, and any specified filters, find all the MCH files to - use for replay. +def get_mch_files_for_replay(local_mch_paths, filters): + """ Given a list of local MCH files, and any specified filters (in coreclr_args.filter), + find all the MCH files to use for replay. Note that `local_mch_paths` can contain + both files and directories. Args: - coreclr_args (CoreclrArguments) : parsed args + local_mch_paths (list) : list of local files and directories to use to find MCH files to use + filters (list) : list of strings, one of which must match each candidate MCH path Returns: - None if error (with an error message already printed), else a list of MCH files. + None if error (with an error message already printed), else a filtered list of full paths of MCH files. """ - if coreclr_args.mch_files is None: + if local_mch_paths is None: logging.error("No MCH files specified") return None mch_files = [] - for item in coreclr_args.mch_files: + for item in local_mch_paths: # If there are specified filters, only run those matching files. mch_files += get_files_from_path(item, match_func=lambda path: any(path.endswith(extension) for extension in [".mch"]) - and ((coreclr_args.filter is None) or any(filter_item.lower() in path for filter_item in coreclr_args.filter))) + and ((filters is None) or any(filter_item.lower() in path for filter_item in filters))) if len(mch_files) == 0: logging.error("No MCH files found to replay") @@ -2913,6 +3053,7 @@ def process_base_jit_path_arg(coreclr_args): if coreclr_args.base_jit_path is not None: if not os.path.isfile(coreclr_args.base_jit_path): raise RuntimeError("Specified -base_jit_path does not point to a file") + coreclr_args.base_jit_path = os.path.abspath(coreclr_args.base_jit_path) return # We cache baseline jits under the following directory. Note that we can't create the full directory path @@ -2990,7 +3131,7 @@ def process_base_jit_path_arg(coreclr_args): blob_folder_name = "{}/{}/{}/{}/{}/{}".format(az_builds_root_folder, git_hash, coreclr_args.host_os, coreclr_args.arch, coreclr_args.build_type, jit_name) blob_uri = "{}/{}".format(az_blob_storage_jitrollingbuild_container_uri, blob_folder_name) urls = [ blob_uri ] - local_files = download_urls(urls, basejit_dir, verbose=False, fail_if_not_found=False) + local_files = download_files(urls, basejit_dir, verbose=False, fail_if_not_found=False) if len(local_files) > 0: if hashnum > 1: @@ -3207,6 +3348,12 @@ def verify_replay_common_args(): lambda unused: True, "Unable to set mch_files") + coreclr_args.verify(args, + "private_store", + lambda item: True, + "Specify private_store or set environment variable SUPERPMI_PRIVATE_STORE to use a private store.", + modify_arg=lambda arg: os.environ["SUPERPMI_PRIVATE_STORE"].split(";") if arg is None and "SUPERPMI_PRIVATE_STORE" in os.environ else arg) + if coreclr_args.mode == "collect": verify_target_args() @@ -3553,6 +3700,31 @@ def verify_replay_common_args(): lambda unused: True, "Unable to set mch_files") + elif coreclr_args.mode == "upload-private": + + verify_target_args() + verify_jit_ee_version_arg() + + coreclr_args.verify(args, + "mch_files", + lambda unused: True, + "Unable to set mch_files") + + coreclr_args.verify(args, + "private_store", + lambda unused: True, + "Unable to set private_store") + + if not os.path.isdir(coreclr_args.private_store): + print("Error: private store directory '" + coreclr_args.private_store + "' not found.") + sys.exit(1) + + # Safety measure: don't allow CLRJIT_AZ_KEY to be set if we are uploading to a private store. + # Note that this should be safe anyway, since we're publishing something private, not public. + if "CLRJIT_AZ_KEY" in os.environ: + print("Error: environment variable CLRJIT_AZ_KEY is set, but command is `upload-private`, not `upload`. That is not allowed.") + sys.exit(1) + elif coreclr_args.mode == "download": verify_target_args() @@ -3573,6 +3745,12 @@ def verify_replay_common_args(): lambda unused: True, "Unable to set mch_files") + coreclr_args.verify(args, + "private_store", + lambda item: True, + "Specify private_store or set environment variable SUPERPMI_PRIVATE_STORE to use a private store.", + modify_arg=lambda arg: os.environ["SUPERPMI_PRIVATE_STORE"].split(";") if arg is None and "SUPERPMI_PRIVATE_STORE" in os.environ else arg) + elif coreclr_args.mode == "list-collections": verify_target_args() @@ -3601,6 +3779,12 @@ def verify_replay_common_args(): lambda unused: True, "Unable to set pattern") + if coreclr_args.mode == "replay" or coreclr_args.mode == "asmdiffs" or coreclr_args.mode == "download": + if hasattr(coreclr_args, "private_store") and coreclr_args.private_store is not None: + logging.info("Using private stores:") + for path in coreclr_args.private_store: + logging.info(" %s", path) + return coreclr_args ################################################################################ @@ -3653,8 +3837,8 @@ def main(args): elif coreclr_args.mode == "replay": # Start a new SuperPMI Replay - process_mch_files_arg(coreclr_args) - mch_files = get_mch_files_for_replay(coreclr_args) + local_mch_paths = process_mch_files_arg(coreclr_args) + mch_files = get_mch_files_for_replay(local_mch_paths, coreclr_args.filter) if mch_files is None: return 1 @@ -3684,8 +3868,8 @@ def main(args): elif coreclr_args.mode == "asmdiffs": # Start a new SuperPMI Replay with AsmDiffs - process_mch_files_arg(coreclr_args) - mch_files = get_mch_files_for_replay(coreclr_args) + local_mch_paths = process_mch_files_arg(coreclr_args) + mch_files = get_mch_files_for_replay(local_mch_paths, coreclr_args.filter) if mch_files is None: return 1 @@ -3730,6 +3914,22 @@ def main(args): logging.debug("Finish time: %s", end_time.strftime("%H:%M:%S")) logging.debug("Elapsed time: %s", elapsed_time) + elif coreclr_args.mode == "upload-private": + + begin_time = datetime.datetime.now() + + logging.info("SuperPMI upload-private") + logging.debug("------------------------------------------------------------") + logging.debug("Start time: %s", begin_time.strftime("%H:%M:%S")) + + upload_private_mch(coreclr_args) + + end_time = datetime.datetime.now() + elapsed_time = end_time - begin_time + + logging.debug("Finish time: %s", end_time.strftime("%H:%M:%S")) + logging.debug("Elapsed time: %s", elapsed_time) + elif coreclr_args.mode == "download": begin_time = datetime.datetime.now() From 4581290e1b608e08ff628cceb6de2f3365a9912d Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 31 Mar 2021 10:50:25 -0700 Subject: [PATCH 77/98] Refactor missing reference errors to allow not throwing for all cases (#50437) - Build the concept of a cacheable resolution failure - Plumb it through the Ecma type loader and the required public api surfaces - Use it within the Mibc parser to avoid throwing --- .../tools/Common/Compiler/TypeExtensions.cs | 2 +- .../TypeSystem/Common/ExceptionStringID.cs | 1 + .../Common/MetadataTypeSystemContext.cs | 2 +- .../Common/TypeSystem/Common/ModuleDesc.cs | 8 +- .../TypeSystem/Common/NotFoundBehavior.cs | 12 ++ .../Common/Properties/Resources.resx | 3 + .../TypeSystem/Common/ResolutionFailure.cs | 110 +++++++++++++++ .../TypeSystem/Common/ThrowHelper.Common.cs | 5 + .../Common/TypeSystem/Common/ThrowHelper.cs | 6 + .../TypeSystem/Common/TypeSystemException.cs | 6 + .../CustomAttributeTypeNameParser.cs | 2 +- .../tools/Common/TypeSystem/Ecma/EcmaField.cs | 4 +- .../Common/TypeSystem/Ecma/EcmaMethod.cs | 4 +- .../Common/TypeSystem/Ecma/EcmaModule.cs | 132 +++++++++++++----- .../TypeSystem/Ecma/EcmaSignatureParser.cs | 120 +++++++++++++--- .../Common/TypeSystem/IL/EcmaMethodIL.cs | 6 +- .../Common/TypeSystem/IL/HelperExtensions.cs | 4 +- .../TypeSystem/IL/InstantiatedMethodIL.cs | 4 +- .../tools/Common/TypeSystem/IL/MethodIL.cs | 2 +- .../Common/TypeSystem/IL/Stubs/ILEmitter.cs | 2 +- .../ILVerification/ILVerification.projitems | 6 + ...impleArrayOfTRuntimeInterfacesAlgorithm.cs | 2 +- .../IBC/IBCProfileParser.cs | 8 +- .../IBC/MIbcProfileParser.cs | 22 ++- .../ILCompiler.TypeSystem.ReadyToRun.csproj | 6 + .../dotnet-pgo/R2RSignatureTypeProvider.cs | 18 ++- .../TypeRefTypeSystemContext.cs | 8 +- .../TypeRefTypeSystemModule.cs | 11 +- 28 files changed, 426 insertions(+), 90 deletions(-) create mode 100644 src/coreclr/tools/Common/TypeSystem/Common/NotFoundBehavior.cs create mode 100644 src/coreclr/tools/Common/TypeSystem/Common/ResolutionFailure.cs diff --git a/src/coreclr/tools/Common/Compiler/TypeExtensions.cs b/src/coreclr/tools/Common/Compiler/TypeExtensions.cs index e75f982abcdc..c9418961afd9 100644 --- a/src/coreclr/tools/Common/Compiler/TypeExtensions.cs +++ b/src/coreclr/tools/Common/Compiler/TypeExtensions.cs @@ -32,7 +32,7 @@ public static DefType GetClosestDefType(this TypeDesc type) { if (!type.IsArrayTypeWithoutGenericInterfaces()) { - MetadataType arrayShadowType = type.Context.SystemModule.GetType("System", "Array`1", throwIfNotFound: false); + MetadataType arrayShadowType = type.Context.SystemModule.GetType("System", "Array`1", NotFoundBehavior.ReturnNull); if (arrayShadowType != null) { return arrayShadowType.MakeInstantiatedType(((ArrayType)type).ElementType); diff --git a/src/coreclr/tools/Common/TypeSystem/Common/ExceptionStringID.cs b/src/coreclr/tools/Common/TypeSystem/Common/ExceptionStringID.cs index d9d3de41634d..dd38a715c5fe 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/ExceptionStringID.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/ExceptionStringID.cs @@ -40,5 +40,6 @@ public enum ExceptionStringID // BadImageFormatException BadImageFormatGeneric, + BadImageFormatSpecific, } } diff --git a/src/coreclr/tools/Common/TypeSystem/Common/MetadataTypeSystemContext.cs b/src/coreclr/tools/Common/TypeSystem/Common/MetadataTypeSystemContext.cs index b578eb6ca6ab..d8ed92df961e 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/MetadataTypeSystemContext.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/MetadataTypeSystemContext.cs @@ -73,7 +73,7 @@ public virtual void SetSystemModule(ModuleDesc systemModule) { // Require System.Object to be present as a minimal sanity check. // The set of required well-known types is not strictly defined since different .NET profiles implement different subsets. - MetadataType type = systemModule.GetType("System", s_wellKnownTypeNames[typeIndex], typeIndex == (int)WellKnownType.Object); + MetadataType type = systemModule.GetType("System", s_wellKnownTypeNames[typeIndex], typeIndex == (int)WellKnownType.Object ? NotFoundBehavior.Throw : NotFoundBehavior.ReturnNull); if (type != null) { type.SetWellKnownType((WellKnownType)(typeIndex + 1)); diff --git a/src/coreclr/tools/Common/TypeSystem/Common/ModuleDesc.cs b/src/coreclr/tools/Common/TypeSystem/Common/ModuleDesc.cs index c1e143dfe192..f2d45c1550b2 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/ModuleDesc.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/ModuleDesc.cs @@ -1,6 +1,7 @@ // 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.Collections.Generic; namespace Internal.TypeSystem @@ -28,8 +29,13 @@ public ModuleDesc(TypeSystemContext context, IAssemblyDesc assembly) /// /// Gets a type in this module with the specified name. + /// If notFoundBehavior == NotFoundBehavior.ReturnResolutionFailure + /// then ModuleDesc.GetTypeResolutionFailure will be set to the failure, and the function will return null /// - public abstract MetadataType GetType(string nameSpace, string name, bool throwIfNotFound = true); + public abstract MetadataType GetType(string nameSpace, string name, NotFoundBehavior notFoundBehavior = NotFoundBehavior.Throw); + + [ThreadStatic] + public static ResolutionFailure GetTypeResolutionFailure; /// /// Gets the global <Module> type. diff --git a/src/coreclr/tools/Common/TypeSystem/Common/NotFoundBehavior.cs b/src/coreclr/tools/Common/TypeSystem/Common/NotFoundBehavior.cs new file mode 100644 index 000000000000..1403bb8e0863 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Common/NotFoundBehavior.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Internal.TypeSystem +{ + public enum NotFoundBehavior + { + Throw, + ReturnNull, + ReturnResolutionFailure + } +} \ No newline at end of file diff --git a/src/coreclr/tools/Common/TypeSystem/Common/Properties/Resources.resx b/src/coreclr/tools/Common/TypeSystem/Common/Properties/Resources.resx index b0efd67b4adc..489fa6d1e3d9 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/Properties/Resources.resx +++ b/src/coreclr/tools/Common/TypeSystem/Common/Properties/Resources.resx @@ -180,4 +180,7 @@ The format of a DLL or executable being loaded is invalid + + The format of a DLL or executable being loaded is invalid with {0} + diff --git a/src/coreclr/tools/Common/TypeSystem/Common/ResolutionFailure.cs b/src/coreclr/tools/Common/TypeSystem/Common/ResolutionFailure.cs new file mode 100644 index 000000000000..04d75a5e5ae9 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Common/ResolutionFailure.cs @@ -0,0 +1,110 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Internal.TypeSystem +{ + public sealed class ResolutionFailure + { + private enum FailureType + { + TypeLoadException1, + TypeLoadException2, + TypeLoadException3, + MissingMethodException1, + MissingFieldException1, + MissingAssemblyException1, + } + + private ResolutionFailure() { } + + private FailureType _failureType; + private string _namespace; + private string _name; + private string _moduleName; + private ModuleDesc _module; + private TypeDesc _owningType; + private MethodSignature _methodSignature; + + + public static ResolutionFailure GetTypeLoadResolutionFailure(string nestedTypeName, ModuleDesc module) + { + ResolutionFailure failure = new ResolutionFailure(); + failure._failureType = FailureType.TypeLoadException1; + failure._name = nestedTypeName; + failure._module = module; + return failure; + } + + public static ResolutionFailure GetTypeLoadResolutionFailure(string @namespace, string name, ModuleDesc module) + { + ResolutionFailure failure = new ResolutionFailure(); + failure._failureType = FailureType.TypeLoadException2; + failure._namespace = @namespace; + failure._name = name; + failure._module = module; + return failure; + } + + public static ResolutionFailure GetTypeLoadResolutionFailure(string @namespace, string name, string moduleName) + { + ResolutionFailure failure = new ResolutionFailure(); + failure._failureType = FailureType.TypeLoadException3; + failure._namespace = @namespace; + failure._name = name; + failure._moduleName = moduleName; + return failure; + } + + public static ResolutionFailure GetMissingMethodFailure(TypeDesc owningType, string methodName, MethodSignature signature) + { + ResolutionFailure failure = new ResolutionFailure(); + failure._failureType = FailureType.MissingMethodException1; + failure._methodSignature = signature; + failure._name = methodName; + failure._owningType = owningType; + return failure; + } + + public static ResolutionFailure GetMissingFieldFailure(TypeDesc owningType, string fieldName) + { + ResolutionFailure failure = new ResolutionFailure(); + failure._failureType = FailureType.MissingMethodException1; + failure._name = fieldName; + failure._owningType = owningType; + return failure; + } + + public static ResolutionFailure GetAssemblyResolutionFailure(string simpleName) + { + ResolutionFailure failure = new ResolutionFailure(); + failure._failureType = FailureType.MissingAssemblyException1; + failure._name = simpleName; + return failure; + } + + public void Throw() + { + switch(_failureType) + { + case FailureType.TypeLoadException1: + ThrowHelper.ThrowTypeLoadException(_name, _module); + break; + case FailureType.TypeLoadException2: + ThrowHelper.ThrowTypeLoadException(_namespace, _name, _module); + break; + case FailureType.TypeLoadException3: + ThrowHelper.ThrowTypeLoadException(_namespace, _name, _moduleName); + break; + case FailureType.MissingMethodException1: + ThrowHelper.ThrowMissingMethodException(_owningType, _name, _methodSignature); + break; + case FailureType.MissingFieldException1: + ThrowHelper.ThrowMissingFieldException(_owningType, _name); + break; + case FailureType.MissingAssemblyException1: + ThrowHelper.ThrowFileNotFoundException(ExceptionStringID.FileLoadErrorGeneric, _name); + break; + } + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/Common/ThrowHelper.Common.cs b/src/coreclr/tools/Common/TypeSystem/Common/ThrowHelper.Common.cs index bfe36b4cd829..cf644ad14d4c 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/ThrowHelper.Common.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/ThrowHelper.Common.cs @@ -22,6 +22,11 @@ public static void ThrowTypeLoadException(string @namespace, string name, Module ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, Format.Type(@namespace, name), Format.Module(module)); } + public static void ThrowTypeLoadException(string @namespace, string name, string moduleName) + { + ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, Format.Type(@namespace, name), moduleName); + } + [System.Diagnostics.DebuggerHidden] public static void ThrowTypeLoadException(TypeDesc type) { diff --git a/src/coreclr/tools/Common/TypeSystem/Common/ThrowHelper.cs b/src/coreclr/tools/Common/TypeSystem/Common/ThrowHelper.cs index 094ade10faca..90c02f6ad3aa 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/ThrowHelper.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/ThrowHelper.cs @@ -59,6 +59,12 @@ public static void ThrowBadImageFormatException() throw new TypeSystemException.BadImageFormatException(); } + [System.Diagnostics.DebuggerHidden] + public static void ThrowBadImageFormatException(string message) + { + throw new TypeSystemException.BadImageFormatException(message); + } + private static partial class Format { public static string OwningModule(TypeDesc type) diff --git a/src/coreclr/tools/Common/TypeSystem/Common/TypeSystemException.cs b/src/coreclr/tools/Common/TypeSystem/Common/TypeSystemException.cs index 17836ca309bd..598f0f602748 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/TypeSystemException.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/TypeSystemException.cs @@ -155,6 +155,12 @@ internal BadImageFormatException() : base(ExceptionStringID.BadImageFormatGeneric) { } + + internal BadImageFormatException(string reason) + : base(ExceptionStringID.BadImageFormatSpecific, reason) + { + + } } } } diff --git a/src/coreclr/tools/Common/TypeSystem/Common/Utilities/CustomAttributeTypeNameParser.cs b/src/coreclr/tools/Common/TypeSystem/Common/Utilities/CustomAttributeTypeNameParser.cs index 1b901f5e623e..d63e92da5476 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/Utilities/CustomAttributeTypeNameParser.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/Utilities/CustomAttributeTypeNameParser.cs @@ -276,7 +276,7 @@ private static MetadataType GetType(this ModuleDesc module, string fullName, boo namespaceName = fullName.Substring(0, split); typeName = fullName.Substring(split + 1); } - return module.GetType(namespaceName, typeName, throwIfNotFound); + return module.GetType(namespaceName, typeName, throwIfNotFound ? NotFoundBehavior.Throw : NotFoundBehavior.ReturnNull); } private static AssemblyName FindAssemblyIfNamePresent(string name) diff --git a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaField.cs b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaField.cs index 31a4c8def5c5..b6d5e4fd41f8 100644 --- a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaField.cs +++ b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaField.cs @@ -98,7 +98,7 @@ private TypeDesc InitializeFieldType() var metadataReader = MetadataReader; BlobReader signatureReader = metadataReader.GetBlobReader(metadataReader.GetFieldDefinition(_handle).Signature); - EcmaSignatureParser parser = new EcmaSignatureParser(Module, signatureReader); + EcmaSignatureParser parser = new EcmaSignatureParser(Module, signatureReader, NotFoundBehavior.Throw); var fieldType = parser.ParseFieldSignature(); return (_fieldType = fieldType); } @@ -264,7 +264,7 @@ public override MarshalAsDescriptor GetMarshalAsDescriptor() if ((definition.Attributes & FieldAttributes.HasFieldMarshal) != 0) { BlobReader marshalAsReader = reader.GetBlobReader(definition.GetMarshallingDescriptor()); - EcmaSignatureParser parser = new EcmaSignatureParser(_type.EcmaModule, marshalAsReader); + EcmaSignatureParser parser = new EcmaSignatureParser(_type.EcmaModule, marshalAsReader, NotFoundBehavior.Throw); return parser.ParseMarshalAsDescriptor(); } diff --git a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaMethod.cs b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaMethod.cs index da5126529e48..182a87c56765 100644 --- a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaMethod.cs +++ b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaMethod.cs @@ -82,7 +82,7 @@ private MethodSignature InitializeSignature() var metadataReader = MetadataReader; BlobReader signatureReader = metadataReader.GetBlobReader(metadataReader.GetMethodDefinition(_handle).Signature); - EcmaSignatureParser parser = new EcmaSignatureParser(Module, signatureReader); + EcmaSignatureParser parser = new EcmaSignatureParser(Module, signatureReader, NotFoundBehavior.Throw); var signature = parser.ParseMethodSignature(); return (_signature = signature); } @@ -573,7 +573,7 @@ private MarshalAsDescriptor GetMarshalAsDescriptor(Parameter parameter) { MetadataReader metadataReader = MetadataReader; BlobReader marshalAsReader = metadataReader.GetBlobReader(parameter.GetMarshallingDescriptor()); - EcmaSignatureParser parser = new EcmaSignatureParser(Module, marshalAsReader); + EcmaSignatureParser parser = new EcmaSignatureParser(Module, marshalAsReader, NotFoundBehavior.Throw); MarshalAsDescriptor marshalAs = parser.ParseMarshalAsDescriptor(); Debug.Assert(marshalAs != null); return marshalAs; diff --git a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs index 389f0607399b..737b266b9b6e 100644 --- a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs +++ b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs @@ -96,7 +96,7 @@ protected override IEntityHandleObject CreateValueFromKey(EntityHandle handle) { MethodDefinitionHandle methodDefinitionHandle = (MethodDefinitionHandle)handle; TypeDefinitionHandle typeDefinitionHandle = _module._metadataReader.GetMethodDefinition(methodDefinitionHandle).GetDeclaringType(); - EcmaType type = (EcmaType)_module.GetObject(typeDefinitionHandle); + EcmaType type = (EcmaType)_module.GetObject(typeDefinitionHandle, NotFoundBehavior.Throw); item = new EcmaMethod(type, methodDefinitionHandle); } break; @@ -105,7 +105,7 @@ protected override IEntityHandleObject CreateValueFromKey(EntityHandle handle) { FieldDefinitionHandle fieldDefinitionHandle = (FieldDefinitionHandle)handle; TypeDefinitionHandle typeDefinitionHandle = _module._metadataReader.GetFieldDefinition(fieldDefinitionHandle).GetDeclaringType(); - EcmaType type = (EcmaType)_module.GetObject(typeDefinitionHandle); + EcmaType type = (EcmaType)_module.GetObject(typeDefinitionHandle, NotFoundBehavior.Throw); item = new EcmaField(type, fieldDefinitionHandle); } break; @@ -149,7 +149,9 @@ protected override IEntityHandleObject CreateValueFromKey(EntityHandle handle) break; default: - throw new BadImageFormatException("Unknown metadata token type: " + handle.Kind); + ThrowHelper.ThrowBadImageFormatException("unknown metadata token type: " + handle.Kind); + item = null; + break; } switch (handle.Kind) @@ -261,7 +263,8 @@ public MethodDesc EntryPoint } // Bad metadata - throw new BadImageFormatException(); + ThrowHelper.ThrowBadImageFormatException(); + return null; } } @@ -277,7 +280,7 @@ public bool IsPlatformNeutral } } - public sealed override MetadataType GetType(string nameSpace, string name, bool throwIfNotFound = true) + public sealed override MetadataType GetType(string nameSpace, string name, NotFoundBehavior notFoundBehavior) { var stringComparer = _metadataReader.StringComparer; @@ -300,12 +303,20 @@ public sealed override MetadataType GetType(string nameSpace, string name, bool { if (exportedType.IsForwarder) { - Object implementation = GetObject(exportedType.Implementation); + Object implementation = GetObject(exportedType.Implementation, notFoundBehavior); + + if (implementation == null) + return null; if (implementation is ModuleDesc) { return ((ModuleDesc)(implementation)).GetType(nameSpace, name); } + else if (implementation is ResolutionFailure failure) + { + ModuleDesc.GetTypeResolutionFailure = failure; + return null; + } // TODO throw new NotImplementedException(); @@ -315,42 +326,57 @@ public sealed override MetadataType GetType(string nameSpace, string name, bool } } - if (throwIfNotFound) - ThrowHelper.ThrowTypeLoadException(nameSpace, name, this); + if (notFoundBehavior != NotFoundBehavior.ReturnNull) + { + var failure = ResolutionFailure.GetTypeLoadResolutionFailure(nameSpace, name, this); + if (notFoundBehavior == NotFoundBehavior.Throw) + failure.Throw(); + + ModuleDesc.GetTypeResolutionFailure = failure; + return null; + } return null; } public TypeDesc GetType(EntityHandle handle) { - TypeDesc type = GetObject(handle) as TypeDesc; + TypeDesc type = GetObject(handle, NotFoundBehavior.Throw) as TypeDesc; if (type == null) - throw new BadImageFormatException("Type expected"); + ThrowHelper.ThrowBadImageFormatException($"type expected for handle {handle.ToString()}"); return type; } public MethodDesc GetMethod(EntityHandle handle) { - MethodDesc method = GetObject(handle) as MethodDesc; + MethodDesc method = GetObject(handle, NotFoundBehavior.Throw) as MethodDesc; if (method == null) - throw new BadImageFormatException("Method expected"); + ThrowHelper.ThrowBadImageFormatException($"method expected for handle {handle.ToString()}"); return method; } public FieldDesc GetField(EntityHandle handle) { - FieldDesc field = GetObject(handle) as FieldDesc; + FieldDesc field = GetObject(handle, NotFoundBehavior.Throw) as FieldDesc; if (field == null) - throw new BadImageFormatException("Field expected"); + ThrowHelper.ThrowBadImageFormatException($"field expected for handle {handle.ToString()}"); return field; } - public Object GetObject(EntityHandle handle) + public Object GetObject(EntityHandle handle, NotFoundBehavior notFoundBehavior = NotFoundBehavior.Throw) { IEntityHandleObject obj = _resolvedTokens.GetOrCreateValue(handle); if (obj is EcmaObjectLookupWrapper) { - return ((EcmaObjectLookupWrapper)obj).Object; + object result = ((EcmaObjectLookupWrapper)obj).Object; + if ((result is ResolutionFailure failure) && (notFoundBehavior != NotFoundBehavior.ReturnResolutionFailure)) + { + if (notFoundBehavior == NotFoundBehavior.ReturnNull) + return null; + else + failure.Throw(); + } + return result; } else { @@ -362,12 +388,22 @@ private Object ResolveMethodSpecification(MethodSpecificationHandle handle) { MethodSpecification methodSpecification = _metadataReader.GetMethodSpecification(handle); - MethodDesc methodDef = GetMethod(methodSpecification.Method); + object resolvedMethod = GetObject(methodSpecification.Method, NotFoundBehavior.ReturnResolutionFailure); + if (resolvedMethod is ResolutionFailure) + return resolvedMethod; + + MethodDesc methodDef = resolvedMethod as MethodDesc; + if (methodDef == null) + ThrowHelper.ThrowBadImageFormatException($"method expected for handle {handle.ToString()}"); BlobReader signatureReader = _metadataReader.GetBlobReader(methodSpecification.Signature); - EcmaSignatureParser parser = new EcmaSignatureParser(this, signatureReader); + EcmaSignatureParser parser = new EcmaSignatureParser(this, signatureReader, NotFoundBehavior.ReturnResolutionFailure); TypeDesc[] instantiation = parser.ParseMethodSpecSignature(); + + if (instantiation == null) + return parser.ResolutionFailure; + return Context.GetInstantiatedMethod(methodDef, new Instantiation(instantiation)); } @@ -375,9 +411,11 @@ private Object ResolveStandaloneSignature(StandaloneSignatureHandle handle) { StandaloneSignature signature = _metadataReader.GetStandaloneSignature(handle); BlobReader signatureReader = _metadataReader.GetBlobReader(signature.Signature); - EcmaSignatureParser parser = new EcmaSignatureParser(this, signatureReader); + EcmaSignatureParser parser = new EcmaSignatureParser(this, signatureReader, NotFoundBehavior.ReturnResolutionFailure); MethodSignature methodSig = parser.ParseMethodSignature(); + if (methodSig == null) + return parser.ResolutionFailure; return methodSig; } @@ -386,23 +424,30 @@ private Object ResolveTypeSpecification(TypeSpecificationHandle handle) TypeSpecification typeSpecification = _metadataReader.GetTypeSpecification(handle); BlobReader signatureReader = _metadataReader.GetBlobReader(typeSpecification.Signature); - EcmaSignatureParser parser = new EcmaSignatureParser(this, signatureReader); + EcmaSignatureParser parser = new EcmaSignatureParser(this, signatureReader, NotFoundBehavior.ReturnResolutionFailure); - return parser.ParseType(); + TypeDesc parsedType = parser.ParseType(); + if (parsedType == null) + return parser.ResolutionFailure; + else + return parsedType; } private Object ResolveMemberReference(MemberReferenceHandle handle) { MemberReference memberReference = _metadataReader.GetMemberReference(handle); - Object parent = GetObject(memberReference.Parent); + Object parent = GetObject(memberReference.Parent, NotFoundBehavior.ReturnResolutionFailure); + + if (parent is ResolutionFailure) + return parent; TypeDesc parentTypeDesc = parent as TypeDesc; if (parentTypeDesc != null) { BlobReader signatureReader = _metadataReader.GetBlobReader(memberReference.Signature); - EcmaSignatureParser parser = new EcmaSignatureParser(this, signatureReader); + EcmaSignatureParser parser = new EcmaSignatureParser(this, signatureReader, NotFoundBehavior.ReturnResolutionFailure); string name = _metadataReader.GetString(memberReference.Name); @@ -412,11 +457,13 @@ private Object ResolveMemberReference(MemberReferenceHandle handle) if (field != null) return field; - ThrowHelper.ThrowMissingFieldException(parentTypeDesc, name); + return ResolutionFailure.GetMissingFieldFailure(parentTypeDesc, name); } else { MethodSignature sig = parser.ParseMethodSignature(); + if (sig == null) + return parser.ResolutionFailure; TypeDesc typeDescToInspect = parentTypeDesc; Instantiation substitution = default(Instantiation); @@ -460,7 +507,7 @@ private Object ResolveMemberReference(MemberReferenceHandle handle) typeDescToInspect = baseType; } while (typeDescToInspect != null); - ThrowHelper.ThrowMissingMethodException(parentTypeDesc, name, sig); + return ResolutionFailure.GetMissingMethodFailure(parentTypeDesc, name, sig); } } else if (parent is MethodDesc) @@ -472,18 +519,26 @@ private Object ResolveMemberReference(MemberReferenceHandle handle) throw new NotImplementedException("MemberRef to a global function or variable."); } - throw new BadImageFormatException(); + ThrowHelper.ThrowBadImageFormatException(); + return null; } private Object ResolveTypeReference(TypeReferenceHandle handle) { TypeReference typeReference = _metadataReader.GetTypeReference(handle); - Object resolutionScope = GetObject(typeReference.ResolutionScope); + Object resolutionScope = GetObject(typeReference.ResolutionScope, NotFoundBehavior.ReturnResolutionFailure); + if (resolutionScope is ResolutionFailure) + { + return resolutionScope; + } if (resolutionScope is ModuleDesc) { - return ((ModuleDesc)(resolutionScope)).GetType(_metadataReader.GetString(typeReference.Namespace), _metadataReader.GetString(typeReference.Name)); + object result = ((ModuleDesc)(resolutionScope)).GetType(_metadataReader.GetString(typeReference.Namespace), _metadataReader.GetString(typeReference.Name), NotFoundBehavior.ReturnResolutionFailure); + if (result == null) + result = ModuleDesc.GetTypeResolutionFailure; + return result; } else if (resolutionScope is MetadataType) @@ -495,7 +550,7 @@ private Object ResolveTypeReference(TypeReferenceHandle handle) if (result != null) return result; - ThrowHelper.ThrowTypeLoadException(typeName, ((MetadataType)resolutionScope).Module); + return ResolutionFailure.GetTypeLoadResolutionFailure(typeName, ((MetadataType)resolutionScope).Module); } // TODO @@ -523,20 +578,24 @@ private Object ResolveAssemblyReference(AssemblyReferenceHandle handle) an.CultureName = _metadataReader.GetString(assemblyReference.Culture); an.ContentType = GetContentTypeFromAssemblyFlags(assemblyReference.Flags); - return _moduleResolver.ResolveAssembly(an); + var assembly = _moduleResolver.ResolveAssembly(an, throwIfNotFound: false); + if (assembly == null) + return ResolutionFailure.GetAssemblyResolutionFailure(an.Name); + else + return assembly; } private Object ResolveExportedType(ExportedTypeHandle handle) { ExportedType exportedType = _metadataReader.GetExportedType(handle); - var implementation = GetObject(exportedType.Implementation); + var implementation = GetObject(exportedType.Implementation, NotFoundBehavior.ReturnResolutionFailure); if (implementation is ModuleDesc) { var module = (ModuleDesc)implementation; string nameSpace = _metadataReader.GetString(exportedType.Namespace); string name = _metadataReader.GetString(exportedType.Name); - return module.GetType(nameSpace, name); + return module.GetType(nameSpace, name, NotFoundBehavior.ReturnResolutionFailure); } else if (implementation is MetadataType) @@ -545,12 +604,17 @@ private Object ResolveExportedType(ExportedTypeHandle handle) string name = _metadataReader.GetString(exportedType.Name); var nestedType = type.GetNestedType(name); if (nestedType == null) - ThrowHelper.ThrowTypeLoadException(name, this); + return ResolutionFailure.GetTypeLoadResolutionFailure(name, this); return nestedType; } + else if (implementation is ResolutionFailure) + { + return implementation; + } else { - throw new BadImageFormatException("Unknown metadata token type for exported type"); + ThrowHelper.ThrowBadImageFormatException(); + return null; } } diff --git a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaSignatureParser.cs b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaSignatureParser.cs index 9a39fc67c126..cb3766f98bc6 100644 --- a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaSignatureParser.cs +++ b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaSignatureParser.cs @@ -14,40 +14,75 @@ namespace Internal.TypeSystem.Ecma public struct EcmaSignatureParser { private TypeSystemContext _tsc; - private Func _typeResolver; + private Func _typeResolver; + private NotFoundBehavior _notFoundBehavior; private EcmaModule _ecmaModule; private BlobReader _reader; + private ResolutionFailure _resolutionFailure; private Stack _indexStack; private List _embeddedSignatureDataList; - public EcmaSignatureParser(TypeSystemContext tsc, Func typeResolver, BlobReader reader) + public EcmaSignatureParser(TypeSystemContext tsc, Func typeResolver, BlobReader reader, NotFoundBehavior notFoundBehavior) { + _notFoundBehavior = notFoundBehavior; _ecmaModule = null; _tsc = tsc; _typeResolver = typeResolver; _reader = reader; _indexStack = null; _embeddedSignatureDataList = null; + _resolutionFailure = null; } - public EcmaSignatureParser(EcmaModule ecmaModule, BlobReader reader) + public EcmaSignatureParser(EcmaModule ecmaModule, BlobReader reader, NotFoundBehavior notFoundBehavior) { + _notFoundBehavior = notFoundBehavior; _ecmaModule = ecmaModule; _tsc = ecmaModule.Context; _typeResolver = null; _reader = reader; _indexStack = null; _embeddedSignatureDataList = null; + _resolutionFailure = null; } + void SetResolutionFailure(ResolutionFailure failure) + { + if (_resolutionFailure == null) + _resolutionFailure = failure; + } + + public ResolutionFailure ResolutionFailure => _resolutionFailure; + private TypeDesc ResolveHandle(EntityHandle handle) { + object resolvedValue; if (_ecmaModule != null) - return _ecmaModule.GetType(handle); + { + resolvedValue = _ecmaModule.GetObject(handle, _notFoundBehavior); + } else - return _typeResolver(handle); + { + resolvedValue = _typeResolver(handle, _notFoundBehavior); + } + + if (resolvedValue == null) + return null; + if (resolvedValue is ResolutionFailure failure) + { + SetResolutionFailure(failure); + return null; + } + if (resolvedValue is TypeDesc type) + { + return type; + } + else + { + throw new BadImageFormatException("Type expected"); + } } private TypeDesc GetWellKnownType(WellKnownType wellKnownType) @@ -57,7 +92,6 @@ private TypeDesc GetWellKnownType(WellKnownType wellKnownType) private TypeDesc ParseType(SignatureTypeCode typeCode) { - if (_indexStack != null) { int was = _indexStack.Pop(); @@ -114,7 +148,12 @@ private TypeDesc ParseTypeImpl(SignatureTypeCode typeCode) case SignatureTypeCode.TypeHandle: return ResolveHandle(_reader.ReadTypeHandle()); case SignatureTypeCode.SZArray: - return _tsc.GetArrayType(ParseType()); + { + var elementType = ParseType(); + if (elementType == null) + return null; + return _tsc.GetArrayType(elementType); + } case SignatureTypeCode.Array: { var elementType = ParseType(); @@ -128,12 +167,27 @@ private TypeDesc ParseTypeImpl(SignatureTypeCode typeCode) for (int j = 0; j < lowerBoundsCount; j++) _reader.ReadCompressedInteger(); - return _tsc.GetArrayType(elementType, rank); + if (elementType != null) + return _tsc.GetArrayType(elementType, rank); + else + return null; } case SignatureTypeCode.ByReference: - return ParseType().MakeByRefType(); + { + TypeDesc byRefedType = ParseType(); + if (byRefedType != null) + return byRefedType.MakeByRefType(); + else + return null; + } case SignatureTypeCode.Pointer: - return _tsc.GetPointerType(ParseType()); + { + TypeDesc pointedAtType = ParseType(); + if (pointedAtType != null) + return _tsc.GetPointerType(pointedAtType); + else + return null; + } case SignatureTypeCode.GenericTypeParameter: return _tsc.GetSignatureVariable(_reader.ReadCompressedInteger(), false); case SignatureTypeCode.GenericMethodParameter: @@ -141,19 +195,36 @@ private TypeDesc ParseTypeImpl(SignatureTypeCode typeCode) case SignatureTypeCode.GenericTypeInstance: { TypeDesc typeDef = ParseType(); - MetadataType metadataTypeDef = typeDef as MetadataType; - if (metadataTypeDef == null) - throw new BadImageFormatException(); + MetadataType metadataTypeDef = null; + + if (typeDef != null) + { + metadataTypeDef = typeDef as MetadataType; + if (metadataTypeDef == null) + throw new BadImageFormatException(); + } TypeDesc[] instance = new TypeDesc[_reader.ReadCompressedInteger()]; for (int i = 0; i < instance.Length; i++) + { instance[i] = ParseType(); - return _tsc.GetInstantiatedType(metadataTypeDef, new Instantiation(instance)); + if (instance[i] == null) + metadataTypeDef = null; + } + + if (metadataTypeDef != null) + return _tsc.GetInstantiatedType(metadataTypeDef, new Instantiation(instance)); + else + return null; } case SignatureTypeCode.TypedReference: return GetWellKnownType(WellKnownType.TypedReference); case SignatureTypeCode.FunctionPointer: - return _tsc.GetFunctionPointerType(ParseMethodSignatureInternal(skipEmbeddedSignatureData: true)); + MethodSignature sig = ParseMethodSignatureInternal(skipEmbeddedSignatureData: true); + if (sig != null) + return _tsc.GetFunctionPointerType(sig); + else + return null; default: throw new BadImageFormatException(); } @@ -320,12 +391,19 @@ private MethodSignature ParseMethodSignatureImpl(bool skipEmbeddedSignatureData) EmbeddedSignatureData[] embeddedSignatureDataArray = (_embeddedSignatureDataList == null || _embeddedSignatureDataList.Count == 0 || skipEmbeddedSignatureData) ? null : _embeddedSignatureDataList.ToArray(); - return new MethodSignature(flags, arity, returnType, parameters, embeddedSignatureDataArray); + if (_resolutionFailure == null) + return new MethodSignature(flags, arity, returnType, parameters, embeddedSignatureDataArray); + else + return null; } public PropertySignature ParsePropertySignature() { + // As PropertySignature is a struct, we cannot return null + if (_notFoundBehavior != NotFoundBehavior.Throw) + throw new ArgumentException(); + SignatureHeader header = _reader.ReadSignatureHeader(); if (header.Kind != SignatureKind.Property) throw new BadImageFormatException(); @@ -392,7 +470,10 @@ public LocalVariableDefinition[] ParseLocalsSignature() { locals = Array.Empty(); } - return locals; + if (_resolutionFailure == null) + return locals; + else + return null; } public TypeDesc[] ParseMethodSpecSignature() @@ -410,7 +491,10 @@ public TypeDesc[] ParseMethodSpecSignature() { arguments[i] = ParseType(); } - return arguments; + if (_resolutionFailure == null) + return arguments; + else + return null; } public MarshalAsDescriptor ParseMarshalAsDescriptor() diff --git a/src/coreclr/tools/Common/TypeSystem/IL/EcmaMethodIL.cs b/src/coreclr/tools/Common/TypeSystem/IL/EcmaMethodIL.cs index e0142800be95..02e5207c90a8 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/EcmaMethodIL.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/EcmaMethodIL.cs @@ -89,7 +89,7 @@ public override LocalVariableDefinition[] GetLocals() return Array.Empty(); BlobReader signatureReader = metadataReader.GetBlobReader(metadataReader.GetStandaloneSignature(localSignature).Signature); - EcmaSignatureParser parser = new EcmaSignatureParser(_module, signatureReader); + EcmaSignatureParser parser = new EcmaSignatureParser(_module, signatureReader, NotFoundBehavior.Throw); LocalVariableDefinition[] locals = parser.ParseLocalsSignature(); Interlocked.CompareExchange(ref _locals, locals, null); @@ -131,13 +131,13 @@ public override ILExceptionRegion[] GetExceptionRegions() return _ilExceptionRegions; } - public override object GetObject(int token) + public override object GetObject(int token, NotFoundBehavior notFoundBehavior = NotFoundBehavior.Throw) { // UserStrings cannot be wrapped in EntityHandle if ((token & 0xFF000000) == 0x70000000) return _module.GetUserString(MetadataTokens.UserStringHandle(token)); - return _module.GetObject(MetadataTokens.EntityHandle(token)); + return _module.GetObject(MetadataTokens.EntityHandle(token), notFoundBehavior); } } } diff --git a/src/coreclr/tools/Common/TypeSystem/IL/HelperExtensions.cs b/src/coreclr/tools/Common/TypeSystem/IL/HelperExtensions.cs index 0723d40b363f..3c6d4c1d0b22 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/HelperExtensions.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/HelperExtensions.cs @@ -22,7 +22,7 @@ public static MetadataType GetHelperType(this TypeSystemContext context, string public static MetadataType GetOptionalHelperType(this TypeSystemContext context, string name) { - MetadataType helperType = context.SystemModule.GetType(HelperTypesNamespace, name, throwIfNotFound: false); + MetadataType helperType = context.SystemModule.GetType(HelperTypesNamespace, name, NotFoundBehavior.ReturnNull); return helperType; } @@ -111,7 +111,7 @@ public static MetadataType GetKnownNestedType(this MetadataType type, string nam /// public static MetadataType GetKnownType(this ModuleDesc module, string @namespace, string name) { - MetadataType type = module.GetType(@namespace, name, false); + MetadataType type = module.GetType(@namespace, name, NotFoundBehavior.ReturnNull); if (type == null) { throw new InvalidOperationException( diff --git a/src/coreclr/tools/Common/TypeSystem/IL/InstantiatedMethodIL.cs b/src/coreclr/tools/Common/TypeSystem/IL/InstantiatedMethodIL.cs index 7192e547f39a..9def3ed499b8 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/InstantiatedMethodIL.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/InstantiatedMethodIL.cs @@ -89,9 +89,9 @@ public override LocalVariableDefinition[] GetLocals() return (clone == null) ? locals : clone; } - public override Object GetObject(int token) + public override Object GetObject(int token, NotFoundBehavior notFoundBehavior) { - Object o = _methodIL.GetObject(token); + Object o = _methodIL.GetObject(token, notFoundBehavior); if (o is MethodDesc) { diff --git a/src/coreclr/tools/Common/TypeSystem/IL/MethodIL.cs b/src/coreclr/tools/Common/TypeSystem/IL/MethodIL.cs index 83fecc15119b..b5eb21fcba42 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/MethodIL.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/MethodIL.cs @@ -86,7 +86,7 @@ public abstract partial class MethodIL /// (typically a , , , /// or ). /// - public abstract Object GetObject(int token); + public abstract Object GetObject(int token, NotFoundBehavior notFoundBehavior = NotFoundBehavior.ReturnNull); /// /// Gets a list of exception regions this method body defines. diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ILEmitter.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ILEmitter.cs index 67f79ad809dc..aa203289c3c6 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ILEmitter.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ILEmitter.cs @@ -620,7 +620,7 @@ public override LocalVariableDefinition[] GetLocals() { return _locals; } - public override Object GetObject(int token) + public override Object GetObject(int token, NotFoundBehavior notFoundBehavior) { return _tokens[(token & 0xFFFFFF) - 1]; } diff --git a/src/coreclr/tools/ILVerification/ILVerification.projitems b/src/coreclr/tools/ILVerification/ILVerification.projitems index c68e48bdc552..35b992f6f023 100644 --- a/src/coreclr/tools/ILVerification/ILVerification.projitems +++ b/src/coreclr/tools/ILVerification/ILVerification.projitems @@ -46,6 +46,12 @@ TypeSystem\Common\ModuleDesc.cs + + TypeSystem\Common\NotFoundBehavior.cs + + + TypeSystem\Common\ResolutionFailure.cs + TypeSystem\Common\TypeSystemEntity.cs diff --git a/src/coreclr/tools/ILVerification/SimpleArrayOfTRuntimeInterfacesAlgorithm.cs b/src/coreclr/tools/ILVerification/SimpleArrayOfTRuntimeInterfacesAlgorithm.cs index 3a08440c4fa7..019f669a73a0 100644 --- a/src/coreclr/tools/ILVerification/SimpleArrayOfTRuntimeInterfacesAlgorithm.cs +++ b/src/coreclr/tools/ILVerification/SimpleArrayOfTRuntimeInterfacesAlgorithm.cs @@ -35,7 +35,7 @@ public SimpleArrayOfTRuntimeInterfacesAlgorithm(ModuleDesc systemModule) int count = 0; for (int i = 0; i < s_genericRuntimeInterfacesNames.Length; ++i) { - MetadataType runtimeInterface =_systemModule.GetType("System.Collections.Generic", s_genericRuntimeInterfacesNames[i], false); + MetadataType runtimeInterface =_systemModule.GetType("System.Collections.Generic", s_genericRuntimeInterfacesNames[i], NotFoundBehavior.ReturnNull); if (runtimeInterface != null) _genericRuntimeInterfaces[count++] = runtimeInterface; }; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IBC/IBCProfileParser.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IBC/IBCProfileParser.cs index b26d9ab6226f..8b419d4b3955 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IBC/IBCProfileParser.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IBC/IBCProfileParser.cs @@ -106,7 +106,7 @@ public ProfileData ParseIBCDataFromModule(EcmaModule ecmaModule) case CorTokenType.mdtMethodDef: case CorTokenType.mdtMemberRef: case CorTokenType.mdtMethodSpec: - object metadataObject = ecmaModule.GetObject(System.Reflection.Metadata.Ecma335.MetadataTokens.EntityHandle((int)entry.Token)); + object metadataObject = ecmaModule.GetObject(System.Reflection.Metadata.Ecma335.MetadataTokens.EntityHandle((int)entry.Token), NotFoundBehavior.ReturnNull); if (metadataObject is MethodDesc) { associatedMethod = (MethodDesc)metadataObject; @@ -346,7 +346,7 @@ private uint LookupIbcTypeToken(ref EcmaModule externalModule, uint ibcToken, Di if (!(m is EcmaModule)) continue; - foundType = (EcmaType)m.GetType(typeNamespace, typeName, throwIfNotFound: false); + foundType = (EcmaType)m.GetType(typeNamespace, typeName, NotFoundBehavior.ReturnNull); if (foundType != null) { externalModule = foundType.EcmaModule; @@ -356,7 +356,7 @@ private uint LookupIbcTypeToken(ref EcmaModule externalModule, uint ibcToken, Di } else { - foundType = (EcmaType)externalModule.GetType(typeNamespace, typeName, throwIfNotFound: false); + foundType = (EcmaType)externalModule.GetType(typeNamespace, typeName, NotFoundBehavior.ReturnNull); } if (foundType == null) @@ -451,7 +451,7 @@ public EcmaModule GetModuleFromIndex(int index) { if (EcmaModule.MetadataReader.GetTableRowCount(TableIndex.AssemblyRef) < index) return null; - return EcmaModule.GetObject(MetadataTokens.EntityHandle(((int)CorTokenType.mdtAssemblyRef) | index)) as EcmaModule; + return EcmaModule.GetObject(MetadataTokens.EntityHandle(((int)CorTokenType.mdtAssemblyRef) | index), NotFoundBehavior.ReturnNull) as EcmaModule; } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IBC/MIbcProfileParser.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IBC/MIbcProfileParser.cs index 987300df24b2..22e54a8922ad 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IBC/MIbcProfileParser.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IBC/MIbcProfileParser.cs @@ -41,7 +41,12 @@ TypeSystemEntityOrUnknown IPgoSchemaDataLoader.TypeFr // token type is 0, therefore it can't be a type return new TypeSystemEntityOrUnknown((int)token); } - return new TypeSystemEntityOrUnknown((TypeDesc)_ilBody.GetObject((int)token)); + TypeDesc foundType = _ilBody.GetObject((int)token, NotFoundBehavior.ReturnNull) as TypeDesc; + if (foundType == null) + { + return new TypeSystemEntityOrUnknown((int)token & 0x00FFFFFF); + } + return new TypeSystemEntityOrUnknown(foundType); } catch { @@ -265,7 +270,9 @@ static IEnumerable ReadMIbcGroup(TypeSystemContext tsc, EcmaM metadataObject = null; try { - metadataObject = ilBody.GetObject(token); + metadataObject = ilBody.GetObject(token, NotFoundBehavior.ReturnNull); + if (metadataObject == null) + metadataObject = metadataNotResolvable; } catch (TypeSystemException) { @@ -509,7 +516,7 @@ public override MetadataType GetGlobalModuleType() throw new NotImplementedException(); } - public override MetadataType GetType(string nameSpace, string name, bool throwIfNotFound = true) + public override MetadataType GetType(string nameSpace, string name, NotFoundBehavior notFoundBehavior) { TypeSystemContext context = Context; @@ -519,9 +526,14 @@ public override MetadataType GetType(string nameSpace, string name, bool throwIf return Context.UniversalCanonType; else { - if (throwIfNotFound) + if (notFoundBehavior != NotFoundBehavior.ReturnNull) { - throw new TypeLoadException($"{nameSpace}.{name}"); + var failure = ResolutionFailure.GetTypeLoadResolutionFailure(nameSpace, name, "System.Private.Canon"); + ModuleDesc.GetTypeResolutionFailure = failure; + if (notFoundBehavior == NotFoundBehavior.Throw) + failure.Throw(); + + return null; } return null; } diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun/ILCompiler.TypeSystem.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun/ILCompiler.TypeSystem.ReadyToRun.csproj index c8e83d94bf84..580438dcc29d 100644 --- a/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun/ILCompiler.TypeSystem.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun/ILCompiler.TypeSystem.ReadyToRun.csproj @@ -146,6 +146,12 @@ TypeSystem\Common\ModuleDesc.cs + + TypeSystem\Common\NotFoundBehavior.cs + + + TypeSystem\Common\ResolutionFailure.cs + TypeSystem\Common\TypeSystemEntity.cs diff --git a/src/coreclr/tools/dotnet-pgo/R2RSignatureTypeProvider.cs b/src/coreclr/tools/dotnet-pgo/R2RSignatureTypeProvider.cs index 0bc52929256b..a640425f209b 100644 --- a/src/coreclr/tools/dotnet-pgo/R2RSignatureTypeProvider.cs +++ b/src/coreclr/tools/dotnet-pgo/R2RSignatureTypeProvider.cs @@ -97,7 +97,11 @@ MethodDesc IR2RSignatureTypeProvider.GetMethodFromMemberRef(MetadataReader reader, MemberReferenceHandle handle, TypeDesc owningTypeOverride) { var ecmaModule = (EcmaModule)_tsc.GetModuleForSimpleName(reader.GetString(reader.GetAssemblyDefinition().Name)); - var method = (MethodDesc)ecmaModule.GetObject(handle); + var method = (MethodDesc)ecmaModule.GetObject(handle, NotFoundBehavior.ReturnNull); + if (method == null) + { + return null; + } if (owningTypeOverride != null) { return _tsc.GetMethodForInstantiatedType(method.GetTypicalMethodDefinition(), (InstantiatedType)owningTypeOverride); @@ -108,7 +112,11 @@ MethodDesc IR2RSignatureTypeProvider.GetMethodFromMethodDef(MetadataReader reader, MethodDefinitionHandle handle, TypeDesc owningTypeOverride) { var ecmaModule = (EcmaModule)_tsc.GetModuleForSimpleName(reader.GetString(reader.GetAssemblyDefinition().Name)); - var method = (MethodDesc)ecmaModule.GetObject(handle); + var method = (MethodDesc)ecmaModule.GetObject(handle, NotFoundBehavior.ReturnNull); + if (method == null) + { + return null; + } if (owningTypeOverride != null) { return _tsc.GetMethodForInstantiatedType(method.GetTypicalMethodDefinition(), (InstantiatedType)owningTypeOverride); @@ -214,19 +222,19 @@ TypeDesc ISZArrayTypeProvider.GetSZArrayType(TypeDesc elementType) TypeDesc ISimpleTypeProvider.GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) { var ecmaModule = (EcmaModule)_tsc.GetModuleForSimpleName(reader.GetString(reader.GetAssemblyDefinition().Name)); - return (TypeDesc)ecmaModule.GetObject(handle); + return (TypeDesc)ecmaModule.GetObject(handle, NotFoundBehavior.ReturnNull); } TypeDesc ISimpleTypeProvider.GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) { var ecmaModule = (EcmaModule)_tsc.GetModuleForSimpleName(reader.GetString(reader.GetAssemblyDefinition().Name)); - return (TypeDesc)ecmaModule.GetObject(handle); + return (TypeDesc)ecmaModule.GetObject(handle, NotFoundBehavior.ReturnNull); } TypeDesc ISignatureTypeProvider.GetTypeFromSpecification(MetadataReader reader, R2RSigProviderContext genericContext, TypeSpecificationHandle handle, byte rawTypeKind) { var ecmaModule = (EcmaModule)_tsc.GetModuleForSimpleName(reader.GetString(reader.GetAssemblyDefinition().Name)); - return (TypeDesc)ecmaModule.GetObject(handle); + return (TypeDesc)ecmaModule.GetObject(handle, NotFoundBehavior.ReturnNull); } } } diff --git a/src/coreclr/tools/dotnet-pgo/TypeRefTypeSystem/TypeRefTypeSystemContext.cs b/src/coreclr/tools/dotnet-pgo/TypeRefTypeSystem/TypeRefTypeSystemContext.cs index 1cb1138a4929..bb32521c0b75 100644 --- a/src/coreclr/tools/dotnet-pgo/TypeRefTypeSystem/TypeRefTypeSystemContext.cs +++ b/src/coreclr/tools/dotnet-pgo/TypeRefTypeSystem/TypeRefTypeSystemContext.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Reflection.Metadata; using System.Text; @@ -120,7 +121,7 @@ public TypeRefTypeSystemContext(IEnumerable refReaders) { TypeRefSignatureParserProvider parserHelper = new TypeRefSignatureParserProvider(this, peInfo.handleLookup); - Func resolverFunc = ResolveTypeRefForPeInfo; + Func resolverFunc = ResolveTypeRefForPeInfo; int memberRefRowCount = peInfo.reader.GetTableRowCount(TableIndex.MemberRef); for (int row = 1; row <= memberRefRowCount; row++) { @@ -142,7 +143,7 @@ public TypeRefTypeSystemContext(IEnumerable refReaders) continue; } - EcmaSignatureParser ecmaSigParse = new EcmaSignatureParser(this, ResolveTypeRefForPeInfo, peInfo.reader.GetBlobReader(memberRef.Signature)); + EcmaSignatureParser ecmaSigParse = new EcmaSignatureParser(this, ResolveTypeRefForPeInfo, peInfo.reader.GetBlobReader(memberRef.Signature), NotFoundBehavior.ReturnNull); string name = peInfo.reader.GetString(memberRef.Name); if (memberRef.GetKind() == MemberReferenceKind.Method) @@ -157,8 +158,9 @@ public TypeRefTypeSystemContext(IEnumerable refReaders) } } - TypeDesc ResolveTypeRefForPeInfo(EntityHandle handle) + TypeDesc ResolveTypeRefForPeInfo(EntityHandle handle, NotFoundBehavior notFoundBehavior) { + Debug.Assert(notFoundBehavior == NotFoundBehavior.ReturnNull); TypeRefTypeSystemType type = null; if (handle.Kind == HandleKind.TypeReference) { diff --git a/src/coreclr/tools/dotnet-pgo/TypeRefTypeSystem/TypeRefTypeSystemModule.cs b/src/coreclr/tools/dotnet-pgo/TypeRefTypeSystem/TypeRefTypeSystemModule.cs index 45529383eeab..d057112766cc 100644 --- a/src/coreclr/tools/dotnet-pgo/TypeRefTypeSystem/TypeRefTypeSystemModule.cs +++ b/src/coreclr/tools/dotnet-pgo/TypeRefTypeSystem/TypeRefTypeSystemModule.cs @@ -71,11 +71,16 @@ private TypeRefTypeSystemType GetTypeInternal(string nameSpace, string name) return type; } - public override MetadataType GetType(string nameSpace, string name, bool throwIfNotFound = true) + public override MetadataType GetType(string nameSpace, string name, NotFoundBehavior notFoundBehavior) { MetadataType type = GetTypeInternal(nameSpace, name); - if ((type == null) && throwIfNotFound) - ThrowHelper.ThrowTypeLoadException(nameSpace, name, this); + if ((type == null) && notFoundBehavior != NotFoundBehavior.ReturnNull) + { + ResolutionFailure failure = ResolutionFailure.GetTypeLoadResolutionFailure(nameSpace, name, this); + ModuleDesc.GetTypeResolutionFailure = failure; + if (notFoundBehavior == NotFoundBehavior.Throw) + failure.Throw(); + } return type; } } From 7677f7dc71fafad1f35639803b86d05b0bd7df72 Mon Sep 17 00:00:00 2001 From: Andy Gocke Date: Wed, 31 Mar 2021 11:34:05 -0700 Subject: [PATCH 78/98] Add option for building a test exe as single file (#42972) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add option for building a test exe as single file * Remove left over test * Add target to exclude references from single-file * First attempt at adding a CI job * Opt-in specific libraries for single-file testing support Start with System.Collections as all tests pass. * Config testing using single-file in build.cmd * Change yml suffix name to SingleFile * Windows_NT_x64 -> windows_x64 * Fix for helix queueing * Respond to host rename * Change TargetOS to check for windows * chmod test exe on linux * Direct singlefilehost to the locally built copy * Adjust singlefilehost copy+call * Add .exe suffix on Windows * Move libraries after hosts build to allow for libs.test to depend on hosts build * Split up host and libs packaging and tests * Move host packaging * Move pretest up * Move packages up as well * Reorder libs pretest and libs.packages * Add isSingleFile build parameter to limit Linux Helix jobs * Typo * Change conditional check * Fix yml * Fix yml * Fix neq * Fix subsets * Typo * Fix * Adjust assert * Include code from Michal to skip failing test * Remove empty ItemGroup * Update src/libraries/Common/tests/SingleFileTestRunner/SingleFileTestRunner.cs Co-authored-by: Michal Strehovský * Update eng/testing/tests.singlefile.targets Co-authored-by: Michal Strehovský * Apply suggestions from code review Co-authored-by: Michal Strehovský * Use ProjectExclusions * Update eng/testing/tests.singlefile.targets Co-authored-by: Michal Strehovský * Revert changes * Remove host build from tests Co-authored-by: Michal Strehovský --- .../libraries/helix-queues-setup.yml | 6 +- eng/pipelines/runtime.yml | 22 ++++++ eng/testing/tests.singlefile.targets | 62 +++++++++++++++ eng/testing/tests.targets | 7 +- eng/testing/xunit/xunit.console.targets | 4 +- eng/testing/xunit/xunit.props | 2 +- .../SingleFileTestRunner.cs | 77 +++++++++++++++++++ .../tests/System.Reflection.Tests.csproj | 4 + src/libraries/tests.proj | 20 +++-- 9 files changed, 188 insertions(+), 16 deletions(-) create mode 100644 eng/testing/tests.singlefile.targets create mode 100644 src/libraries/Common/tests/SingleFileTestRunner/SingleFileTestRunner.cs diff --git a/eng/pipelines/libraries/helix-queues-setup.yml b/eng/pipelines/libraries/helix-queues-setup.yml index 8b0f58894cd4..90bf48d7cae1 100644 --- a/eng/pipelines/libraries/helix-queues-setup.yml +++ b/eng/pipelines/libraries/helix-queues-setup.yml @@ -52,7 +52,7 @@ jobs: # Linux x64 - ${{ if eq(parameters.platform, 'Linux_x64') }}: - - ${{ if eq(parameters.jobParameters.interpreter, '') }}: + - ${{ if and(eq(parameters.jobParameters.interpreter, ''), ne(parameters.jobParameters.isSingleFile, true)) }}: - ${{ if and(eq(parameters.jobParameters.testScope, 'outerloop'), eq(parameters.jobParameters.runtimeFlavor, 'mono')) }}: - (Centos.8.Amd64.Open)Ubuntu.1604.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:centos-8-helix-20201229003624-c1bf759 - RedHat.7.Amd64.Open @@ -81,7 +81,7 @@ jobs: - Ubuntu.1804.Amd64.Open - SLES.15.Amd64.Open - (Fedora.30.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-30-helix-20200512010621-4f8cef7 - - ${{ if eq(parameters.jobParameters.interpreter, 'true') }}: + - ${{ if or(eq(parameters.jobParameters.interpreter, 'true'), eq(parameters.jobParameters.isSingleFile, true)) }}: # Limiting interp runs as we don't need as much coverage. - Debian.9.Amd64.Open @@ -135,7 +135,7 @@ jobs: # .NETFramework - ${{ if eq(parameters.jobParameters.framework, 'net48') }}: - Windows.10.Amd64.Client19H1.Open - + # AllConfigurations - ${{ if eq(parameters.jobParameters.framework, 'allConfigurations') }}: - Windows.10.Amd64.Server19H1.Open diff --git a/eng/pipelines/runtime.yml b/eng/pipelines/runtime.yml index df3f7ca8184b..476150aad686 100644 --- a/eng/pipelines/runtime.yml +++ b/eng/pipelines/runtime.yml @@ -302,6 +302,28 @@ jobs: eq(variables['monoContainsChange'], true), eq(variables['isFullMatrix'], true)) +# Build and test libraries under single-file publishing +- template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/global-build-job.yml + helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml + buildConfig: Release + platforms: + - windows_x64 + - Linux_x64 + jobParameters: + testGroup: innerloop + isFullMatrix: ${{ variables.isFullMatrix }} + isSingleFile: true + nameSuffix: SingleFile + buildArgs: -s clr+libs+libs.tests -c $(_BuildConfig) /p:TestSingleFile=true /p:ArchiveTests=true + timeoutInMinutes: 120 + # extra steps, run tests + extraStepsTemplate: /eng/pipelines/libraries/helix.yml + extraStepsParameters: + creator: dotnet-bot + testRunNamePrefixSuffix: SingleFile_$(_BuildConfig) + # # Build the whole product using Mono and run runtime tests # diff --git a/eng/testing/tests.singlefile.targets b/eng/testing/tests.singlefile.targets new file mode 100644 index 000000000000..86cbc7ee5109 --- /dev/null +++ b/eng/testing/tests.singlefile.targets @@ -0,0 +1,62 @@ + + + Exe + + $([MSBuild]::NormalizeDirectory('$(OutDir)', 'publish')) + $([MSBuild]::NormalizePath('$(BundleDir)', '$(RunScriptOutputName)')) + $(PackageRID) + + $(AssemblyName).exe + chmod +rwx $(AssemblyName) && ./$(AssemblyName) + + + + true + true + true + $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'coreclr', '$(TargetOS).$(TargetArchitecture).$(Configuration)', 'corehost'))/singlefilehost + $(SingleFileHostSourcePath).exe + + + + + + + + + + + + + <__Identity>%(ResolvedFileToPublish.Identity) + <__FileName>%(ResolvedFileToPublish.Filename)%(ResolvedFileToPublish.Extension) + + + + <__NewResolvedFiles Include="@(ResolvedFileToPublish)"> + true + + + + + + + + + + + + + + diff --git a/eng/testing/tests.targets b/eng/testing/tests.targets index b7b642b3527e..9fc08ddd6bcc 100644 --- a/eng/testing/tests.targets +++ b/eng/testing/tests.targets @@ -25,7 +25,7 @@ - + @@ -37,7 +37,7 @@ <_ZipSourceDirectory>$(OutDir) - <_ZipSourceDirectory Condition="'$(TargetOS)' == 'Browser'">$(BundleDir) + <_ZipSourceDirectory Condition="'$(TargetOS)' == 'Browser' or '$(TestSingleFile)' == 'true'">$(BundleDir) @@ -118,10 +118,11 @@ + - + diff --git a/eng/testing/xunit/xunit.console.targets b/eng/testing/xunit/xunit.console.targets index a88b08f61398..7710a05b5a37 100644 --- a/eng/testing/xunit/xunit.console.targets +++ b/eng/testing/xunit/xunit.console.targets @@ -5,7 +5,7 @@ true - + <_depsFileArgument Condition="'$(GenerateDependencyFile)' == 'true'">--depsfile $(AssemblyName).deps.json "$(RunScriptHost)" exec --runtimeconfig $(AssemblyName).runtimeconfig.json $(_depsFileArgument) xunit.console.dll xunit.console.exe @@ -36,7 +36,7 @@ $(_withoutCategories.Replace(';', '%0dcategory=')) - + diff --git a/eng/testing/xunit/xunit.props b/eng/testing/xunit/xunit.props index f63b3906fccb..3f9c4b67141a 100644 --- a/eng/testing/xunit/xunit.props +++ b/eng/testing/xunit/xunit.props @@ -13,7 +13,7 @@ - + diff --git a/src/libraries/Common/tests/SingleFileTestRunner/SingleFileTestRunner.cs b/src/libraries/Common/tests/SingleFileTestRunner/SingleFileTestRunner.cs new file mode 100644 index 000000000000..7b67a1338e5a --- /dev/null +++ b/src/libraries/Common/tests/SingleFileTestRunner/SingleFileTestRunner.cs @@ -0,0 +1,77 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable disable + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.Loader; +using System.Threading.Tasks; +using System.Xml.Linq; +using Xunit; +using Xunit.Abstractions; +using Xunit.Sdk; + +public class SingleFileTestRunner : XunitTestFramework +{ + private SingleFileTestRunner(IMessageSink messageSink) + : base(messageSink) { } + + public static int Main(string[] args) + { + var asm = typeof(SingleFileTestRunner).Assembly; + Console.WriteLine("Running assembly:" + asm.FullName); + + var diagnosticSink = new ConsoleDiagnosticMessageSink(); + var testsFinished = new TaskCompletionSource(); + var testSink = new TestMessageSink(); + var summarySink = new DelegatingExecutionSummarySink(testSink, + () => false, + (completed, summary) => Console.WriteLine($"Tests run: {summary.Total}, Errors: {summary.Errors}, Failures: {summary.Failed}, Skipped: {summary.Skipped}. Time: {TimeSpan.FromSeconds((double)summary.Time).TotalSeconds}s")); + var resultsXmlAssembly = new XElement("assembly"); + var resultsSink = new DelegatingXmlCreationSink(summarySink, resultsXmlAssembly); + + testSink.Execution.TestSkippedEvent += args => { Console.WriteLine($"[SKIP] {args.Message.Test.DisplayName}"); }; + testSink.Execution.TestFailedEvent += args => { Console.WriteLine($"[FAIL] {args.Message.Test.DisplayName}{Environment.NewLine}{Xunit.ExceptionUtility.CombineMessages(args.Message)}{Environment.NewLine}{Xunit.ExceptionUtility.CombineStackTraces(args.Message)}"); }; + + testSink.Execution.TestAssemblyFinishedEvent += args => + { + Console.WriteLine($"Finished {args.Message.TestAssembly.Assembly}{Environment.NewLine}"); + testsFinished.SetResult(); + }; + + var xunitTestFx = new SingleFileTestRunner(diagnosticSink); + var asmInfo = Reflector.Wrap(asm); + var asmName = asm.GetName(); + + var discoverySink = new TestDiscoverySink(); + var discoverer = xunitTestFx.CreateDiscoverer(asmInfo); + discoverer.Find(false, discoverySink, TestFrameworkOptions.ForDiscovery()); + discoverySink.Finished.WaitOne(); + XunitFilters filters = new XunitFilters(); + filters.ExcludedTraits.Add("category", new List { "failing" }); + var filteredTestCases = discoverySink.TestCases.Where(filters.Filter).ToList(); + var executor = xunitTestFx.CreateExecutor(asmName); + executor.RunTests(filteredTestCases, resultsSink, TestFrameworkOptions.ForExecution()); + + resultsSink.Finished.WaitOne(); + + var failed = resultsSink.ExecutionSummary.Failed > 0 || resultsSink.ExecutionSummary.Errors > 0; + return failed ? 1 : 0; + } +} + +internal class ConsoleDiagnosticMessageSink : IMessageSink +{ + public bool OnMessage(IMessageSinkMessage message) + { + if (message is IDiagnosticMessage diagnosticMessage) + { + return true; + } + return false; + } +} diff --git a/src/libraries/System.Reflection/tests/System.Reflection.Tests.csproj b/src/libraries/System.Reflection/tests/System.Reflection.Tests.csproj index 5b4ff2785839..deceb65e37f3 100644 --- a/src/libraries/System.Reflection/tests/System.Reflection.Tests.csproj +++ b/src/libraries/System.Reflection/tests/System.Reflection.Tests.csproj @@ -71,4 +71,8 @@ + + + <__ExcludeFromBundle Include="TestAssembly.dll" /> + diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index 55fdbc13f9f1..1442c9d3020f 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -12,7 +12,7 @@ false false - + @@ -107,17 +107,17 @@ - + - + - + - + @@ -320,6 +320,12 @@ + + + + + + - + @@ -357,7 +363,7 @@ Exclude="$(RepoRoot)\src\tests\FunctionalTests\Android\Device_Emulator\AOT\Android.Device_Emulator.Aot.Test.csproj" BuildInParallel="false" /> - + From 74cb83d7f62262604f2009d7bde4d0d88e65f390 Mon Sep 17 00:00:00 2001 From: Andy Gocke Date: Wed, 31 Mar 2021 12:08:18 -0700 Subject: [PATCH 79/98] Also build bundleproj for PGO (#50434) I previously disabled publishing all but Microsoft.NETCore.App.sfxproj, which publishes the dotnet-runtime-internal package. This is almost, but not quite, the right package. It doesn't contain the host dependencies, although it does contain the right runtime files. The right package is Microsoft.NETCore.App.bundleproj, which builds dotnet-runtime. Unfortunately, I didn't catch this in manual testing because I think copying over the temp bits reused the host dependencies from the previous install location. I've wiped this from scratch and it looks fine now. (crossed fingers) --- eng/Subsets.props | 6 +++--- .../Microsoft.NET.HostModel.csproj | 1 + .../sfx/bundle/Microsoft.NETCore.App.Bundle.bundleproj | 3 ++- src/libraries/libraries-packages.proj | 8 +++++--- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/eng/Subsets.props b/eng/Subsets.props index 4d7e7119e00e..6e9f9bf1a702 100644 --- a/eng/Subsets.props +++ b/eng/Subsets.props @@ -208,7 +208,7 @@ $(CoreClrProjectRoot)crossgen-corelib.proj" Category="clr" /> - + @@ -274,7 +274,7 @@ - + @@ -300,10 +300,10 @@ - + diff --git a/src/installer/managed/Microsoft.NET.HostModel/Microsoft.NET.HostModel.csproj b/src/installer/managed/Microsoft.NET.HostModel/Microsoft.NET.HostModel.csproj index 923e75d0772d..64ac366ee390 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/Microsoft.NET.HostModel.csproj +++ b/src/installer/managed/Microsoft.NET.HostModel/Microsoft.NET.HostModel.csproj @@ -15,6 +15,7 @@ MicrosoftAspNetCore true + Microsoft.Net.HostModel.PGO diff --git a/src/installer/pkg/sfx/bundle/Microsoft.NETCore.App.Bundle.bundleproj b/src/installer/pkg/sfx/bundle/Microsoft.NETCore.App.Bundle.bundleproj index 3c9a701c9637..307a2fdca470 100644 --- a/src/installer/pkg/sfx/bundle/Microsoft.NETCore.App.Bundle.bundleproj +++ b/src/installer/pkg/sfx/bundle/Microsoft.NETCore.App.Bundle.bundleproj @@ -6,6 +6,7 @@ product band to upgrade in place. --> true + false .NET Core Shared Framework Bundle Installer $(MSBuildProjectDirectory) dotnet-runtime @@ -18,7 +19,7 @@ - + diff --git a/src/libraries/libraries-packages.proj b/src/libraries/libraries-packages.proj index b8fde0c0adb1..9af91645ebef 100644 --- a/src/libraries/libraries-packages.proj +++ b/src/libraries/libraries-packages.proj @@ -4,7 +4,7 @@ BuildAllProjects=true $(AdditionalBuildTargetFrameworks);package-$(Configuration) - + $(NuGetPackageRoot)microsoft.dotnet.build.tasks.packaging\$(MicrosoftDotNetBuildTasksPackagingVersion)\tools\ $(PackagingTaskAssembly)netcoreapp3.1\ @@ -14,8 +14,10 @@ - - + + From 28629d9beb38f9d5bd3aa15ddbb43df3b31d75ec Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Wed, 31 Mar 2021 12:09:35 -0700 Subject: [PATCH 80/98] [AndroidCrypto] Make SslStreamCertificateContext treat an empty chain as not finding intermediates (#50195) --- .../Security/SslStreamCertificateContext.cs | 50 +++++++++++-------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.cs index ed4002495820..4ae127aadcc1 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.cs @@ -39,39 +39,45 @@ public static SslStreamCertificateContext Create(X509Certificate2 target, X509Ce } int count = chain.ChainElements.Count - 1; -#pragma warning disable 0162 // Disable unreachable code warning. TrimRootCertificate is const bool = false on some platforms - if (TrimRootCertificate) + + // Some platforms (e.g. Android) can't ignore all verification and will return zero + // certificates on failure to build a chain. Treat this as not finding any intermediates. + if (count >= 0) { - count--; - foreach (X509ChainStatus status in chain.ChainStatus) +#pragma warning disable 0162 // Disable unreachable code warning. TrimRootCertificate is const bool = false on some platforms + if (TrimRootCertificate) { - if (status.Status.HasFlag(X509ChainStatusFlags.PartialChain)) + count--; + foreach (X509ChainStatus status in chain.ChainStatus) { - // The last cert isn't a root cert - count++; - break; + if (status.Status.HasFlag(X509ChainStatusFlags.PartialChain)) + { + // The last cert isn't a root cert + count++; + break; + } } } - } #pragma warning restore 0162 - // Count can be zero for a self-signed certificate, or a cert issued directly from a root. - if (count > 0 && chain.ChainElements.Count > 1) - { - intermediates = new X509Certificate2[count]; - for (int i = 0; i < count; i++) + // Count can be zero for a self-signed certificate, or a cert issued directly from a root. + if (count > 0 && chain.ChainElements.Count > 1) { - intermediates[i] = chain.ChainElements[i + 1].Certificate; + intermediates = new X509Certificate2[count]; + for (int i = 0; i < count; i++) + { + intermediates[i] = chain.ChainElements[i + 1].Certificate; + } } - } - // Dispose the copy of the target cert. - chain.ChainElements[0].Certificate.Dispose(); + // Dispose the copy of the target cert. + chain.ChainElements[0].Certificate.Dispose(); - // Dispose the last cert, if we didn't include it. - for (int i = count + 1; i < chain.ChainElements.Count; i++) - { - chain.ChainElements[i].Certificate.Dispose(); + // Dispose the last cert, if we didn't include it. + for (int i = count + 1; i < chain.ChainElements.Count; i++) + { + chain.ChainElements[i].Certificate.Dispose(); + } } } From 3c135c5c38a39b212afebfaa39f03d2c9e6e723c Mon Sep 17 00:00:00 2001 From: Prashanth Govindarajan Date: Wed, 31 Mar 2021 13:08:23 -0700 Subject: [PATCH 81/98] Prohibit BF unless the app opts in (#48527) * Prohibit BF unless the app opts in * Address feedback * First unit test * 2nd unit test. Checkpoint * sq * sq * sq * Address Levi's feedback * sq * Address nits * Linker changes and tests * sq * sq * Linker warnings ids * Address comments * Change trimming test so linker can detect a pattern * sq * Address comments * sq Co-authored-by: Eric Erhardt --- docs/workflow/trimming/feature-switches.md | 1 + eng/testing/linker/project.csproj.template | 1 + eng/testing/linker/trimmingTests.targets | 6 + .../src/ILLink/ILLink.Substitutions.xml | 7 + .../src/ILLink/ILLink.Suppressions.xml | 12 -- .../src/Resources/Strings.resx | 3 + ...System.ComponentModel.TypeConverter.csproj | 3 + .../DesigntimeLicenseContextSerializer.cs | 151 +++++++++++++++++- ...DesigntimeLicenseContextSerializerTests.cs | 140 ++++++++++++++++ ....ComponentModel.TypeConverter.Tests.csproj | 1 + ...LicenseContextSerialization_Deserialize.cs | 39 +++++ ...meLicenseContextSerialization_Serialize.cs | 45 ++++++ ...ntimeLicenseContextSerialization_Stream.cs | 15 ++ ...nentModel.TypeConverter.TrimmingTests.proj | 19 +++ 14 files changed, 423 insertions(+), 20 deletions(-) create mode 100644 src/libraries/System.ComponentModel.TypeConverter/src/ILLink/ILLink.Substitutions.xml create mode 100644 src/libraries/System.ComponentModel.TypeConverter/tests/Design/DesigntimeLicenseContextSerializerTests.cs create mode 100644 src/libraries/System.ComponentModel.TypeConverter/tests/TrimmingTests/DesigntimeLicenseContextSerialization_Deserialize.cs create mode 100644 src/libraries/System.ComponentModel.TypeConverter/tests/TrimmingTests/DesigntimeLicenseContextSerialization_Serialize.cs create mode 100644 src/libraries/System.ComponentModel.TypeConverter/tests/TrimmingTests/DesigntimeLicenseContextSerialization_Stream.cs diff --git a/docs/workflow/trimming/feature-switches.md b/docs/workflow/trimming/feature-switches.md index 40971d1fdb9d..36ac156f4f86 100644 --- a/docs/workflow/trimming/feature-switches.md +++ b/docs/workflow/trimming/feature-switches.md @@ -19,6 +19,7 @@ configurations but their defaults might vary as any SDK can set the defaults dif | StartupHookSupport | System.StartupHookProvider.IsSupported | Startup hooks are disabled when set to false. Startup hook related functionality can be trimmed. | | TBD | System.Threading.ThreadPool.EnableDispatchAutoreleasePool | When set to true, creates an NSAutoreleasePool around each thread pool work item on applicable platforms. | | CustomResourceTypesSupport | System.Resources.ResourceManager.AllowCustomResourceTypes | Use of custom resource types is disabled when set to false. ResourceManager code paths that use reflection for custom types can be trimmed. | +| EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization | System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization | BinaryFormatter serialization support is trimmed when set to false. | Any feature-switch which defines property can be set in csproj file or on the command line as any other MSBuild property. Those without predefined property name diff --git a/eng/testing/linker/project.csproj.template b/eng/testing/linker/project.csproj.template index 7ac21cbcb5b5..53576a8cc216 100644 --- a/eng/testing/linker/project.csproj.template +++ b/eng/testing/linker/project.csproj.template @@ -29,6 +29,7 @@ {AdditionalProjectReferences} + {TrimmerRootAssemblies} + browser-wasm + + + --feature System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization true + DesigntimeLicenseContextSerialization_Stream.cs + + browser-wasm + + + + + + From d5aeac9cfc390c5002106e81daaf0bd502a6d32f Mon Sep 17 00:00:00 2001 From: Juan Hoyos Date: Wed, 31 Mar 2021 13:34:25 -0700 Subject: [PATCH 82/98] Fix usage of process_vm_readv in createdump (#50477) * Fix usage of process_vm_readv in createdump; fallback to pread64 * No-op logging --- src/coreclr/debug/createdump/crashinfo.cpp | 2 -- src/coreclr/debug/createdump/crashinfo.h | 5 ++- .../debug/createdump/crashinfounix.cpp | 34 +++++++++++++------ 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/coreclr/debug/createdump/crashinfo.cpp b/src/coreclr/debug/createdump/crashinfo.cpp index bccbb8553a91..4592b0d253a5 100644 --- a/src/coreclr/debug/createdump/crashinfo.cpp +++ b/src/coreclr/debug/createdump/crashinfo.cpp @@ -16,10 +16,8 @@ CrashInfo::CrashInfo(pid_t pid) : m_task = 0; #else m_auxvValues.fill(0); -#ifndef HAVE_PROCESS_VM_READV m_fd = -1; #endif -#endif } CrashInfo::~CrashInfo() diff --git a/src/coreclr/debug/createdump/crashinfo.h b/src/coreclr/debug/createdump/crashinfo.h index 9bdf63be49e7..46b0825c0a7e 100644 --- a/src/coreclr/debug/createdump/crashinfo.h +++ b/src/coreclr/debug/createdump/crashinfo.h @@ -45,9 +45,8 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback, #ifdef __APPLE__ vm_map_t m_task; // the mach task for the process #else -#ifndef HAVE_PROCESS_VM_READV + bool m_canUseProcVmReadSyscall; int m_fd; // /proc//mem handle -#endif #endif std::string m_coreclrPath; // the path of the coreclr module or empty if none #ifdef __APPLE__ @@ -112,7 +111,7 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback, void VisitModule(uint64_t baseAddress, std::string& moduleName); void VisitProgramHeader(uint64_t loadbias, uint64_t baseAddress, ElfW(Phdr)* phdr); bool EnumerateModuleMappings(); -#endif +#endif bool EnumerateMemoryRegionsWithDAC(MINIDUMP_TYPE minidumpType); bool EnumerateManagedModules(IXCLRDataProcess* pClrDataProcess); bool UnwindAllThreads(IXCLRDataProcess* pClrDataProcess); diff --git a/src/coreclr/debug/createdump/crashinfounix.cpp b/src/coreclr/debug/createdump/crashinfounix.cpp index d300d343d54a..93a55394a5d8 100644 --- a/src/coreclr/debug/createdump/crashinfounix.cpp +++ b/src/coreclr/debug/createdump/crashinfounix.cpp @@ -8,7 +8,6 @@ bool GetStatus(pid_t pid, pid_t* ppid, pid_t* tgid, std::string* name); bool CrashInfo::Initialize() { -#ifndef HAVE_PROCESS_VM_READV char memPath[128]; _snprintf_s(memPath, sizeof(memPath), sizeof(memPath), "/proc/%lu/mem", m_pid); @@ -18,12 +17,13 @@ CrashInfo::Initialize() fprintf(stderr, "open(%s) FAILED %d (%s)\n", memPath, errno, strerror(errno)); return false; } -#endif // Get the process info if (!GetStatus(m_pid, &m_ppid, &m_tgid, &m_name)) { return false; } + + m_canUseProcVmReadSyscall = true; return true; } @@ -39,13 +39,11 @@ CrashInfo::CleanupAndResumeProcess() waitpid(thread->Tid(), &waitStatus, __WALL); } } -#ifndef HAVE_PROCESS_VM_READV if (m_fd != -1) { close(m_fd); m_fd = -1; } -#endif } // @@ -253,7 +251,7 @@ CrashInfo::GetDSOInfo() int phnum = m_auxvValues[AT_PHNUM]; assert(m_auxvValues[AT_PHENT] == sizeof(Phdr)); assert(phnum != PN_XNUM); - return EnumerateElfInfo(phdrAddr, phnum); + return EnumerateElfInfo(phdrAddr, phnum); } // @@ -334,17 +332,31 @@ CrashInfo::ReadProcessMemory(void* address, void* buffer, size_t size, size_t* r { assert(buffer != nullptr); assert(read != nullptr); + *read = 0; #ifdef HAVE_PROCESS_VM_READV - iovec local{ buffer, size }; - iovec remote{ address, size }; - *read = process_vm_readv(m_pid, &local, 1, &remote, 1, 0); -#else - assert(m_fd != -1); - *read = pread64(m_fd, buffer, size, (off64_t)address); + if (m_canUseProcVmReadSyscall) + { + iovec local{ buffer, size }; + iovec remote{ address, size }; + *read = process_vm_readv(m_pid, &local, 1, &remote, 1, 0); + } + + if (!m_canUseProcVmReadSyscall || (*read == (size_t)-1 && errno == EPERM)) #endif + { + // If we've failed, avoid going through expensive syscalls + // After all, the use of process_vm_readv is largely as a + // performance optimization. + m_canUseProcVmReadSyscall = false; + assert(m_fd != -1); + *read = pread64(m_fd, buffer, size, (off64_t)address); + } + if (*read == (size_t)-1) { + int readErrno = errno; + TRACE_VERBOSE("ReadProcessMemory FAILED, addr: %" PRIA PRIx ", size: %zu, ERRNO %d: %s\n", address, size, readErrno, strerror(readErrno)); return false; } return true; From da5dfef04a138c8638f8b2db478adf9d51f8ea2a Mon Sep 17 00:00:00 2001 From: Bill Wert Date: Wed, 31 Mar 2021 14:06:48 -0700 Subject: [PATCH 83/98] add android scenarios (#50407) --- eng/common/performance/android_scenarios.proj | 34 ++++++++++++++ eng/common/performance/performance-setup.ps1 | 12 ++++- eng/pipelines/coreclr/perf.yml | 39 ++++++++++++++++ .../templates/build-perf-sample-apps.yml | 45 +++++++++++++++++++ eng/pipelines/coreclr/templates/perf-job.yml | 43 ++++++++++++------ src/mono/sample/Android/Makefile | 5 ++- 6 files changed, 162 insertions(+), 16 deletions(-) create mode 100644 eng/common/performance/android_scenarios.proj create mode 100644 eng/pipelines/coreclr/templates/build-perf-sample-apps.yml diff --git a/eng/common/performance/android_scenarios.proj b/eng/common/performance/android_scenarios.proj new file mode 100644 index 000000000000..2f34947b07a0 --- /dev/null +++ b/eng/common/performance/android_scenarios.proj @@ -0,0 +1,34 @@ + + + python3 + $(HelixPreCommands);chmod +x $HELIX_WORKITEM_PAYLOAD/SOD/SizeOnDisk + + + + + %(Identity) + + + + + %HELIX_CORRELATION_PAYLOAD%\performance\src\scenarios\ + + + $HELIX_CORRELATION_PAYLOAD/performance/src/scenarios/ + + + + + $(ScenarioDirectory)helloandroid + copy $HELIX_CORRELATION_PAYLOAD%\HelloAndroid.apk .;$(Python) pre.py + $(Python) test.py sod --scenario-name "%(Identity)" + $(Python) post.py + + + $(ScenarioDirectory)helloandroid + copy $HELIX_CORRELATION_PAYLOAD%\HelloAndroid.apk .;$(Python) pre.py --unzip + $(Python) test.py sod --scenario-name "%(Identity)" + $(Python) post.py + + + \ No newline at end of file diff --git a/eng/common/performance/performance-setup.ps1 b/eng/common/performance/performance-setup.ps1 index b6324039ad37..174ceeb1b9d7 100644 --- a/eng/common/performance/performance-setup.ps1 +++ b/eng/common/performance/performance-setup.ps1 @@ -19,7 +19,8 @@ Param( [switch] $Compare, [string] $MonoDotnet="", [string] $Configurations="CompilationMode=$CompilationMode RunKind=$Kind", - [string] $LogicalMachine="" + [string] $LogicalMachine="", + [switch] $AndroidMono ) $RunFromPerformanceRepo = ($Repository -eq "dotnet/performance") -or ($Repository -eq "dotnet-performance") @@ -102,6 +103,15 @@ if ($UseBaselineCoreRun) { Move-Item -Path $BaselineCoreRootDirectory -Destination $NewBaselineCoreRoot } +if ($AndroidMono) { + if(!(Test-Path $WorkItemDirectory)) + { + mkdir $WorkItemDirectory + } + Copy-Item -path "$SourceDirectory\artifacts\bin\AndroidSampleApp\arm64\Release\android-arm64\publish\apk\bin\HelloAndroid.apk" $PayloadDirectory + $SetupArguments = $SetupArguments -replace $Architecture, 'arm64' +} + $DocsDir = (Join-Path $PerformanceDirectory "docs") robocopy $DocsDir $WorkItemDirectory diff --git a/eng/pipelines/coreclr/perf.yml b/eng/pipelines/coreclr/perf.yml index 511db7798004..ce756c81303a 100644 --- a/eng/pipelines/coreclr/perf.yml +++ b/eng/pipelines/coreclr/perf.yml @@ -152,6 +152,29 @@ jobs: archiveType: tar tarCompression: gz + # build mono Android scenarios + - template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/global-build-job.yml + buildConfig: release + runtimeFlavor: mono + platforms: + - Android_arm64 + jobParameters: + buildArgs: -s mono+libs+host+packs -c $(_BuildConfig) + nameSuffix: AndroidMono + isOfficialBuild: false + extraStepsTemplate: /eng/pipelines/coreclr/templates/build-perf-sample-apps.yml + extraStepsParameters: + rootFolder: '$(Build.SourcesDirectory)/artifacts/' + includeRootFolder: true + displayName: Android Mono Artifacts + artifactName: AndroidMonoarm64 + archiveExtension: '.tar.gz' + archiveType: tar + tarCompression: gz + + # build mono - template: /eng/pipelines/common/platform-matrix.yml parameters: @@ -161,6 +184,22 @@ jobs: platforms: - Linux_x64 + # run mono android scenarios + - template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/coreclr/templates/perf-job.yml + buildConfig: release + runtimeFlavor: mono + platforms: + - Windows_x64 + jobParameters: + testGroup: perf + runtimeType: AndroidMono + projectFile: android_scenarios.proj + runKind: android_scenarios + runJobTemplate: /eng/pipelines/coreclr/templates/run-scenarios-job.yml + logicalmachine: 'perftiger' + # run mono microbenchmarks perf job - template: /eng/pipelines/common/platform-matrix.yml parameters: diff --git a/eng/pipelines/coreclr/templates/build-perf-sample-apps.yml b/eng/pipelines/coreclr/templates/build-perf-sample-apps.yml new file mode 100644 index 000000000000..2b556af6d911 --- /dev/null +++ b/eng/pipelines/coreclr/templates/build-perf-sample-apps.yml @@ -0,0 +1,45 @@ +parameters: + osGroup: '' + osSubgroup: '' + archType: '' + buildConfig: '' + runtimeFlavor: '' + helixQueues: '' + targetRid: '' + nameSuffix: '' + platform: '' + shouldContinueOnError: '' + rootFolder: '' + includeRootFolder: '' + displayName: '' + artifactName: '' + archiveExtension: '' + archiveType: '' + tarCompression: '' + +steps: +# Build Android sample app + - ${{ if eq(parameters.osGroup, 'Android') }}: + - script: make run MONO_ARCH=arm64 DEPLOY_AND_RUN=false + workingDirectory: $(Build.SourcesDirectory)/src/mono/sample/Android + displayName: Build HelloAndroid sample app + + - template: /eng/pipelines/common/upload-artifact-step.yml + parameters: + osGroup: ${{ parameters.osGroup }} + osSubgroup: ${{ parameters.osSubgroup }} + archType: ${{ parameters.archType }} + buildConfig: ${{ parameters.buildConfig }} + runtimeFlavor: ${{ parameters.runtimeFlavor }} + helixQueues: ${{ parameters.helixQueues }} + targetRid: ${{ parameters.targetRid }} + nameSuffix: ${{ parameters.nameSuffix }} + platform: ${{ parameters.platform }} + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + rootFolder: ${{ parameters.rootFolder }} + includeRootFolder: ${{ parameters.includeRootFolder }} + displayName: ${{ parameters.displayName }} + artifactName: ${{ parameters.artifactName }} + archiveExtension: ${{ parameters.archiveExtension }} + archiveType: ${{ parameters.archiveType }} + tarCompression: ${{ parameters.tarCompression }} \ No newline at end of file diff --git a/eng/pipelines/coreclr/templates/perf-job.yml b/eng/pipelines/coreclr/templates/perf-job.yml index 703e7a4645c6..5c9c2e4f4d91 100644 --- a/eng/pipelines/coreclr/templates/perf-job.yml +++ b/eng/pipelines/coreclr/templates/perf-job.yml @@ -11,7 +11,7 @@ parameters: runtimeType: 'coreclr' pool: '' codeGenType: 'JIT' - projetFile: '' + projectFile: '' runKind: '' runJobTemplate: '/eng/pipelines/coreclr/templates/run-performance-job.yml' additionalSetupParameters: '' @@ -44,7 +44,8 @@ jobs: logicalmachine: ${{ parameters.logicalmachine }} # Test job depends on the corresponding build job dependsOn: - - ${{ format('coreclr_{0}_product_build_{1}{2}_{3}_{4}', parameters.runtimeVariant, parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.buildConfig) }} + - ${{ if eq(parameters.runtimeType, 'coreclr')}}: + - ${{ format('coreclr_{0}_product_build_{1}{2}_{3}_{4}', parameters.runtimeVariant, parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.buildConfig) }} - ${{ if ne(parameters.liveLibrariesBuildConfig, '') }}: - ${{ format('libraries_build_{0}{1}_{2}_{3}', parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.liveLibrariesBuildConfig) }} - ${{ if and(eq(parameters.runtimeType, 'mono'), ne(parameters.codeGenType, 'AOT')) }}: @@ -53,13 +54,15 @@ jobs: - ${{ format('build_{0}{1}_{2}_{3}_{4}', 'Browser', '', 'wasm', parameters.buildConfig, parameters.runtimeType) }} - ${{ if eq(parameters.codeGenType, 'AOT')}}: - ${{ format('build_{0}{1}_{2}_{3}_{4}', parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.buildConfig, parameters.codeGenType) }} + - ${{ if eq(parameters.runtimeType, 'AndroidMono')}}: + - ${{ 'build_Android_arm64_release_AndroidMono' }} - ${{ if eq(parameters.osGroup, 'windows') }}: + ${{ if and(eq(parameters.osGroup, 'windows'), ne(parameters.runtimeType, 'AndroidMono')) }}: ${{ if eq(parameters.runtimeType, 'mono') }}: extraSetupParameters: -Architecture ${{ parameters.archType }} -MonoDotnet $(Build.SourcesDirectory)\.dotnet-mono ${{ if eq(parameters.runtimeType, 'coreclr') }}: extraSetupParameters: -CoreRootDirectory $(Build.SourcesDirectory)\artifacts\tests\coreclr\${{ parameters.osGroup }}.${{ parameters.archType }}.Release\Tests\Core_Root -Architecture ${{ parameters.archType }} - ${{ if ne(parameters.osGroup, 'windows') }}: + ${{ if and(ne(parameters.osGroup, 'windows'), ne(parameters.runtimeType, 'AndroidMono')) }}: ${{ if and(eq(parameters.runtimeType, 'mono'), ne(parameters.codeGenType, 'AOT')) }}: extraSetupParameters: --architecture ${{ parameters.archType }} --monodotnet $(Build.SourcesDirectory)/.dotnet-mono ${{ if eq(parameters.runtimeType, 'wasm') }}: @@ -70,7 +73,9 @@ jobs: extraSetupParameters: --corerootdirectory $(Build.SourcesDirectory)/artifacts/tests/coreclr/${{ parameters.osGroup }}.${{ parameters.archType }}.Release/Tests/Core_Root --architecture ${{ parameters.archType }} ${{ if and(eq(parameters.runtimeType, 'coreclr'), eq(parameters.osSubGroup, '_musl')) }}: extraSetupParameters: --corerootdirectory $(Build.SourcesDirectory)/artifacts/tests/coreclr/${{ parameters.osGroup }}.${{ parameters.archType }}.Release/Tests/Core_Root --architecture ${{ parameters.archType }} --alpine - + ${{ if eq(parameters.runtimeType, 'AndroidMono') }}: + extraSetupParameters: -Architecture ${{ parameters.archType }} -AndroidMono + variables: ${{ parameters.variables }} frameworks: @@ -89,12 +94,13 @@ jobs: displayName: 'live-built libraries' # Download coreclr - - template: /eng/pipelines/common/download-artifact-step.yml - parameters: - unpackFolder: $(buildProductRootFolderPath) - artifactFileName: '$(buildProductArtifactName)$(archiveExtension)' - artifactName: '$(buildProductArtifactName)' - displayName: 'Coreclr product build' + - ${{ if eq(parameters.runtimeType, 'coreclr') }}: + - template: /eng/pipelines/common/download-artifact-step.yml + parameters: + unpackFolder: $(buildProductRootFolderPath) + artifactFileName: '$(buildProductArtifactName)$(archiveExtension)' + artifactName: '$(buildProductArtifactName)' + displayName: 'Coreclr product build' # Download mono - ${{ if and(eq(parameters.runtimeType, 'mono'), ne(parameters.codeGenType, 'AOT')) }}: @@ -130,6 +136,17 @@ jobs: - script: "mkdir -p $(librariesDownloadDir)/bin/aot/sgen;mkdir -p $(librariesDownloadDir)/bin/aot/pack;cp -r $(librariesDownloadDir)/LinuxMonoAOT/artifacts/obj/mono/Linux.${{ parameters.archType }}.Release/mono/* $(librariesDownloadDir)/bin/aot/sgen;cp -r $(librariesDownloadDir)/LinuxMonoAOT/artifacts/bin/microsoft.netcore.app.runtime.linux-${{ parameters.archType }}/Release/* $(librariesDownloadDir)/bin/aot/pack" displayName: "Create aot directory (Linux)" + # Download AndroidMono + - ${{ if eq(parameters.runtimeType, 'AndroidMono')}}: + - template: /eng/pipelines/common/download-artifact-step.yml + parameters: + unpackFolder: $(Build.SourcesDirectory) + cleanUnpackFolder: false + artifactFileName: 'AndroidMonoarm64.tar.gz' + artifactName: 'AndroidMonoarm64' + displayName: 'Mono Android runtime' + + # Create Core_Root - script: $(Build.SourcesDirectory)/src/tests/build$(scriptExt) $(buildConfig) $(archType) generatelayoutonly $(librariesOverrideArg) displayName: Create Core_Root @@ -138,8 +155,8 @@ jobs: # Copy the runtime directory into the testhost folder to include OOBs. - script: "build.cmd -subset libs.pretest -configuration release -ci -arch $(archType) -testscope innerloop /p:RuntimeArtifactsPath=$(librariesDownloadDir)\\bin\\mono\\$(osGroup).$(archType).$(buildConfigUpper) /p:RuntimeFlavor=mono;xcopy $(Build.SourcesDirectory)\\artifacts\\bin\\runtime\\$(_Framework)-$(osGroup)-$(buildConfigUpper)-$(archType)\\* $(Build.SourcesDirectory)\\artifacts\\bin\\testhost\\$(_Framework)-$(osGroup)-$(buildConfigUpper)-$(archType)\\shared\\Microsoft.NETCore.App\\6.0.0 /E /I /Y;xcopy $(Build.SourcesDirectory)\\artifacts\\bin\\testhost\\$(_Framework)-$(osGroup)-$(buildConfigUpper)-$(archType)\\* $(Build.SourcesDirectory)\\.dotnet-mono /E /I /Y;copy $(Build.SourcesDirectory)\\artifacts\\bin\\coreclr\\$(osGroup).$(archType).$(buildConfigUpper)\\corerun.exe $(Build.SourcesDirectory)\\.dotnet-mono\\shared\\Microsoft.NETCore.App\\6.0.0\\corerun.exe" displayName: "Create mono dotnet (Windows)" - condition: and(and(succeeded(), eq(variables.runtimeFlavorName, 'Mono')), eq(variables.osGroup, 'windows')) + condition: and(and(succeeded(), eq(variables.runtimeFlavorName, 'Mono')), eq(variables.osGroup, 'windows'), ne('${{ parameters.runtimeType }}', 'AndroidMono')) - script: "mkdir $(Build.SourcesDirectory)/.dotnet-mono;./build.sh -subset libs.pretest -configuration release -ci -arch $(archType) -testscope innerloop /p:RuntimeArtifactsPath=$(librariesDownloadDir)/bin/mono/$(osGroup).$(archType).$(buildConfigUpper) /p:RuntimeFlavor=mono;cp $(Build.SourcesDirectory)/artifacts/bin/runtime/$(_Framework)-$(osGroup)-$(buildConfigUpper)-$(archType)/* $(Build.SourcesDirectory)/artifacts/bin/testhost/$(_Framework)-$(osGroup)-$(buildConfigUpper)-$(archType)/shared/Microsoft.NETCore.App/6.0.0 -rf;cp $(Build.SourcesDirectory)/artifacts/bin/testhost/$(_Framework)-$(osGroup)-$(buildConfigUpper)-$(archType)/* $(Build.SourcesDirectory)/.dotnet-mono -r;cp $(Build.SourcesDirectory)/artifacts/bin/coreclr/$(osGroup).$(archType).$(buildConfigUpper)/corerun $(Build.SourcesDirectory)/.dotnet-mono/shared/Microsoft.NETCore.App/6.0.0/corerun" displayName: "Create mono dotnet (Linux)" - condition: and(and(succeeded(), eq(variables.runtimeFlavorName, 'Mono')), ne(variables.osGroup, 'windows')) + condition: and(and(succeeded(), eq(variables.runtimeFlavorName, 'Mono')), ne(variables.osGroup, 'windows'), ne('${{ parameters.runtimeType }}', 'AndroidMono')) diff --git a/src/mono/sample/Android/Makefile b/src/mono/sample/Android/Makefile index 86473ac0962a..aae622cb2862 100644 --- a/src/mono/sample/Android/Makefile +++ b/src/mono/sample/Android/Makefile @@ -1,8 +1,9 @@ MONO_CONFIG=Release -MONO_ARCH=x64 +MONO_ARCH?=x64 DOTNET := ../../../../dotnet.sh USE_LLVM=true AOT=false +DEPLOY_AND_RUN?=true all: runtimepack run @@ -14,7 +15,7 @@ run: /p:TargetArchitecture=$(MONO_ARCH) \ /p:TargetOS=Android \ /p:Configuration=$(MONO_CONFIG) \ - /p:DeployAndRun=true \ + /p:DeployAndRun=$(DEPLOY_AND_RUN) \ /p:ForceAOT=$(AOT) \ /p:UseLLVM=$(USE_LLVM) \ /p:RunActivity=false From 42499561311c46c878b9d92414c8d9466d744feb Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 31 Mar 2021 17:25:52 -0400 Subject: [PATCH 84/98] Avoid closure in ValidationContext if serviceProvider is null (#50483) --- .../System/ComponentModel/DataAnnotations/ValidationContext.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationContext.cs b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationContext.cs index f670db0d2350..836bf43ac9f7 100644 --- a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationContext.cs +++ b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationContext.cs @@ -93,7 +93,8 @@ public ValidationContext(object instance, IServiceProvider? serviceProvider, IDi if (serviceProvider != null) { - InitializeServiceProvider(serviceType => serviceProvider.GetService(serviceType)); + IServiceProvider localServiceProvider = serviceProvider; + InitializeServiceProvider(serviceType => localServiceProvider.GetService(serviceType)); } _items = items != null ? new Dictionary(items) : new Dictionary(); From bdadd59050d357965a83c69d0e7f9887ae0236fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Wed, 31 Mar 2021 23:39:24 +0200 Subject: [PATCH 85/98] Reenable Android.Device_Emulator.Aot.Test.csproj (#50332) * Reenable Android.Device_Emulator.Aot.Test.csproj Closes https://github.com/dotnet/runtime/issues/49757 * Update tests.proj --- src/libraries/tests.proj | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index 1442c9d3020f..0f5a52d6cb20 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -68,6 +68,11 @@ + + + + + @@ -347,20 +352,21 @@ - @@ -368,6 +374,7 @@ From ef8c5c70e3f3308f2a26ea16eef33caf80ab3c62 Mon Sep 17 00:00:00 2001 From: Natalia Kondratyeva Date: Wed, 31 Mar 2021 23:40:43 +0200 Subject: [PATCH 86/98] Fix msquic.dll loading (#50506) --- .../Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs index d13438cc12d6..5914cf200c65 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs @@ -122,7 +122,7 @@ private MsQuicApi(NativeApi* vtable) static MsQuicApi() { // TODO: Consider updating all of these delegates to instead use function pointers. - if (NativeLibrary.TryLoad(Interop.Libraries.MsQuic, out IntPtr msQuicHandle)) + if (NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out IntPtr msQuicHandle)) { try { From 1adf9adab7f128becc9cd3550f4a604f87d9a7bc Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 31 Mar 2021 21:46:37 +0000 Subject: [PATCH 87/98] [main] Update dependencies from dotnet/arcade dotnet/icu dotnet/xharness dotnet/llvm-project (#50416) [main] Update dependencies from dotnet/arcade dotnet/icu dotnet/xharness dotnet/llvm-project - Revert publishing branch changes https://github.com/dotnet/arcade/issues/6987 - reintroduce publishing branch workaround https://github.com/dotnet/arcade/issues/6987 --- .config/dotnet-tools.json | 2 +- eng/Version.Details.xml | 112 +++++++++++++++++++------------------- eng/Versions.props | 48 ++++++++-------- global.json | 8 +-- 4 files changed, 85 insertions(+), 85 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 691e8e80af85..28a90bad92d3 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "1.0.0-prerelease.21179.2", + "version": "1.0.0-prerelease.21180.1", "commands": [ "xharness" ] diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 7daac0a01812..84622c15dd0c 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,74 +1,74 @@ - + https://github.com/dotnet/icu - 370a34dfa95ba6f071aa315ee85749b21a263a96 + 29647ace51f6bb8085326ff137525f7a6d89d726 - + https://github.com/dotnet/arcade - 9a72efb067b74bb9147f9413ade6173b568ea1af + fd5f55c64d48b7894516cc841fba1253b2e79ffd - + https://github.com/dotnet/arcade - 9a72efb067b74bb9147f9413ade6173b568ea1af + fd5f55c64d48b7894516cc841fba1253b2e79ffd - + https://github.com/dotnet/arcade - 9a72efb067b74bb9147f9413ade6173b568ea1af + fd5f55c64d48b7894516cc841fba1253b2e79ffd - + https://github.com/dotnet/arcade - 9a72efb067b74bb9147f9413ade6173b568ea1af + fd5f55c64d48b7894516cc841fba1253b2e79ffd - + https://github.com/dotnet/arcade - 9a72efb067b74bb9147f9413ade6173b568ea1af + fd5f55c64d48b7894516cc841fba1253b2e79ffd - + https://github.com/dotnet/arcade - 9a72efb067b74bb9147f9413ade6173b568ea1af + fd5f55c64d48b7894516cc841fba1253b2e79ffd - + https://github.com/dotnet/arcade - 9a72efb067b74bb9147f9413ade6173b568ea1af + fd5f55c64d48b7894516cc841fba1253b2e79ffd - + https://github.com/dotnet/arcade - 9a72efb067b74bb9147f9413ade6173b568ea1af + fd5f55c64d48b7894516cc841fba1253b2e79ffd - + https://github.com/dotnet/arcade - 9a72efb067b74bb9147f9413ade6173b568ea1af + fd5f55c64d48b7894516cc841fba1253b2e79ffd - + https://github.com/dotnet/arcade - 9a72efb067b74bb9147f9413ade6173b568ea1af + fd5f55c64d48b7894516cc841fba1253b2e79ffd - + https://github.com/dotnet/arcade - 9a72efb067b74bb9147f9413ade6173b568ea1af + fd5f55c64d48b7894516cc841fba1253b2e79ffd - + https://github.com/dotnet/arcade - 9a72efb067b74bb9147f9413ade6173b568ea1af + fd5f55c64d48b7894516cc841fba1253b2e79ffd - + https://github.com/dotnet/arcade - 9a72efb067b74bb9147f9413ade6173b568ea1af + fd5f55c64d48b7894516cc841fba1253b2e79ffd - + https://github.com/dotnet/arcade - 9a72efb067b74bb9147f9413ade6173b568ea1af + fd5f55c64d48b7894516cc841fba1253b2e79ffd - + https://github.com/dotnet/arcade - 9a72efb067b74bb9147f9413ade6173b568ea1af + fd5f55c64d48b7894516cc841fba1253b2e79ffd - + https://github.com/dotnet/arcade - 9a72efb067b74bb9147f9413ade6173b568ea1af + fd5f55c64d48b7894516cc841fba1253b2e79ffd https://dev.azure.com/dnceng/internal/_git/dotnet-optimization @@ -130,37 +130,37 @@ https://github.com/dotnet/runtime-assets 055ed026132a7070e41629cfb5e410f0fcbdf948 - + https://github.com/dotnet/llvm-project - 53c10319be026ec370f293b342ca490026db5358 + a2bf9755287bdd3dcf998e41042e79dfd0136c85 - + https://github.com/dotnet/llvm-project - 53c10319be026ec370f293b342ca490026db5358 + a2bf9755287bdd3dcf998e41042e79dfd0136c85 - + https://github.com/dotnet/llvm-project - 53c10319be026ec370f293b342ca490026db5358 + a2bf9755287bdd3dcf998e41042e79dfd0136c85 - + https://github.com/dotnet/llvm-project - 53c10319be026ec370f293b342ca490026db5358 + a2bf9755287bdd3dcf998e41042e79dfd0136c85 - + https://github.com/dotnet/llvm-project - 53c10319be026ec370f293b342ca490026db5358 + a2bf9755287bdd3dcf998e41042e79dfd0136c85 - + https://github.com/dotnet/llvm-project - 53c10319be026ec370f293b342ca490026db5358 + a2bf9755287bdd3dcf998e41042e79dfd0136c85 - + https://github.com/dotnet/llvm-project - 53c10319be026ec370f293b342ca490026db5358 + a2bf9755287bdd3dcf998e41042e79dfd0136c85 - + https://github.com/dotnet/llvm-project - 53c10319be026ec370f293b342ca490026db5358 + a2bf9755287bdd3dcf998e41042e79dfd0136c85 https://github.com/dotnet/runtime @@ -198,17 +198,17 @@ https://github.com/mono/linker 26d9440e6683b44c4db5f45b25e9c857a6563bb8 - + https://github.com/dotnet/xharness - ae0f235b8c5110f94a21826de6faf9499a4e0c9b + 5c3ac06a6a3192222857d3a3683e838f91a9751d - + https://github.com/dotnet/xharness - ae0f235b8c5110f94a21826de6faf9499a4e0c9b + 5c3ac06a6a3192222857d3a3683e838f91a9751d - + https://github.com/dotnet/arcade - 9a72efb067b74bb9147f9413ade6173b568ea1af + fd5f55c64d48b7894516cc841fba1253b2e79ffd diff --git a/eng/Versions.props b/eng/Versions.props index 2e3ff3518a2f..35a8cd4ced1d 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -49,19 +49,19 @@ 3.9.0-5.final 3.9.0-5.final - 6.0.0-beta.21176.2 - 6.0.0-beta.21176.2 - 6.0.0-beta.21176.2 - 6.0.0-beta.21176.2 - 6.0.0-beta.21176.2 - 6.0.0-beta.21176.2 - 2.5.1-beta.21176.2 - 6.0.0-beta.21176.2 - 6.0.0-beta.21176.2 - 6.0.0-beta.21176.2 - 6.0.0-beta.21176.2 - 6.0.0-beta.21176.2 - 6.0.0-beta.21176.2 + 6.0.0-beta.21179.7 + 6.0.0-beta.21179.7 + 6.0.0-beta.21179.7 + 6.0.0-beta.21179.7 + 6.0.0-beta.21179.7 + 6.0.0-beta.21179.7 + 2.5.1-beta.21179.7 + 6.0.0-beta.21179.7 + 6.0.0-beta.21179.7 + 6.0.0-beta.21179.7 + 6.0.0-beta.21179.7 + 6.0.0-beta.21179.7 + 6.0.0-beta.21179.7 5.9.0-preview.2 @@ -148,8 +148,8 @@ 1.0.1-prerelease-00006 16.9.0-preview-20201201-01 - 1.0.0-prerelease.21179.2 - 1.0.0-prerelease.21179.2 + 1.0.0-prerelease.21180.1 + 1.0.0-prerelease.21180.1 2.4.1 2.4.2 1.3.0 @@ -161,16 +161,16 @@ 6.0.100-preview.2.21180.1 - 6.0.0-preview.4.21172.5 + 6.0.0-preview.4.21179.1 - 9.0.1-alpha.1.21172.2 - 9.0.1-alpha.1.21172.2 - 9.0.1-alpha.1.21172.2 - 9.0.1-alpha.1.21172.2 - 9.0.1-alpha.1.21172.2 - 9.0.1-alpha.1.21172.2 - 9.0.1-alpha.1.21172.2 - 9.0.1-alpha.1.21172.2 + 9.0.1-alpha.1.21179.2 + 9.0.1-alpha.1.21179.2 + 9.0.1-alpha.1.21179.2 + 9.0.1-alpha.1.21179.2 + 9.0.1-alpha.1.21179.2 + 9.0.1-alpha.1.21179.2 + 9.0.1-alpha.1.21179.2 + 9.0.1-alpha.1.21179.2 diff --git a/global.json b/global.json index a6e3436c1f79..28dd3bb990da 100644 --- a/global.json +++ b/global.json @@ -12,10 +12,10 @@ "python3": "3.7.1" }, "msbuild-sdks": { - "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21176.2", - "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21176.2", - "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21176.2", - "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21176.2", + "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21179.7", + "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21179.7", + "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21179.7", + "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21179.7", "Microsoft.Build.NoTargets": "2.0.17", "Microsoft.Build.Traversal": "2.1.1", "Microsoft.NET.Sdk.IL": "6.0.0-preview.4.21178.6" From 13c1b8ffc27ae2ef212b8409670ac5955d49c7ce Mon Sep 17 00:00:00 2001 From: Bill Wert Date: Wed, 31 Mar 2021 15:43:50 -0700 Subject: [PATCH 88/98] Fix linux test legs (#50518) --- eng/pipelines/coreclr/templates/perf-job.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/pipelines/coreclr/templates/perf-job.yml b/eng/pipelines/coreclr/templates/perf-job.yml index 5c9c2e4f4d91..d208f362fac0 100644 --- a/eng/pipelines/coreclr/templates/perf-job.yml +++ b/eng/pipelines/coreclr/templates/perf-job.yml @@ -44,7 +44,7 @@ jobs: logicalmachine: ${{ parameters.logicalmachine }} # Test job depends on the corresponding build job dependsOn: - - ${{ if eq(parameters.runtimeType, 'coreclr')}}: + - ${{ if ne(parameters.runtimeType, 'AndroidMono')}}: - ${{ format('coreclr_{0}_product_build_{1}{2}_{3}_{4}', parameters.runtimeVariant, parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.buildConfig) }} - ${{ if ne(parameters.liveLibrariesBuildConfig, '') }}: - ${{ format('libraries_build_{0}{1}_{2}_{3}', parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.liveLibrariesBuildConfig) }} @@ -94,7 +94,7 @@ jobs: displayName: 'live-built libraries' # Download coreclr - - ${{ if eq(parameters.runtimeType, 'coreclr') }}: + - ${{ if ne(parameters.runtimeType, 'AndroidMono') }}: - template: /eng/pipelines/common/download-artifact-step.yml parameters: unpackFolder: $(buildProductRootFolderPath) From e164551f1c96138521b4e58f14f8ac1e4369005d Mon Sep 17 00:00:00 2001 From: Bill Wert Date: Wed, 31 Mar 2021 15:54:45 -0700 Subject: [PATCH 89/98] fix XML tag (#50523) --- eng/common/performance/android_scenarios.proj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/common/performance/android_scenarios.proj b/eng/common/performance/android_scenarios.proj index 2f34947b07a0..d0850eab1bc0 100644 --- a/eng/common/performance/android_scenarios.proj +++ b/eng/common/performance/android_scenarios.proj @@ -30,5 +30,5 @@ $(Python) test.py sod --scenario-name "%(Identity)" $(Python) post.py - + \ No newline at end of file From 9e9fb064897f78cff51d602017cf5ddaa7971499 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 31 Mar 2021 20:05:10 -0400 Subject: [PATCH 90/98] Fix accidental closure in Regex.Replace (#50512) And sprinkle some "static" around to help avoid this in the future. --- .../src/System/Text/RegularExpressions/Regex.Replace.cs | 6 +++--- .../src/System/Text/RegularExpressions/Regex.Split.cs | 4 ++-- .../src/System/Text/RegularExpressions/RegexCharClass.cs | 2 +- .../src/System/Text/RegularExpressions/RegexCompiler.cs | 4 ++-- .../src/System/Text/RegularExpressions/RegexParser.cs | 2 +- .../src/System/Text/SegmentStringBuilder.cs | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Replace.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Replace.cs index f94730510072..b3dfbcb4ac57 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Replace.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Replace.cs @@ -174,7 +174,7 @@ private static string Replace(MatchEvaluator evaluator, Regex regex, string inpu if (!regex.RightToLeft) { - regex.Run(input, startat, ref state, (ref (SegmentStringBuilder segments, MatchEvaluator evaluator, int prevat, string input, int count) state, Match match) => + regex.Run(input, startat, ref state, static (ref (SegmentStringBuilder segments, MatchEvaluator evaluator, int prevat, string input, int count) state, Match match) => { state.segments.Add(state.input.AsMemory(state.prevat, match.Index - state.prevat)); state.prevat = match.Index + match.Length; @@ -193,11 +193,11 @@ private static string Replace(MatchEvaluator evaluator, Regex regex, string inpu { state.prevat = input.Length; - regex.Run(input, startat, ref state, (ref (SegmentStringBuilder segments, MatchEvaluator evaluator, int prevat, string input, int count) state, Match match) => + regex.Run(input, startat, ref state, static (ref (SegmentStringBuilder segments, MatchEvaluator evaluator, int prevat, string input, int count) state, Match match) => { state.segments.Add(state.input.AsMemory(match.Index + match.Length, state.prevat - match.Index - match.Length)); state.prevat = match.Index; - state.segments.Add(evaluator(match).AsMemory()); + state.segments.Add(state.evaluator(match).AsMemory()); return --state.count != 0; }, reuseMatchObject: false); diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Split.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Split.cs index ac3c4b32fc88..f223c4e6ed97 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Split.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Split.cs @@ -89,7 +89,7 @@ private static string[] Split(Regex regex, string input, int count, int startat) if (!regex.RightToLeft) { - regex.Run(input, startat, ref state, (ref (List results, int prevat, string input, int count) state, Match match) => + regex.Run(input, startat, ref state, static (ref (List results, int prevat, string input, int count) state, Match match) => { state.results.Add(state.input.Substring(state.prevat, match.Index - state.prevat)); state.prevat = match.Index + match.Length; @@ -117,7 +117,7 @@ private static string[] Split(Regex regex, string input, int count, int startat) { state.prevat = input.Length; - regex.Run(input, startat, ref state, (ref (List results, int prevat, string input, int count) state, Match match) => + regex.Run(input, startat, ref state, static (ref (List results, int prevat, string input, int count) state, Match match) => { state.results.Add(state.input.Substring(match.Index + match.Length, state.prevat - match.Index - match.Length)); state.prevat = match.Index; diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs index 3dc776fe6047..a569614032f0 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs @@ -547,7 +547,7 @@ public static string ConvertOldStringsToClass(string set, string category) strLength -= 2; } - return string.Create(strLength, (set, category, startsWithNulls), (span, state) => + return string.Create(strLength, (set, category, startsWithNulls), static (span, state) => { int index; diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs index 7316eb8a1073..c06b5af67c63 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs @@ -1368,7 +1368,7 @@ protected void GenerateFindFirstChar() { // Create a string to store the lookup table we use to find the offset. Debug.Assert(_boyerMoorePrefix.Pattern.Length <= char.MaxValue, "RegexBoyerMoore should have limited the size allowed."); - string negativeLookup = string.Create(negativeRange, (thisRef: this, beforefirst), (span, state) => + string negativeLookup = string.Create(negativeRange, (thisRef: this, beforefirst), static (span, state) => { // Store the offsets into the string. RightToLeft has negative offsets, so to support it with chars (unsigned), we negate // the values to be stored in the string, and then at run time after looking up the offset in the string, negate it again. @@ -5256,7 +5256,7 @@ void EmitCharInClass() // Generate the lookup table to store 128 answers as bits. We use a const string instead of a byte[] / static // data property because it lets IL emit handle all the details for us. - string bitVectorString = string.Create(8, (charClass, invariant), (dest, state) => // String length is 8 chars == 16 bytes == 128 bits. + string bitVectorString = string.Create(8, (charClass, invariant), static (dest, state) => // String length is 8 chars == 16 bytes == 128 bits. { for (int i = 0; i < 128; i++) { diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs index fae6d582421b..8885ae485f49 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs @@ -2134,7 +2134,7 @@ private void AddConcatenate(int pos, int cch, bool isReplacement) if (cch > 1) { string str = UseOptionI() && !isReplacement ? - string.Create(cch, (_pattern, _culture, pos, cch), (span, state) => state._pattern.AsSpan(state.pos, state.cch).ToLower(span, state._culture)) : + string.Create(cch, (_pattern, _culture, pos, cch), static (span, state) => state._pattern.AsSpan(state.pos, state.cch).ToLower(span, state._culture)) : _pattern.Substring(pos, cch); node = new RegexNode(RegexNode.Multi, _options, str); diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/SegmentStringBuilder.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/SegmentStringBuilder.cs index 87b729712046..eb19c8574858 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/SegmentStringBuilder.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/SegmentStringBuilder.cs @@ -74,7 +74,7 @@ public override string ToString() length += span[i].Length; } - string result = string.Create(length, this, (dest, builder) => + string result = string.Create(length, this, static (dest, builder) => { Span> localSpan = builder.AsSpan(); for (int i = 0; i < localSpan.Length; i++) From e5efbd8eea8078375d215642b3186bda0b01128c Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 31 Mar 2021 20:05:51 -0400 Subject: [PATCH 91/98] Avoid closure in WebSocketHandle.ConnectAsync (#50502) --- .../Net/WebSockets/WebSocketHandle.Managed.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs b/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs index d9c5adef82b1..d61f368e7aae 100644 --- a/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs +++ b/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs @@ -162,7 +162,18 @@ public async Task ConnectAsync(Uri uri, CancellationToken cancellationToken, Cli string[] subprotocolArray = (string[])subprotocolEnumerableValues; if (subprotocolArray.Length > 0 && !string.IsNullOrEmpty(subprotocolArray[0])) { - subprotocol = options.RequestedSubProtocols.Find(requested => string.Equals(requested, subprotocolArray[0], StringComparison.OrdinalIgnoreCase)); + if (options._requestedSubProtocols is not null) + { + foreach (string requestedProtocol in options._requestedSubProtocols) + { + if (requestedProtocol.Equals(subprotocolArray[0], StringComparison.OrdinalIgnoreCase)) + { + subprotocol = requestedProtocol; + break; + } + } + } + if (subprotocol == null) { throw new WebSocketException( From 78acffefca4e8ce9764fb8f93031c02c3f552178 Mon Sep 17 00:00:00 2001 From: Ulrich Weigand Date: Thu, 1 Apr 2021 02:06:05 +0200 Subject: [PATCH 92/98] Big-endian fix: Accessing OBJECTHEADER in ImageConverter (#50499) * Big-endian fix: Accessing OBJECTHEADER in ImageConverter The GetBitmapStream routine in ImageConverter is supposed to skip an embedded OBJECTHEADER structure. However, this structure needs to be read in little-endian mode, not in native byte order. Fixes the ImageConverterTest.ImageWithOleHeader test on big-endian systems. * Update src/libraries/System.Drawing.Common/src/System/Drawing/ImageConverter.cs Co-authored-by: Stephen Toub --- .../src/System/Drawing/ImageConverter.cs | 44 ++++++++++--------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/ImageConverter.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/ImageConverter.cs index 710ce1d4381b..9836fbfc9a38 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/ImageConverter.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/ImageConverter.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers.Binary; using System.ComponentModel; using System.Diagnostics; using System.Drawing.Imaging; @@ -112,20 +113,38 @@ public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContex { try { - short signature = MemoryMarshal.Read(rawData); + short signature = BinaryPrimitives.ReadInt16LittleEndian(rawData); if (signature != 0x1c15) { return null; } - // The data is in the form of OBJECTHEADER. It's an encoded format that Access uses to push imagesinto the DB. - OBJECTHEADER pHeader = MemoryMarshal.Read(rawData); + // The data is in the form of OBJECTHEADER. It's an encoded format that Access uses to push images into the DB. + // + // The layout of OBJECTHEADER is as follows - we only need the signature + // and headersize fields, which need to be read as little-endian data: + // + // [StructLayout(LayoutKind.Sequential)] + // private struct OBJECTHEADER + // { + // public short signature; // it's always 0x1c15 + // public short headersize; + // public short objectType; + // public short nameLen; + // public short classLen; + // public short nameOffset; + // public short classOffset; + // public short width; + // public short height; + // public IntPtr pInfo; + // } + short headersize = BinaryPrimitives.ReadInt16LittleEndian(rawData.Slice(2, 2)); // pHeader.signature will always be 0x1c15. // "PBrush" should be the 6 chars after position 12 as well. - if (rawData.Length <= pHeader.headersize + 18 || - !rawData.Slice(pHeader.headersize + 12, 6).SequenceEqual(PBrush)) + if (rawData.Length <= headersize + 18 || + !rawData.Slice(headersize + 12, 6).SequenceEqual(PBrush)) { return null; } @@ -143,20 +162,5 @@ public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContex return null; } - - [StructLayout(LayoutKind.Sequential)] - private struct OBJECTHEADER - { - public short signature; // it's always 0x1c15 - public short headersize; - public short objectType; - public short nameLen; - public short classLen; - public short nameOffset; - public short classOffset; - public short width; - public short height; - public IntPtr pInfo; - } } } From a6d74722fe1d7dd4f5c5ef386b2ac11b4592ab2b Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 31 Mar 2021 20:06:35 -0400 Subject: [PATCH 93/98] Update dependencies from https://github.com/mono/linker build 20210330.4 (#50481) Microsoft.NET.ILLink.Tasks From Version 6.0.100-preview.2.21180.1 -> To Version 6.0.100-preview.2.21180.4 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 84622c15dd0c..34bcbbe46e0e 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -194,9 +194,9 @@ https://github.com/dotnet/runtime 102d1e856c7e0e553abeec937783da5debed73ad - + https://github.com/mono/linker - 26d9440e6683b44c4db5f45b25e9c857a6563bb8 + dd7d70118b7146125781c830bbff47a8cb953f39 https://github.com/dotnet/xharness diff --git a/eng/Versions.props b/eng/Versions.props index 35a8cd4ced1d..cb62f56ddcf8 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -159,7 +159,7 @@ 5.0.0-preview-20201009.2 - 6.0.100-preview.2.21180.1 + 6.0.100-preview.2.21180.4 6.0.0-preview.4.21179.1 From c474b6e103d93fdbce421eb7019022670d2845e6 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Wed, 31 Mar 2021 17:06:50 -0700 Subject: [PATCH 94/98] Stop skipping entire System.Net.Http functional test suite on Android (#50509) It is skipped based on flakiness/slowness on a Checked/Debug runtime, which we are not currently using for Android tests. Put the test suite under Android test ProjectExclusions instead. --- .../System.Net.Http/tests/FunctionalTests/AssemblyInfo.cs | 2 +- src/libraries/tests.proj | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/AssemblyInfo.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/AssemblyInfo.cs index c3b2610bde76..eb15585b4c93 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/AssemblyInfo.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/AssemblyInfo.cs @@ -5,5 +5,5 @@ using Xunit; [assembly: SkipOnCoreClr("System.Net.Tests are flaky and/or long running: https://github.com/dotnet/runtime/issues/131", RuntimeConfiguration.Checked)] -[assembly: ActiveIssue("https://github.com/dotnet/runtime/issues/131", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsNotBrowser))] // System.Net.Tests are flaky and/or long running +[assembly: ActiveIssue("https://github.com/dotnet/runtime/issues/131", ~(TestPlatforms.Android | TestPlatforms.Browser), TargetFrameworkMonikers.Any, TestRuntimes.Mono)] // System.Net.Tests are flaky and/or long running diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index 0f5a52d6cb20..8befb7650e68 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -56,6 +56,7 @@ + From 9064eed7e0e3416b65ec6fb9e8b172ef1bea919a Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Wed, 31 Mar 2021 17:08:42 -0700 Subject: [PATCH 95/98] Add ExceptionDispatchInfo.SetRemoteStackTrace (#50392) --- .../src/System/Exception.CoreCLR.cs | 17 ++++------ .../src/System/Exception.cs | 30 +++++++++++++++++ .../ExceptionDispatchInfo.cs | 33 ++++++++++++++++++- .../System.Runtime/ref/System.Runtime.cs | 1 + .../ExceptionDispatchInfoTests.cs | 29 +++++++++++++--- .../src/System/Exception.Mono.cs | 18 ++++------ 6 files changed, 100 insertions(+), 28 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Exception.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Exception.CoreCLR.cs index d6bb435387b0..81b9be41cdf8 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Exception.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Exception.CoreCLR.cs @@ -7,7 +7,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; -using System.Text; namespace System { @@ -323,14 +322,16 @@ internal DispatchState CaptureDispatchState() _remoteStackTraceString, _ipForWatsonBuckets, _watsonBuckets); } - [StackTraceHidden] - internal void SetCurrentStackTrace() + // Returns true if setting the _remoteStackTraceString field is legal, false if not (immutable exception). + // A false return value means the caller should early-exit the operation. + // Can also throw InvalidOperationException if a stack trace is already set or if object has been thrown. + private bool CanSetRemoteStackTrace() { // If this is a preallocated singleton exception, silently skip the operation, // regardless of the value of throwIfHasExistingStack. if (IsImmutableAgileException(this)) { - return; + return false; } // Check to see if the exception already has a stack set in it. @@ -339,13 +340,7 @@ internal void SetCurrentStackTrace() ThrowHelper.ThrowInvalidOperationException(); } - // Store the current stack trace into the "remote" stack trace, which was originally introduced to support - // remoting of exceptions cross app-domain boundaries, and is thus concatenated into Exception.StackTrace - // when it's retrieved. - var sb = new StringBuilder(256); - new StackTrace(fNeedFileInfo: true).ToString(System.Diagnostics.StackTrace.TraceFormat.TrailingNewLine, sb); - sb.AppendLine(SR.Exception_EndStackTraceFromPreviousThrow); - _remoteStackTraceString = sb.ToString(); + return true; } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Exception.cs b/src/libraries/System.Private.CoreLib/src/System/Exception.cs index 14e84a603cce..cef527ee50d1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Exception.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Exception.cs @@ -4,6 +4,7 @@ using System.Collections; using System.Diagnostics; using System.Runtime.Serialization; +using System.Text; namespace System { @@ -197,5 +198,34 @@ public int HResult public new Type GetType() => base.GetType(); partial void RestoreRemoteStackTrace(SerializationInfo info, StreamingContext context); + + [StackTraceHidden] + internal void SetCurrentStackTrace() + { + if (!CanSetRemoteStackTrace()) + { + return; // early-exit + } + + // Store the current stack trace into the "remote" stack trace, which was originally introduced to support + // remoting of exceptions cross app-domain boundaries, and is thus concatenated into Exception.StackTrace + // when it's retrieved. + var sb = new StringBuilder(256); + new StackTrace(fNeedFileInfo: true).ToString(System.Diagnostics.StackTrace.TraceFormat.TrailingNewLine, sb); + sb.AppendLine(SR.Exception_EndStackTraceFromPreviousThrow); + _remoteStackTraceString = sb.ToString(); + } + + internal void SetRemoteStackTrace(string stackTrace) + { + if (!CanSetRemoteStackTrace()) + { + return; // early-exit + } + + // Store the provided text into the "remote" stack trace, following the same format SetCurrentStackTrace + // would have generated. + _remoteStackTraceString = stackTrace + Environment.NewLineConst + SR.Exception_EndStackTraceFromPreviousThrow + Environment.NewLineConst; + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionDispatchInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionDispatchInfo.cs index 18c72613056b..d89ef99c9ac7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionDispatchInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionDispatchInfo.cs @@ -65,7 +65,7 @@ public void Throw() /// Stores the current stack trace into the specified instance. /// The unthrown instance. /// The argument was null. - /// The argument was previously thrown or previously had a stack trace stored into it.. + /// The argument was previously thrown or previously had a stack trace stored into it. /// The exception instance. [StackTraceHidden] public static Exception SetCurrentStackTrace(Exception source) @@ -79,5 +79,36 @@ public static Exception SetCurrentStackTrace(Exception source) return source; } + + /// + /// Stores the provided stack trace into the specified instance. + /// + /// The unthrown instance. + /// The stack trace string to persist within . This is normally acquired + /// from the property from the remote exception instance. + /// The or argument was null. + /// The argument was previously thrown or previously had a stack trace stored into it. + /// The exception instance. + /// + /// This method populates the property from an arbitrary string value. + /// The typical use case is the transmission of objects across processes with high fidelity, + /// allowing preservation of the exception object's stack trace information. .NET does not attempt to parse the + /// provided string value. The caller is responsible for normalizing line endings if required. + /// + public static Exception SetRemoteStackTrace(Exception source, string stackTrace) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + if (stackTrace is null) + { + throw new ArgumentNullException(nameof(stackTrace)); + } + + source.SetRemoteStackTrace(stackTrace); + + return source; + } } } diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 97f212162c9d..0bfd76c940dc 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -9782,6 +9782,7 @@ internal ExceptionDispatchInfo() { } public System.Exception SourceException { get { throw null; } } public static System.Runtime.ExceptionServices.ExceptionDispatchInfo Capture(System.Exception source) { throw null; } public static System.Exception SetCurrentStackTrace(System.Exception source) { throw null; } + public static System.Exception SetRemoteStackTrace(System.Exception source, string stackTrace) { throw null; } [System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] public void Throw() => throw null; [System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] diff --git a/src/libraries/System.Runtime/tests/System/Runtime/ExceptionServices/ExceptionDispatchInfoTests.cs b/src/libraries/System.Runtime/tests/System/Runtime/ExceptionServices/ExceptionDispatchInfoTests.cs index c2bb2bdc48cd..e293a1814f29 100644 --- a/src/libraries/System.Runtime/tests/System/Runtime/ExceptionServices/ExceptionDispatchInfoTests.cs +++ b/src/libraries/System.Runtime/tests/System/Runtime/ExceptionServices/ExceptionDispatchInfoTests.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.ComponentModel; +using System.Diagnostics; using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using Xunit; @@ -29,23 +29,27 @@ public static void StaticThrow_UpdatesStackTraceAppropriately() } [Fact] - public static void SetCurrentStackTrace_Invalid_Throws() + public static void SetCurrentOrRemoteStackTrace_Invalid_Throws() { Exception e; // Null argument e = null; AssertExtensions.Throws("source", () => ExceptionDispatchInfo.SetCurrentStackTrace(e)); + AssertExtensions.Throws("source", () => ExceptionDispatchInfo.SetRemoteStackTrace(e, "Hello")); + AssertExtensions.Throws("stackTrace", () => ExceptionDispatchInfo.SetRemoteStackTrace(new Exception(), stackTrace: null)); // Previously set current stack e = new Exception(); ExceptionDispatchInfo.SetCurrentStackTrace(e); Assert.Throws(() => ExceptionDispatchInfo.SetCurrentStackTrace(e)); + Assert.Throws(() => ExceptionDispatchInfo.SetRemoteStackTrace(e, "Hello")); // Previously thrown e = new Exception(); try { throw e; } catch { } Assert.Throws(() => ExceptionDispatchInfo.SetCurrentStackTrace(e)); + Assert.Throws(() => ExceptionDispatchInfo.SetRemoteStackTrace(e, "Hello")); } [Fact] @@ -55,12 +59,29 @@ public static void SetCurrentStackTrace_IncludedInExceptionStackTrace() e = new Exception(); ABCDEFGHIJKLMNOPQRSTUVWXYZ(e); - Assert.Contains(nameof(ABCDEFGHIJKLMNOPQRSTUVWXYZ), e.StackTrace); + Assert.Contains(nameof(ABCDEFGHIJKLMNOPQRSTUVWXYZ), e.StackTrace, StringComparison.Ordinal); e = new Exception(); ABCDEFGHIJKLMNOPQRSTUVWXYZ(e); try { throw e; } catch { } - Assert.Contains(nameof(ABCDEFGHIJKLMNOPQRSTUVWXYZ), e.StackTrace); + Assert.Contains(nameof(ABCDEFGHIJKLMNOPQRSTUVWXYZ), e.StackTrace, StringComparison.Ordinal); + } + + [Fact] + public static void SetRemoteStackTrace_IncludedInExceptionStackTrace() + { + Exception e; + + e = new Exception(); + Assert.Same(e, ExceptionDispatchInfo.SetRemoteStackTrace(e, "pumpkin-anaconda-maritime")); // 3 randomly selected words + Assert.Contains("pumpkin-anaconda-maritime", e.StackTrace, StringComparison.Ordinal); + Assert.DoesNotContain("pumpkin-anaconda-maritime", new StackTrace(e).ToString(), StringComparison.Ordinal); // we shouldn't attempt to parse it in a StackTrace object + + e = new Exception(); + Assert.Same(e, ExceptionDispatchInfo.SetRemoteStackTrace(e, "pumpkin-anaconda-maritime")); + try { throw e; } catch { } + Assert.Contains("pumpkin-anaconda-maritime", e.StackTrace, StringComparison.Ordinal); + Assert.DoesNotContain("pumpkin-anaconda-maritime", new StackTrace(e).ToString(), StringComparison.Ordinal); } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] diff --git a/src/mono/System.Private.CoreLib/src/System/Exception.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Exception.Mono.cs index 5e369825827b..bb38f05e371b 100644 --- a/src/mono/System.Private.CoreLib/src/System/Exception.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Exception.Mono.cs @@ -2,10 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections; -using System.Reflection; using System.Diagnostics; +using System.Reflection; using System.Runtime.InteropServices; -using System.Text; namespace System { @@ -102,22 +101,17 @@ internal void RestoreDispatchState(in DispatchState state) _stackTraceString = null; } - [StackTraceHidden] - internal void SetCurrentStackTrace() + // Returns true if setting the _remoteStackTraceString field is legal, false if not (immutable exception). + // A false return value means the caller should early-exit the operation. + // Can also throw InvalidOperationException if a stack trace is already set or if object has been thrown. + private bool CanSetRemoteStackTrace() { - // Check to see if the exception already has a stack set in it. if (_traceIPs != null || _stackTraceString != null || _remoteStackTraceString != null) { ThrowHelper.ThrowInvalidOperationException(); } - // Store the current stack trace into the "remote" stack trace, which was originally introduced to support - // remoting of exceptions cross app-domain boundaries, and is thus concatenated into Exception.StackTrace - // when it's retrieved. - var sb = new StringBuilder(256); - new StackTrace(fNeedFileInfo: true).ToString(Diagnostics.StackTrace.TraceFormat.TrailingNewLine, sb); - sb.AppendLine(SR.Exception_EndStackTraceFromPreviousThrow); - _remoteStackTraceString = sb.ToString(); + return true; // mono runtime doesn't have immutable agile exceptions, always return true } private string? CreateSourceName() From 00d4dd86518280dbb9a1726ef0bd036dbd273e11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Thu, 1 Apr 2021 02:10:14 +0200 Subject: [PATCH 96/98] Cleanup name of test exclusion itemgroup (#50513) Follow up from feedback in https://github.com/dotnet/runtime/pull/50332 --- src/libraries/tests.proj | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index 8befb7650e68..2157714a38f4 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -71,7 +71,7 @@ - + @@ -353,13 +353,13 @@ @@ -367,7 +367,7 @@ @@ -375,7 +375,7 @@ From e9f004b519022bca62fe900e53897bad71974b55 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Wed, 31 Mar 2021 18:17:19 -0700 Subject: [PATCH 97/98] Fix build breaks - Add Exception._remoteStackTrace --- .../src/System/Exception.CoreCLR.cs | 4 -- .../src/Resources/Strings.resx | 3 ++ .../System/Diagnostics/StackTrace.CoreRT.cs | 7 +--- .../src/System/Exception.CoreRT.cs | 41 +++++++++++++------ .../src/System.Private.TypeLoader.csproj | 6 +++ .../Logging/DocumentationSignatureParser.cs | 2 +- .../TypeSystem/Common/ResolutionFailure.cs | 2 +- .../IL/Stubs/DynamicInvokeMethodThunk.cs | 2 +- .../IL/TypeSystemContext.GeneratedAssembly.cs | 8 +--- .../BlockedInternalsBlockingPolicy.cs | 8 ++-- .../ReflectionMethodBodyScanner.cs | 4 +- .../Compiler/FeatureSwitchManager.cs | 2 +- .../Compiler/LibraryInitializers.cs | 2 +- .../Compiler/PreinitializationManager.cs | 2 +- .../Compiler/PropertyPseudoDesc.cs | 2 +- .../ILCompiler/Metadata/Transform.Property.cs | 2 +- .../ILCompiler.TypeSystem.csproj | 6 +++ .../src/System/Diagnostics/StackTrace.cs | 2 +- .../src/System/Exception.cs | 3 +- .../src/System/Exception.Mono.cs | 1 - 20 files changed, 64 insertions(+), 45 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Exception.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Exception.CoreCLR.cs index 81b9be41cdf8..ebd4660a4bc3 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Exception.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Exception.CoreCLR.cs @@ -14,8 +14,6 @@ public partial class Exception : ISerializable { partial void RestoreRemoteStackTrace(SerializationInfo info, StreamingContext context) { - _remoteStackTraceString = info.GetString("RemoteStackTraceString"); // Do not rename (binary serialization) - // Get the WatsonBuckets that were serialized - this is particularly // done to support exceptions going across AD transitions. // @@ -245,8 +243,6 @@ internal void RestoreDispatchState(in DispatchState dispatchState) // See src\inc\corexcep.h's EXCEPTION_COMPLUS definition: private const int _COMPlusExceptionCode = unchecked((int)0xe0434352); // Win32 exception code for COM+ exceptions - private string? SerializationRemoteStackTraceString => _remoteStackTraceString; - private object? SerializationWatsonBuckets => _watsonBuckets; private string? SerializationStackTraceString diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Resources/Strings.resx b/src/coreclr/nativeaot/System.Private.CoreLib/src/Resources/Strings.resx index c65ac77e478f..0a35cbd8d6e0 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Resources/Strings.resx @@ -1137,6 +1137,9 @@ --- End of inner exception stack trace --- + + --- End of stack trace from previous location --- + Exception of type '{0}' was thrown. diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/StackTrace.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/StackTrace.CoreRT.cs index 2c988ad10652..c35ae4ec9e36 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/StackTrace.CoreRT.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/StackTrace.CoreRT.cs @@ -89,14 +89,13 @@ private void InitializeForIpAddressArray(IntPtr[] ipAddresses, int skipFrames, i } #if !TARGET_WASM - internal string ToString(TraceFormat traceFormat) + internal void ToString(TraceFormat traceFormat, StringBuilder builder) { if (_stackFrames == null) { - return ""; + return; } - StringBuilder builder = new StringBuilder(); foreach (StackFrame frame in _stackFrames) { frame.AppendToStackTrace(builder); @@ -104,8 +103,6 @@ internal string ToString(TraceFormat traceFormat) if (traceFormat == TraceFormat.Normal && builder.Length >= Environment.NewLine.Length) builder.Length -= Environment.NewLine.Length; - - return builder.ToString(); } #endif } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Exception.CoreRT.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Exception.CoreRT.cs index f76d022b179e..c05b05ebc770 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Exception.CoreRT.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Exception.CoreRT.cs @@ -29,7 +29,6 @@ public MethodBase TargetSite private IDictionary CreateDataContainer() => new ListDictionaryInternal(); private string SerializationStackTraceString => StackTrace; - private string SerializationRemoteStackTraceString => null; private string SerializationWatsonBuckets => null; private string CreateSourceName() => HasBeenThrown ? "" : null; @@ -46,21 +45,29 @@ public MethodBase TargetSite private int _HResult; // HResult // To maintain compatibility across runtimes, if this object was deserialized, it will store its stack trace as a string - private string _stackTraceString; + private string? _stackTraceString; + private string? _remoteStackTraceString; // Returns the stack trace as a string. If no stack trace is // available, null is returned. - public virtual string StackTrace + public virtual string? StackTrace { get { - if (_stackTraceString != null) - return _stackTraceString; + string? stackTraceString = _stackTraceString; + string? remoteStackTraceString = _remoteStackTraceString; + // if no stack trace, try to get one + if (stackTraceString != null) + { + return remoteStackTraceString + stackTraceString; + } if (!HasBeenThrown) - return null; + { + return remoteStackTraceString; + } - return StackTraceHelper.FormatStackTrace(GetStackIPs(), true); + return remoteStackTraceString + StackTraceHelper.FormatStackTrace(GetStackIPs(), true); } } @@ -232,12 +239,6 @@ public DispatchState(IntPtr[] stackTrace) } } - [StackTraceHidden] - internal void SetCurrentStackTrace() - { - // TODO: Exception.SetCurrentStackTrace - } - // This is the object against which a lock will be taken // when attempt to restore the EDI. Since its static, its possible // that unrelated exception object restorations could get blocked @@ -289,5 +290,19 @@ internal unsafe byte[] SerializeForDump() return buffer; } } + + // Returns true if setting the _remoteStackTraceString field is legal, false if not (immutable exception). + // A false return value means the caller should early-exit the operation. + // Can also throw InvalidOperationException if a stack trace is already set or if object has been thrown. + private bool CanSetRemoteStackTrace() + { + // Check to see if the exception already has a stack set in it. + if (HasBeenThrown || _stackTraceString != null || _remoteStackTraceString != null) + { + ThrowHelper.ThrowInvalidOperationException(); + } + + return true; // CoreRT runtime doesn't have immutable agile exceptions, always return true + } } } diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj b/src/coreclr/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj index 596dc2a75b66..9a820f0fe213 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj @@ -210,6 +210,12 @@ Internal\TypeSystem\ModuleDesc.cs + + TypeSystem\Common\NotFoundBehavior.cs + + + TypeSystem\Common\ResolutionFailure.cs + Internal\TypeSystem\ParameterizedType.cs diff --git a/src/coreclr/tools/Common/Compiler/Logging/DocumentationSignatureParser.cs b/src/coreclr/tools/Common/Compiler/Logging/DocumentationSignatureParser.cs index d73aea0ef87b..70cf77cb4374 100644 --- a/src/coreclr/tools/Common/Compiler/Logging/DocumentationSignatureParser.cs +++ b/src/coreclr/tools/Common/Compiler/Logging/DocumentationSignatureParser.cs @@ -549,7 +549,7 @@ static void GetMatchingTypes(ModuleDesc module, TypeDesc declaringType, string n namepart = name; } - var type = module.GetType(namespacepart, namepart, throwIfNotFound: false); + var type = module.GetType(namespacepart, namepart, NotFoundBehavior.ReturnNull); if (type != null) { results.Add(type); diff --git a/src/coreclr/tools/Common/TypeSystem/Common/ResolutionFailure.cs b/src/coreclr/tools/Common/TypeSystem/Common/ResolutionFailure.cs index 04d75a5e5ae9..882537e27f73 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/ResolutionFailure.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/ResolutionFailure.cs @@ -84,7 +84,7 @@ public static ResolutionFailure GetAssemblyResolutionFailure(string simpleName) public void Throw() { - switch(_failureType) + switch (_failureType) { case FailureType.TypeLoadException1: ThrowHelper.ThrowTypeLoadException(_name, _module); diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DynamicInvokeMethodThunk.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DynamicInvokeMethodThunk.cs index 55e562c0a152..af07693af6f0 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DynamicInvokeMethodThunk.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DynamicInvokeMethodThunk.cs @@ -33,7 +33,7 @@ public DynamicInvokeMethodThunk(TypeDesc owningType, DynamicInvokeMethodSignatur internal static bool SupportsDynamicInvoke(TypeSystemContext context) { - return context.SystemModule.GetType("System", "InvokeUtils", false) != null; + return context.SystemModule.GetType("System", "InvokeUtils", NotFoundBehavior.ReturnNull) != null; } private static TypeDesc UnwrapByRef(TypeDesc type) diff --git a/src/coreclr/tools/Common/TypeSystem/IL/TypeSystemContext.GeneratedAssembly.cs b/src/coreclr/tools/Common/TypeSystem/IL/TypeSystemContext.GeneratedAssembly.cs index 4126aba76d9d..834fb18deeb7 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/TypeSystemContext.GeneratedAssembly.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/TypeSystemContext.GeneratedAssembly.cs @@ -55,14 +55,10 @@ public AssemblyName GetName() return new AssemblyName("System.Private.CompilerGenerated"); } - public override MetadataType GetType(string nameSpace, string name, bool throwIfNotFound = true) + public override MetadataType GetType(string nameSpace, string name, NotFoundBehavior notFoundBehavior) { Debug.Fail("Resolving a TypeRef in the compiler generated assembly?"); - - if (throwIfNotFound) - ThrowHelper.ThrowTypeLoadException(nameSpace, name, this); - - return null; + throw new NotImplementedException(); } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/BlockedInternalsBlockingPolicy.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/BlockedInternalsBlockingPolicy.cs index b12d22202531..f9dbc66d91fd 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/BlockedInternalsBlockingPolicy.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/BlockedInternalsBlockingPolicy.cs @@ -46,11 +46,11 @@ protected override ModuleBlockingState CreateValueFromKey(ModuleDesc module) { ModuleBlockingMode blockingMode = ModuleBlockingMode.None; - if (module.GetType("System.Runtime.CompilerServices", "__BlockAllReflectionAttribute", false) != null) + if (module.GetType("System.Runtime.CompilerServices", "__BlockAllReflectionAttribute", NotFoundBehavior.ReturnNull) != null) { blockingMode = ModuleBlockingMode.FullyBlocked; } - else if (module.GetType("System.Runtime.CompilerServices", "__BlockReflectionAttribute", false) != null) + else if (module.GetType("System.Runtime.CompilerServices", "__BlockReflectionAttribute", NotFoundBehavior.ReturnNull) != null) { blockingMode = ModuleBlockingMode.BlockedInternals; } @@ -152,8 +152,8 @@ public BlockedInternalsBlockingPolicy(TypeSystemContext context) { _blockedTypes = new BlockedTypeHashtable(_blockedModules); - ArrayOfTType = context.SystemModule.GetType("System", "Array`1", false); - AttributeType = context.SystemModule.GetType("System", "Attribute", false); + ArrayOfTType = context.SystemModule.GetType("System", "Array`1", NotFoundBehavior.ReturnNull); + AttributeType = context.SystemModule.GetType("System", "Attribute", NotFoundBehavior.ReturnNull); } public override bool IsBlocked(MetadataType type) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionMethodBodyScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionMethodBodyScanner.cs index cd7f469fe4f6..91d3278e2b6f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionMethodBodyScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionMethodBodyScanner.cs @@ -69,13 +69,13 @@ public static bool ResolveType(string name, ModuleDesc callingModule, TypeSystem return false; // Resolve type in the assembly - type = referenceModule.GetType(typeNamespace.ToString(), typeName.ToString(), false); + type = referenceModule.GetType(typeNamespace.ToString(), typeName.ToString(), NotFoundBehavior.ReturnNull); // If it didn't resolve and wasn't assembly-qualified, we also try core library if (type == null && assemblyName.Length == 0) { referenceModule = context.SystemModule; - type = referenceModule.GetType(typeNamespace.ToString(), typeName.ToString(), false); + type = referenceModule.GetType(typeNamespace.ToString(), typeName.ToString(), NotFoundBehavior.ReturnNull); } return type != null; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/FeatureSwitchManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/FeatureSwitchManager.cs index 5a61c2428e48..5f06913b97d3 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/FeatureSwitchManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/FeatureSwitchManager.cs @@ -665,7 +665,7 @@ public SubstitutedMethodIL(MethodIL wrapped, byte[] body, ILExceptionRegion[] eh public override ILExceptionRegion[] GetExceptionRegions() => _ehRegions; public override byte[] GetILBytes() => _body; public override LocalVariableDefinition[] GetLocals() => _wrappedMethodIL.GetLocals(); - public override object GetObject(int token) => _wrappedMethodIL.GetObject(token); + public override object GetObject(int token, NotFoundBehavior notFoundBehavior) => _wrappedMethodIL.GetObject(token, notFoundBehavior); public override MethodDebugInformation GetDebugInfo() => _debugInfo; } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LibraryInitializers.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LibraryInitializers.cs index 35ffad88e638..bdd7f6d1465e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LibraryInitializers.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LibraryInitializers.cs @@ -48,7 +48,7 @@ private void InitLibraryInitializers() foreach (var assembly in _librariesWithInitializers) { - TypeDesc containingType = assembly.GetType(LibraryInitializerContainerNamespaceName, LibraryInitializerContainerTypeName, false); + TypeDesc containingType = assembly.GetType(LibraryInitializerContainerNamespaceName, LibraryInitializerContainerTypeName, NotFoundBehavior.ReturnNull); if (containingType == null) continue; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/PreinitializationManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/PreinitializationManager.cs index 250180960615..1440991c3278 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/PreinitializationManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/PreinitializationManager.cs @@ -17,7 +17,7 @@ public class PreinitializationManager public PreinitializationManager(TypeSystemContext context, CompilationModuleGroup compilationGroup, ILProvider ilprovider, bool enableInterpreter) { - _supportsLazyCctors = context.SystemModule.GetType("System.Runtime.CompilerServices", "ClassConstructorRunner", false) != null; + _supportsLazyCctors = context.SystemModule.GetType("System.Runtime.CompilerServices", "ClassConstructorRunner", NotFoundBehavior.ReturnNull) != null; _preinitHashTable = new PreinitializationInfoHashtable(compilationGroup, ilprovider); _enableInterpreter = enableInterpreter; } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/PropertyPseudoDesc.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/PropertyPseudoDesc.cs index 75e8fafb8563..3113c629926c 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/PropertyPseudoDesc.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/PropertyPseudoDesc.cs @@ -21,7 +21,7 @@ public class PropertyPseudoDesc : TypeSystemEntity private PropertyDefinition Definition => _type.MetadataReader.GetPropertyDefinition(_handle); public PropertySignature Signature => - new EcmaSignatureParser(_type.EcmaModule, _type.MetadataReader.GetBlobReader(Definition.Signature)) + new EcmaSignatureParser(_type.EcmaModule, _type.MetadataReader.GetBlobReader(Definition.Signature), NotFoundBehavior.Throw) .ParsePropertySignature(); public MethodDesc GetMethod diff --git a/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Property.cs b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Property.cs index f01f4726bbf0..b0c694e4a25a 100644 --- a/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Property.cs +++ b/src/coreclr/tools/aot/ILCompiler.MetadataTransform/ILCompiler/Metadata/Transform.Property.cs @@ -33,7 +33,7 @@ private Property HandleProperty(Cts.Ecma.EcmaModule module, Ecma.PropertyDefinit return null; Ecma.BlobReader sigBlobReader = reader.GetBlobReader(propDef.Signature); - Cts.PropertySignature sig = new Cts.Ecma.EcmaSignatureParser(module, sigBlobReader).ParsePropertySignature(); + Cts.PropertySignature sig = new Cts.Ecma.EcmaSignatureParser(module, sigBlobReader, Cts.NotFoundBehavior.Throw).ParsePropertySignature(); Property result = new Property { diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj b/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj index 10fdc028e500..4aa53fd19c5e 100644 --- a/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj +++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj @@ -135,6 +135,12 @@ TypeSystem\Common\ModuleDesc.cs + + TypeSystem\Common\NotFoundBehavior.cs + + + TypeSystem\Common\ResolutionFailure.cs + TypeSystem\Common\TypeSystemEntity.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs index 3c6ba961c79d..47d5b71a306e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs @@ -187,7 +187,6 @@ internal enum TraceFormat TrailingNewLine, // include a trailing new line character } -#if !CORERT /// /// Builds a readable representation of the stack trace, specifying /// the format for backwards compatibility. @@ -199,6 +198,7 @@ internal string ToString(TraceFormat traceFormat) return sb.ToString(); } +#if !CORERT internal void ToString(TraceFormat traceFormat, StringBuilder sb) { // Passing a default string for "at" in case SR.UsingResourceKeys() is true diff --git a/src/libraries/System.Private.CoreLib/src/System/Exception.cs b/src/libraries/System.Private.CoreLib/src/System/Exception.cs index cef527ee50d1..c53561a81713 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Exception.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Exception.cs @@ -47,6 +47,7 @@ protected Exception(SerializationInfo info, StreamingContext context) _innerException = (Exception?)(info.GetValue("InnerException", typeof(Exception))); // Do not rename (binary serialization) _helpURL = info.GetString("HelpURL"); // Do not rename (binary serialization) _stackTraceString = info.GetString("StackTraceString"); // Do not rename (binary serialization) + _remoteStackTraceString = info.GetString("RemoteStackTraceString"); // Do not rename (binary serialization) _HResult = info.GetInt32("HResult"); // Do not rename (binary serialization) _source = info.GetString("Source"); // Do not rename (binary serialization) @@ -110,7 +111,7 @@ public virtual void GetObjectData(SerializationInfo info, StreamingContext conte info.AddValue("InnerException", _innerException, typeof(Exception)); // Do not rename (binary serialization) info.AddValue("HelpURL", _helpURL, typeof(string)); // Do not rename (binary serialization) info.AddValue("StackTraceString", SerializationStackTraceString, typeof(string)); // Do not rename (binary serialization) - info.AddValue("RemoteStackTraceString", SerializationRemoteStackTraceString, typeof(string)); // Do not rename (binary serialization) + info.AddValue("RemoteStackTraceString", _remoteStackTraceString, typeof(string)); // Do not rename (binary serialization) info.AddValue("RemoteStackIndex", 0, typeof(int)); // Do not rename (binary serialization) info.AddValue("ExceptionMethod", null, typeof(string)); // Do not rename (binary serialization) info.AddValue("HResult", _HResult); // Do not rename (binary serialization) diff --git a/src/mono/System.Private.CoreLib/src/System/Exception.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Exception.Mono.cs index bb38f05e371b..490a19699e9f 100644 --- a/src/mono/System.Private.CoreLib/src/System/Exception.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Exception.Mono.cs @@ -143,7 +143,6 @@ private bool CanSetRemoteStackTrace() private static IDictionary CreateDataContainer() => new ListDictionaryInternal(); private static string? SerializationWatsonBuckets => null; - private string? SerializationRemoteStackTraceString => _remoteStackTraceString; private string? SerializationStackTraceString => GetStackTrace(true); } } From de57531aa658dfbcc3ae76149256e7bfa4da43fd Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Wed, 31 Mar 2021 20:08:37 -0700 Subject: [PATCH 98/98] Fix test failure --- src/coreclr/tools/Common/TypeSystem/IL/MethodIL.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tools/Common/TypeSystem/IL/MethodIL.cs b/src/coreclr/tools/Common/TypeSystem/IL/MethodIL.cs index b5eb21fcba42..cfe1f93998b0 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/MethodIL.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/MethodIL.cs @@ -86,7 +86,7 @@ public abstract partial class MethodIL /// (typically a , , , /// or ). /// - public abstract Object GetObject(int token, NotFoundBehavior notFoundBehavior = NotFoundBehavior.ReturnNull); + public abstract Object GetObject(int token, NotFoundBehavior notFoundBehavior = NotFoundBehavior.Throw); /// /// Gets a list of exception regions this method body defines.