diff --git a/eng/Versions.props b/eng/Versions.props index d9f843a36e92b..7d867a37da28d 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -46,6 +46,7 @@ 6.0.0-preview1.21054.10 3.8.0-4.20503.2 + 3.8.0 6.0.0-beta.21062.10 6.0.0-beta.21062.10 diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index 36b60ed6663a3..81a1cceef3d5f 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -13,6 +13,8 @@ Debug;Release;Checked x64;x86;arm;arm64 + true + true $(IntermediateOutputPath)System.Private.CoreLib.xml $(MSBuildThisFileDirectory)src\ILLink\ @@ -301,7 +303,11 @@ - + + + + + diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.sln b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.sln index 5dce3334a8e89..4629497f45f7c 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.sln +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.sln @@ -6,6 +6,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Private.CoreLib", "S EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "System.Private.CoreLib.Shared", "..\..\libraries\System.Private.CoreLib\src\System.Private.CoreLib.Shared.shproj", "{845C8B26-350B-4E63-BD11-2C8150444E28}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Private.CoreLib.Generators", "..\..\libraries\System.Private.CoreLib\generators\System.Private.CoreLib.Generators.csproj", "{7196828B-5E00-4BC6-9A1E-492C948E41A3}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution ..\..\libraries\System.Private.CoreLib\src\System.Private.CoreLib.Shared.projitems*{3da06c3a-2e7b-4cb7-80ed-9b12916013f9}*SharedItemsImports = 5 @@ -13,14 +15,17 @@ Global EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Checked|amd64 = Checked|amd64 + Checked|Any CPU = Checked|Any CPU Checked|arm = Checked|arm Checked|arm64 = Checked|arm64 Checked|x86 = Checked|x86 Debug|amd64 = Debug|amd64 + Debug|Any CPU = Debug|Any CPU Debug|arm = Debug|arm Debug|arm64 = Debug|arm64 Debug|x86 = Debug|x86 Release|amd64 = Release|amd64 + Release|Any CPU = Release|Any CPU Release|arm = Release|arm Release|arm64 = Release|arm64 Release|x86 = Release|x86 @@ -28,6 +33,7 @@ Global GlobalSection(ProjectConfigurationPlatforms) = postSolution {3DA06C3A-2E7B-4CB7-80ED-9B12916013F9}.Checked|amd64.ActiveCfg = Checked|x64 {3DA06C3A-2E7B-4CB7-80ED-9B12916013F9}.Checked|amd64.Build.0 = Checked|x64 + {3DA06C3A-2E7B-4CB7-80ED-9B12916013F9}.Checked|Any CPU.ActiveCfg = Checked|x86 {3DA06C3A-2E7B-4CB7-80ED-9B12916013F9}.Checked|arm.ActiveCfg = Checked|arm {3DA06C3A-2E7B-4CB7-80ED-9B12916013F9}.Checked|arm.Build.0 = Checked|arm {3DA06C3A-2E7B-4CB7-80ED-9B12916013F9}.Checked|arm64.ActiveCfg = Checked|arm64 @@ -36,6 +42,7 @@ Global {3DA06C3A-2E7B-4CB7-80ED-9B12916013F9}.Checked|x86.Build.0 = Checked|x86 {3DA06C3A-2E7B-4CB7-80ED-9B12916013F9}.Debug|amd64.ActiveCfg = Debug|x64 {3DA06C3A-2E7B-4CB7-80ED-9B12916013F9}.Debug|amd64.Build.0 = Debug|x64 + {3DA06C3A-2E7B-4CB7-80ED-9B12916013F9}.Debug|Any CPU.ActiveCfg = Debug|x86 {3DA06C3A-2E7B-4CB7-80ED-9B12916013F9}.Debug|arm.ActiveCfg = Debug|arm {3DA06C3A-2E7B-4CB7-80ED-9B12916013F9}.Debug|arm.Build.0 = Debug|arm {3DA06C3A-2E7B-4CB7-80ED-9B12916013F9}.Debug|arm64.ActiveCfg = Debug|arm64 @@ -44,12 +51,43 @@ Global {3DA06C3A-2E7B-4CB7-80ED-9B12916013F9}.Debug|x86.Build.0 = Debug|x86 {3DA06C3A-2E7B-4CB7-80ED-9B12916013F9}.Release|amd64.ActiveCfg = Release|x64 {3DA06C3A-2E7B-4CB7-80ED-9B12916013F9}.Release|amd64.Build.0 = Release|x64 + {3DA06C3A-2E7B-4CB7-80ED-9B12916013F9}.Release|Any CPU.ActiveCfg = Release|x86 {3DA06C3A-2E7B-4CB7-80ED-9B12916013F9}.Release|arm.ActiveCfg = Release|arm {3DA06C3A-2E7B-4CB7-80ED-9B12916013F9}.Release|arm.Build.0 = Release|arm {3DA06C3A-2E7B-4CB7-80ED-9B12916013F9}.Release|arm64.ActiveCfg = Release|arm64 {3DA06C3A-2E7B-4CB7-80ED-9B12916013F9}.Release|arm64.Build.0 = Release|arm64 {3DA06C3A-2E7B-4CB7-80ED-9B12916013F9}.Release|x86.ActiveCfg = Release|x86 {3DA06C3A-2E7B-4CB7-80ED-9B12916013F9}.Release|x86.Build.0 = Release|x86 + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Checked|amd64.ActiveCfg = Debug|Any CPU + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Checked|amd64.Build.0 = Debug|Any CPU + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Checked|Any CPU.Build.0 = Debug|Any CPU + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Checked|arm.ActiveCfg = Debug|Any CPU + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Checked|arm.Build.0 = Debug|Any CPU + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Checked|arm64.ActiveCfg = Debug|Any CPU + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Checked|arm64.Build.0 = Debug|Any CPU + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Checked|x86.ActiveCfg = Debug|Any CPU + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Checked|x86.Build.0 = Debug|Any CPU + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Debug|amd64.ActiveCfg = Debug|Any CPU + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Debug|amd64.Build.0 = Debug|Any CPU + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Debug|arm.ActiveCfg = Debug|Any CPU + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Debug|arm.Build.0 = Debug|Any CPU + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Debug|arm64.ActiveCfg = Debug|Any CPU + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Debug|arm64.Build.0 = Debug|Any CPU + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Debug|x86.ActiveCfg = Debug|Any CPU + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Debug|x86.Build.0 = Debug|Any CPU + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Release|amd64.ActiveCfg = Release|Any CPU + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Release|amd64.Build.0 = Release|Any CPU + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Release|Any CPU.Build.0 = Release|Any CPU + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Release|arm.ActiveCfg = Release|Any CPU + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Release|arm.Build.0 = Release|Any CPU + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Release|arm64.ActiveCfg = Release|Any CPU + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Release|arm64.Build.0 = Release|Any CPU + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Release|x86.ActiveCfg = Release|Any CPU + {7196828B-5E00-4BC6-9A1E-492C948E41A3}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/libraries/System.Private.CoreLib/generators/EventSourceGenerator.Emitter.cs b/src/libraries/System.Private.CoreLib/generators/EventSourceGenerator.Emitter.cs new file mode 100644 index 0000000000000..9b30dd4de5323 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/generators/EventSourceGenerator.Emitter.cs @@ -0,0 +1,109 @@ +// 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.Text; +using System.Threading; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; + +namespace Generators +{ + public partial class EventSourceGenerator + { + private class Emitter + { + private readonly StringBuilder _builder = new StringBuilder(1024); + private readonly GeneratorExecutionContext _context; + + public Emitter(GeneratorExecutionContext context) => _context = context; + + public void Emit(EventSourceClass[] eventSources, CancellationToken cancellationToken) + { + foreach (EventSourceClass? ec in eventSources) + { + if (cancellationToken.IsCancellationRequested) + { + // stop any additional work + break; + } + + _builder.AppendLine("using System;"); + GenType(ec); + + _context.AddSource($"{ec.ClassName}.Generated", SourceText.From(_builder.ToString(), Encoding.UTF8)); + + _builder.Clear(); + } + } + + private void GenType(EventSourceClass ec) + { + if (!string.IsNullOrWhiteSpace(ec.Namespace)) + { + _builder.AppendLine($@" +namespace {ec.Namespace} +{{"); + } + + _builder.AppendLine($@" + partial class {ec.ClassName} + {{"); + GenerateConstructor(ec); + + GenerateProviderMetadata(ec.SourceName); + + _builder.AppendLine($@" + }}"); + + if (!string.IsNullOrWhiteSpace(ec.Namespace)) + { + _builder.AppendLine($@" +}}"); + } + } + + private void GenerateConstructor(EventSourceClass ec) + { + _builder.AppendLine($@" + private {ec.ClassName}() : base(new Guid({ec.Guid.ToString("x").Replace("{", "").Replace("}", "")}), ""{ec.SourceName}"") {{ }}"); + } + + private void GenerateProviderMetadata(string sourceName) + { + _builder.Append(@" + private protected override ReadOnlySpan ProviderMetadata => new byte[] { "); + + byte[] metadataBytes = MetadataForString(sourceName); + foreach (byte b in metadataBytes) + { + _builder.Append($"0x{b:x}, "); + } + + _builder.AppendLine(@"};"); + } + + // From System.Private.CoreLib + private static byte[] MetadataForString(string name) + { + CheckName(name); + int metadataSize = Encoding.UTF8.GetByteCount(name) + 3; + byte[]? metadata = new byte[metadataSize]; + ushort totalSize = checked((ushort)(metadataSize)); + metadata[0] = unchecked((byte)totalSize); + metadata[1] = unchecked((byte)(totalSize >> 8)); + Encoding.UTF8.GetBytes(name, 0, name.Length, metadata, 2); + return metadata; + } + + private static void CheckName(string? name) + { + if (name != null && 0 <= name.IndexOf('\0')) + { + throw new ArgumentOutOfRangeException(nameof(name)); + } + } + } + } +} diff --git a/src/libraries/System.Private.CoreLib/generators/EventSourceGenerator.Parser.cs b/src/libraries/System.Private.CoreLib/generators/EventSourceGenerator.Parser.cs new file mode 100644 index 0000000000000..03e9dad395ad3 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/generators/EventSourceGenerator.Parser.cs @@ -0,0 +1,195 @@ +// 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.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Generators +{ + public partial class EventSourceGenerator + { + private class Parser + { + private readonly CancellationToken _cancellationToken; + private readonly Compilation _compilation; + private readonly Action _reportDiagnostic; + + public Parser(Compilation compilation, Action reportDiagnostic, CancellationToken cancellationToken) + { + _compilation = compilation; + _cancellationToken = cancellationToken; + _reportDiagnostic = reportDiagnostic; + } + + public EventSourceClass[] GetEventSourceClasses(List classDeclarations) + { + INamedTypeSymbol? autogenerateAttribute = _compilation.GetTypeByMetadataName("System.Diagnostics.Tracing.EventSourceAutoGenerateAttribute"); + if (autogenerateAttribute is null) + { + // No EventSourceAutoGenerateAttribute + return Array.Empty(); + } + + INamedTypeSymbol? eventSourceAttribute = _compilation.GetTypeByMetadataName("System.Diagnostics.Tracing.EventSourceAttribute"); + if (eventSourceAttribute is null) + { + // No EventSourceAttribute + return Array.Empty(); + } + + List? results = null; + // we enumerate by syntax tree, to minimize the need to instantiate semantic models (since they're expensive) + foreach (IGrouping? group in classDeclarations.GroupBy(x => x.SyntaxTree)) + { + SemanticModel? sm = null; + EventSourceClass? eventSourceClass = null; + foreach (ClassDeclarationSyntax? classDef in group) + { + if (_cancellationToken.IsCancellationRequested) + { + // be nice and stop if we're asked to + return results?.ToArray() ?? Array.Empty(); + } + + bool autoGenerate = false; + foreach (AttributeListSyntax? cal in classDef.AttributeLists) + { + foreach (AttributeSyntax? ca in cal.Attributes) + { + // need a semantic model for this tree + sm ??= _compilation.GetSemanticModel(classDef.SyntaxTree); + + if (sm.GetSymbolInfo(ca, _cancellationToken).Symbol is not IMethodSymbol caSymbol) + { + // badly formed attribute definition, or not the right attribute + continue; + } + + if (autogenerateAttribute.Equals(caSymbol.ContainingType, SymbolEqualityComparer.Default)) + { + autoGenerate = true; + continue; + } + if (eventSourceAttribute.Equals(caSymbol.ContainingType, SymbolEqualityComparer.Default)) + { + string nspace = string.Empty; + NamespaceDeclarationSyntax? ns = classDef.Parent as NamespaceDeclarationSyntax; + if (ns is null) + { + if (classDef.Parent is not CompilationUnitSyntax) + { + // since this generator doesn't know how to generate a nested type... + continue; + } + } + else + { + nspace = ns.Name.ToString(); + while (true) + { + ns = ns.Parent as NamespaceDeclarationSyntax; + if (ns == null) + { + break; + } + + nspace = $"{ns.Name}.{nspace}"; + } + } + + string className = classDef.Identifier.ToString(); + string name = className; + string guid = ""; + + SeparatedSyntaxList? args = ca.ArgumentList?.Arguments; + if (args is not null) + { + foreach (AttributeArgumentSyntax? arg in args) + { + string? argName = arg.NameEquals!.Name.Identifier.ToString(); + string? value = sm.GetConstantValue(arg.Expression, _cancellationToken).ToString(); + + switch (argName) + { + case "Guid": + guid = value; + break; + case "Name": + name = value; + break; + } + } + } + + if (!Guid.TryParse(guid, out Guid result)) + { + result = GenerateGuidFromName(name.ToUpperInvariant()); + } + + eventSourceClass = new EventSourceClass + { + Namespace = nspace, + ClassName = className, + SourceName = name, + Guid = result + }; + continue; + } + } + } + + if (!autoGenerate) + { + continue; + } + + if (eventSourceClass is null) + { + continue; + } + + results ??= new List(); + results.Add(eventSourceClass); + } + } + + return results?.ToArray() ?? Array.Empty(); + } + + // From System.Private.CoreLib + private static Guid GenerateGuidFromName(string name) + { + ReadOnlySpan namespaceBytes = new byte[] // rely on C# compiler optimization to remove byte[] allocation + { + 0x48, 0x2C, 0x2D, 0xB2, 0xC3, 0x90, 0x47, 0xC8, + 0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB, + }; + + byte[] bytes = Encoding.BigEndianUnicode.GetBytes(name); + + byte[] combinedBytes = new byte[namespaceBytes.Length + bytes.Length]; + + bytes.CopyTo(combinedBytes, namespaceBytes.Length); + namespaceBytes.CopyTo(combinedBytes); + + using (SHA1 sha = SHA1.Create()) + { + bytes = sha.ComputeHash(combinedBytes); + } + + Array.Resize(ref bytes, 16); + + bytes[7] = unchecked((byte)((bytes[7] & 0x0F) | 0x50)); // Set high 4 bits of octet 7 to 5, as per RFC 4122 + return new Guid(bytes); + } + } + } +} diff --git a/src/libraries/System.Private.CoreLib/generators/EventSourceGenerator.cs b/src/libraries/System.Private.CoreLib/generators/EventSourceGenerator.cs new file mode 100644 index 0000000000000..b86cdebb0a1e4 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/generators/EventSourceGenerator.cs @@ -0,0 +1,108 @@ +// 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 Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Generators +{ + [Generator] + public partial class EventSourceGenerator : ISourceGenerator + { + // Example input: + // + // [EventSource(Guid = "49592C0F-5A05-516D-AA4B-A64E02026C89", Name = "System.Runtime")] + // [EventSourceAutoGenerate] + // internal sealed partial class RuntimeEventSource : EventSource + // + // Example generated output: + // + // using System; + // + // namespace System.Diagnostics.Tracing + // { + // partial class RuntimeEventSource + // { + // private RuntimeEventSource() : base(new Guid(0x49592c0f,0x5a05,0x516d,0xaa,0x4b,0xa6,0x4e,0x02,0x02,0x6c,0x89), "System.Runtime") { } + // + // private protected override ReadOnlySpan ProviderMetadata => new byte[] { 0x11, 0x0, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x0, }; + // } + // } + + public void Initialize(GeneratorInitializationContext context) + => context.RegisterForSyntaxNotifications(() => new SyntaxReceiver()); + + public void Execute(GeneratorExecutionContext context) + { + SyntaxReceiver? receiver = context.SyntaxReceiver as SyntaxReceiver; + if ((receiver?.CandidateClasses?.Count ?? 0) == 0) + { + // nothing to do yet + return; + } + + Parser? p = new Parser(context.Compilation, context.ReportDiagnostic, context.CancellationToken); + EventSourceClass[]? eventSources = p.GetEventSourceClasses(receiver.CandidateClasses); + + if (eventSources?.Length > 0) + { + Emitter? e = new Emitter(context); + e.Emit(eventSources, context.CancellationToken); + } + } + + private sealed class SyntaxReceiver : ISyntaxReceiver + { + private List? _candidateClasses; + + public List? CandidateClasses => _candidateClasses; + + public void OnVisitSyntaxNode(SyntaxNode syntaxNode) + { + // Only add classes annotated [EventSourceAutoGenerate] to reduce busy work. + const string EventSourceAttribute = "EventSourceAutoGenerateAttribute"; + const string EventSourceAttributeShort = "EventSourceAutoGenerate"; + + // Only clasess + if (syntaxNode is ClassDeclarationSyntax classDeclaration) + { + // Check if has EventSource attribute before adding to candidates + // as we don't want to add every class in the project + foreach (AttributeListSyntax? cal in classDeclaration.AttributeLists) + { + foreach (AttributeSyntax? ca in cal.Attributes) + { + // Check if Span length matches before allocating the string to check more + int length = ca.Name.Span.Length; + if (length != EventSourceAttribute.Length && length != EventSourceAttributeShort.Length) + { + continue; + } + + // Possible match, now check the string value + string attrName = ca.Name.ToString(); + if (attrName == EventSourceAttribute || attrName == EventSourceAttributeShort) + { + // Match add to candidates + _candidateClasses ??= new List(); + _candidateClasses.Add(classDeclaration); + return; + } + } + } + } + } + } + + private class EventSourceClass + { + public string Namespace = string.Empty; + public string ClassName = string.Empty; + public string SourceName = string.Empty; + public Guid Guid = Guid.Empty; + } + } +} diff --git a/src/libraries/System.Private.CoreLib/generators/System.Private.CoreLib.Generators.csproj b/src/libraries/System.Private.CoreLib/generators/System.Private.CoreLib.Generators.csproj new file mode 100644 index 0000000000000..9b241b14aaf42 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/generators/System.Private.CoreLib.Generators.csproj @@ -0,0 +1,19 @@ + + + netstandard2.0 + latest + enable + false + $(NoWarn);CS3001 + + + + + + + + + + + + diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/ArrayPoolEventSource.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/ArrayPoolEventSource.cs index 32851bac2d36b..2f6ac1842393d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/ArrayPoolEventSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/ArrayPoolEventSource.cs @@ -6,7 +6,8 @@ namespace System.Buffers { [EventSource(Guid = "0866B2B8-5CEF-5DB9-2612-0C0FFD814A44", Name = "System.Buffers.ArrayPoolEventSource")] - internal sealed class ArrayPoolEventSource : EventSource + [EventSourceAutoGenerate] + internal sealed partial class ArrayPoolEventSource : EventSource { internal static readonly ArrayPoolEventSource Log = new ArrayPoolEventSource(); @@ -21,8 +22,9 @@ internal enum BufferAllocatedReason : int PoolExhausted } - // The ArrayPoolEventSource GUID is {0866b2b8-5cef-5db9-2612-0c0ffd814a44} - private ArrayPoolEventSource() : base(new Guid(0x0866b2b8, 0x5cef, 0x5db9, 0x26, 0x12, 0x0c, 0x0f, 0xfd, 0x81, 0x4a, 0x44), "System.Buffers.ArrayPoolEventSource") { } + // Parameterized constructor to block initialization and ensure the EventSourceGenerator is creating the default constructor + // as you can't make a constructor partial. + private ArrayPoolEventSource(int _) { } /// /// Event for when a buffer is rented. This is invoked once for every successful call to Rent, diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs index 1e67a98ed310f..40c64e486e990 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs @@ -182,10 +182,16 @@ #if ES_BUILD_STANDALONE namespace Microsoft.Diagnostics.Tracing +{ #else namespace System.Diagnostics.Tracing -#endif { + [AttributeUsage(AttributeTargets.Class)] + internal sealed class EventSourceAutoGenerateAttribute : Attribute + { + } + +#endif /// /// This class is meant to be inherited by a user-defined event source in order to define a managed /// ETW provider. Please See DESIGN NOTES above for the internal architecture. @@ -1471,9 +1477,14 @@ private unsafe void Initialize(Guid eventSourceGuid, string eventSourceName, str m_activityTracker = ActivityTracker.Instance; #if FEATURE_MANAGED_ETW || FEATURE_PERFTRACING - // Create and register our provider traits. We do this early because it is needed to log errors - // In the self-describing event case. - this.InitializeProviderMetadata(); +#if !DEBUG + if (ProviderMetadata.Length == 0) +#endif + { + // Create and register our provider traits. We do this early because it is needed to log errors + // In the self-describing event case. + InitializeProviderMetadata(); + } #endif #if FEATURE_MANAGED_ETW // Register the provider with ETW @@ -1504,12 +1515,13 @@ private unsafe void Initialize(Guid eventSourceGuid, string eventSourceName, str if (this.Name != "System.Diagnostics.Eventing.FrameworkEventSource" || Environment.IsWindows8OrAbove) #endif { - fixed (byte* providerMetadata = this.providerMetadata) + var providerMetadata = ProviderMetadata; + fixed (byte* pMetadata = providerMetadata) { m_etwProvider.SetInformation( Interop.Advapi32.EVENT_INFO_CLASS.SetTraits, - providerMetadata, - (uint)this.providerMetadata.Length); + pMetadata, + (uint)providerMetadata.Length); } } #endif // TARGET_WINDOWS diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/FrameworkEventSource.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/FrameworkEventSource.cs index 1d3b01fc0218f..d1e3cfe154e27 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/FrameworkEventSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/FrameworkEventSource.cs @@ -7,7 +7,8 @@ namespace System.Diagnostics.Tracing { [EventSource(Guid = "8E9F5090-2D75-4d03-8A81-E5AFBF85DAF1", Name = "System.Diagnostics.Eventing.FrameworkEventSource")] - internal sealed class FrameworkEventSource : EventSource + [EventSourceAutoGenerate] + internal sealed partial class FrameworkEventSource : EventSource { public static readonly FrameworkEventSource Log = new FrameworkEventSource(); @@ -28,8 +29,9 @@ public static class Keywords public const EventTask ThreadTransfer = (EventTask)3; } - // The FrameworkEventSource GUID is {8E9F5090-2D75-4d03-8A81-E5AFBF85DAF1} - private FrameworkEventSource() : base(new Guid(0x8e9f5090, 0x2d75, 0x4d03, 0x8a, 0x81, 0xe5, 0xaf, 0xbf, 0x85, 0xda, 0xf1), "System.Diagnostics.Eventing.FrameworkEventSource") { } + // Parameterized constructor to block initialization and ensure the EventSourceGenerator is creating the default constructor + // as you can't make a constructor partial. + private FrameworkEventSource(int _) { } // optimized for common signatures (used by the ThreadTransferSend/Receive events) [NonEvent] diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/NativeRuntimeEventSource.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/NativeRuntimeEventSource.cs index 8901696a8cc2a..027e120c5a5c5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/NativeRuntimeEventSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/NativeRuntimeEventSource.cs @@ -7,14 +7,16 @@ namespace System.Diagnostics.Tracing /// NativeRuntimeEventSource is an EventSource that represents the ETW/EventPipe events emitted by the native runtime. /// Most of NativeRuntimeEventSource is auto-generated by scripts/genRuntimeEventSources.py based on the contents of the Microsoft-Windows-DotNETRuntime provider. /// - [EventSource(Guid = "5E5BB766-BBFC-5662-0548-1D44FAD9BB56", Name = "Microsoft-Windows-DotNETRuntime")] + [EventSource(Guid = "5E5BB766-BBFC-5662-0548-1D44FAD9BB56", Name = EventSourceName)] + [EventSourceAutoGenerate] internal sealed partial class NativeRuntimeEventSource : EventSource { internal const string EventSourceName = "Microsoft-Windows-DotNETRuntime"; internal static NativeRuntimeEventSource Log = new NativeRuntimeEventSource(); - // The NativeRuntimeEventSource GUID is {5e5bb766-bbfc-5662-0548-1d44fad9bb56} - private NativeRuntimeEventSource() : base(new Guid(0x5e5bb766, 0xbbfc, 0x5662, 0x05, 0x48, 0x1d, 0x44, 0xfa, 0xd9, 0xbb, 0x56), EventSourceName) { } + // Parameterized constructor to block initialization and ensure the EventSourceGenerator is creating the default constructor + // as you can't make a constructor partial. + private NativeRuntimeEventSource(int _) { } /// /// Dispatch a single event with the specified event ID and payload. diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/RuntimeEventSource.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/RuntimeEventSource.cs index 4123a54e36946..053157c01d36c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/RuntimeEventSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/RuntimeEventSource.cs @@ -8,8 +8,9 @@ namespace System.Diagnostics.Tracing /// /// RuntimeEventSource is an EventSource that represents events emitted by the managed runtime. /// - [EventSource(Guid = "49592C0F-5A05-516D-AA4B-A64E02026C89", Name = "System.Runtime")] - internal sealed class RuntimeEventSource : EventSource + [EventSource(Guid = "49592C0F-5A05-516D-AA4B-A64E02026C89", Name = EventSourceName)] + [EventSourceAutoGenerate] + internal sealed partial class RuntimeEventSource : EventSource { internal const string EventSourceName = "System.Runtime"; @@ -46,9 +47,9 @@ public static void Initialize() s_RuntimeEventSource = new RuntimeEventSource(); } - private RuntimeEventSource() : base(new Guid(0x49592C0F, 0x5A05, 0x516D, 0xAA, 0x4B, 0xA6, 0x4E, 0x02, 0x02, 0x6C, 0x89), EventSourceName, EventSourceSettings.EtwSelfDescribingEventFormat) - { - } + // Parameterized constructor to block initialization and ensure the EventSourceGenerator is creating the default constructor + // as you can't make a constructor partial. + private RuntimeEventSource(int _) { } protected override void OnEventCommand(EventCommandEventArgs command) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs index 399fe3c889d90..4087165e75b45 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs @@ -21,15 +21,22 @@ using System.Text; #if ES_BUILD_STANDALONE +using System.Runtime.CompilerServices; namespace Microsoft.Diagnostics.Tracing #else +using Internal.Runtime.CompilerServices; namespace System.Diagnostics.Tracing #endif { public partial class EventSource { #if FEATURE_MANAGED_ETW - private byte[] providerMetadata = null!; + private byte[]? m_providerMetadata; +#if ES_BUILD_STANDALONE + private byte[] ProviderMetadata => m_providerMetadata ?? Array.Empty(); +#else + private protected virtual ReadOnlySpan ProviderMetadata => m_providerMetadata; +#endif #endif #if FEATURE_PERFTRACING @@ -427,12 +434,13 @@ private unsafe void WriteMultiMergeInner( for (int i = 0; i < pinCount; i++) pins[i] = default; + var providerMetadata = ProviderMetadata; fixed (byte* - pMetadata0 = this.providerMetadata, + pMetadata0 = providerMetadata, pMetadata1 = nameInfo.nameMetadata, pMetadata2 = eventTypes.typeMetadata) { - descriptors[0].SetMetadata(pMetadata0, this.providerMetadata.Length, 2); + descriptors[0].SetMetadata(pMetadata0, providerMetadata.Length, 2); descriptors[1].SetMetadata(pMetadata1, nameInfo.nameMetadata.Length, 1); descriptors[2].SetMetadata(pMetadata2, eventTypes.typeMetadata.Length, 1); @@ -538,12 +546,13 @@ internal unsafe void WriteMultiMerge( for (int i = 0; i < descriptorsLength; i++) descriptors[i] = default; + var providerMetadata = ProviderMetadata; fixed (byte* - pMetadata0 = this.providerMetadata, + pMetadata0 = providerMetadata, pMetadata1 = nameInfo.nameMetadata, pMetadata2 = eventTypes.typeMetadata) { - descriptors[0].SetMetadata(pMetadata0, this.providerMetadata.Length, 2); + descriptors[0].SetMetadata(pMetadata0, providerMetadata.Length, 2); descriptors[1].SetMetadata(pMetadata1, nameInfo.nameMetadata.Length, 1); descriptors[2].SetMetadata(pMetadata2, eventTypes.typeMetadata.Length, 1); int numDescrs = 3; @@ -610,12 +619,13 @@ private unsafe void WriteImpl( for (int i = 0; i < pinCount; i++) pins[i] = default; + var providerMetadata = ProviderMetadata; fixed (byte* - pMetadata0 = this.providerMetadata, + pMetadata0 = providerMetadata, pMetadata1 = nameInfo.nameMetadata, pMetadata2 = eventTypes.typeMetadata) { - descriptors[0].SetMetadata(pMetadata0, this.providerMetadata.Length, 2); + descriptors[0].SetMetadata(pMetadata0, providerMetadata.Length, 2); descriptors[1].SetMetadata(pMetadata1, nameInfo.nameMetadata.Length, 1); descriptors[2].SetMetadata(pMetadata2, eventTypes.typeMetadata.Length, 1); #endif // FEATURE_MANAGED_ETW @@ -749,6 +759,14 @@ private static unsafe void WriteCleanup(GCHandle* pPins, int cPins) private void InitializeProviderMetadata() { #if FEATURE_MANAGED_ETW + bool hasProviderMetadata = ProviderMetadata.Length > 0; +#if !DEBUG && !ES_BUILD_STANDALONE + if (hasProviderMetadata) + { + // Already set + return; + } +#endif if (m_traits != null) { List traitMetaData = new List(100); @@ -778,13 +796,27 @@ private void InitializeProviderMetadata() traitMetaData[lenPos + 1] = unchecked((byte)(valueLen >> 8)); } } - providerMetadata = Statics.MetadataForString(this.Name, 0, traitMetaData.Count, 0); + byte[] providerMetadata = Statics.MetadataForString(this.Name, 0, traitMetaData.Count, 0); int startPos = providerMetadata.Length - traitMetaData.Count; foreach (byte b in traitMetaData) + { providerMetadata[startPos++] = b; + } + + m_providerMetadata = providerMetadata; } else - providerMetadata = Statics.MetadataForString(this.Name, 0, 0, 0); + { + m_providerMetadata = Statics.MetadataForString(this.Name, 0, 0, 0); + } + +#if DEBUG && !ES_BUILD_STANDALONE + if (hasProviderMetadata) + { + // Validate the provided ProviderMetadata still matches in debug + Debug.Assert(ProviderMetadata.SequenceEqual(m_providerMetadata)); + } +#endif #endif //FEATURE_MANAGED_ETW } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPoolEventSource.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPoolEventSource.cs index 65061781083b0..784f64d41c42a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPoolEventSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPoolEventSource.cs @@ -21,7 +21,8 @@ namespace System.Threading // provider, and update PerfView with a trace event parser for the new provider so that it knows about the events and may // use them to identify thread pool threads. [EventSource(Name = "Microsoft-Windows-DotNETRuntime", Guid = "e13c0d23-ccbc-4e12-931b-d9cc2eee27e4")] - internal sealed class PortableThreadPoolEventSource : EventSource + [EventSourceAutoGenerate] + internal sealed partial class PortableThreadPoolEventSource : EventSource { // This value does not seem to be used, leaving it as zero for now. It may be useful for a scenario that may involve // multiple instances of the runtime within the same process, but then it seems unlikely that both instances' thread @@ -76,12 +77,9 @@ public enum ThreadAdjustmentReasonMap : uint ThreadTimedOut } - private PortableThreadPoolEventSource() - : base( - new Guid(0xe13c0d23, 0xccbc, 0x4e12, 0x93, 0x1b, 0xd9, 0xcc, 0x2e, 0xee, 0x27, 0xe4), - "Microsoft-Windows-DotNETRuntime") - { - } + // Parameterized constructor to block initialization and ensure the EventSourceGenerator is creating the default constructor + // as you can't make a constructor partial. + private PortableThreadPoolEventSource(int _) { } [NonEvent] private unsafe void WriteThreadEvent(int eventId, uint numExistingThreads) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TplEventSource.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TplEventSource.cs index f1f12a5c0be25..4d608bdbfcf5f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TplEventSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TplEventSource.cs @@ -18,7 +18,8 @@ namespace System.Threading.Tasks null #endif )] - internal sealed class TplEventSource : EventSource + [EventSourceAutoGenerate] + internal sealed partial class TplEventSource : EventSource { /// Used to determine if tasks should generate Activity IDs for themselves internal bool TasksSetActivityIds; // This keyword is set @@ -43,12 +44,12 @@ protected override void OnEventCommand(EventCommandEventArgs command) /// /// Defines the singleton instance for the TPL ETW provider. - /// The TPL Event provider GUID is {2e5dba47-a3d2-4d16-8ee0-6671ffdcd7b5}. /// public static readonly TplEventSource Log = new TplEventSource(); - /// Prevent external instantiation. All logging should go through the Log instance. - private TplEventSource() : base(new Guid(0x2e5dba47, 0xa3d2, 0x4d16, 0x8e, 0xe0, 0x66, 0x71, 0xff, 0xdc, 0xd7, 0xb5), "System.Threading.Tasks.TplEventSource") { } + // Parameterized constructor to block initialization and ensure the EventSourceGenerator is creating the default constructor + // as you can't make a constructor partial. + private TplEventSource(int _) { } /// Configured behavior of a task wait operation. public enum TaskWaitBehavior : int diff --git a/src/mono/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj index a48b961b142d2..9b8fa384d7508 100644 --- a/src/mono/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -14,6 +14,8 @@ $(RuntimeBinDir)IL/ Debug;Release;Checked x64;x86;arm;arm64;wasm + + true @@ -296,6 +298,10 @@ + + + +