diff --git a/Directory.Build.targets b/Directory.Build.targets index cd68fb94f4fe99..26d5238e9cb56a 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -12,7 +12,6 @@ - diff --git a/eng/python.targets b/eng/python.targets deleted file mode 100644 index 3f933fdfea0eb1..00000000000000 --- a/eng/python.targets +++ /dev/null @@ -1,31 +0,0 @@ - - - - <_PythonLocationScript>-c "import sys; sys.stdout.write(sys.executable)" - - - - - - - - - - - - - - diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index 3d2947219a4780..1549d763810098 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -314,30 +314,7 @@ - - - - - src\System\Diagnostics\Eventing\NativeRuntimeEventSource.Generated.cs - - + + - - - - - - <_PythonWarningParameter>-Wall - <_PythonWarningParameter Condition="'$(MSBuildTreatWarningsAsErrors)' == 'true'">$(_PythonWarningParameter) -Werror - <_EventingSourceFileDirectory>%(EventingSourceFile.RootDir)%(EventingSourceFile.Directory) - <_EventingSourceFileDirectory Condition="HasTrailingSlash('$(_EventingSourceFileDirectory)')">$(_EventingSourceFileDirectory.TrimEnd('\')) - - - - - - - - - diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj index d7f5454259ee61..b3c050d2d2194c 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -565,34 +565,8 @@ - - - - - - src\System\Diagnostics\Eventing\NativeRuntimeEventSource.Generated.cs - - + + + - - - - - - <_PythonWarningParameter>-Wall - <_PythonWarningParameter Condition="'$(MSBuildTreatWarningsAsErrors)' == 'true'">$(_PythonWarningParameter) -Werror - <_EventingSourceFileDirectory>%(EventingSourceFile.RootDir)%(EventingSourceFile.Directory) - <_EventingSourceFileDirectory Condition="HasTrailingSlash('$(_EventingSourceFileDirectory)')">$(_EventingSourceFileDirectory.TrimEnd('\')) - - - - - - - - diff --git a/src/coreclr/scripts/genRuntimeEventSources.py b/src/coreclr/scripts/genRuntimeEventSources.py deleted file mode 100644 index ed13eeec7238ea..00000000000000 --- a/src/coreclr/scripts/genRuntimeEventSources.py +++ /dev/null @@ -1,484 +0,0 @@ -# -## Licensed to the .NET Foundation under one or more agreements. -## The .NET Foundation licenses this file to you under the MIT license. -# - -import os -import xml.dom.minidom as DOM -from utilities import open_for_update, parseInclusionList -import argparse -import sys - -generatedCodeFileHeader="""// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/********************************************************************** - -DO NOT MODIFY. AUTOGENERATED FILE. -This file is generated by /src/coreclr/scripts/genRuntimeEventSources.py - -**********************************************************************/ -""" - -######################################################################## -# START CONFIGURATION -######################################################################## -manifestsToGenerate = { - "Microsoft-Windows-DotNETRuntime" : "NativeRuntimeEventSource.Generated.cs" -} - -providerNameToClassNameMap = { - "Microsoft-Windows-DotNETRuntime" : "NativeRuntimeEventSource" -} - -manifestTypeToCSharpTypeMap = { - "win:UInt8" : "byte", - "win:UInt16" : "ushort", - "win:UInt32" : "uint", - "win:UInt64" : "ulong", - "win:Int32" : "int", - "win:Int64" : "long", - "win:Pointer" : "IntPtr", - "win:UnicodeString" : "string", - "win:Binary" : "byte[]", - "win:Double" : "double", - "win:Boolean" : "bool", - "win:GUID" : "Guid", -} - -overrideEnumBackingTypes = { - "Microsoft-Windows-DotNETRuntime" : { - "GCSuspendEEReasonMap" : "win:UInt32", - "GCRootKindMap" : "win:UInt32" - } -} -######################################################################## -# END CONFIGURATION -######################################################################## - -tabText = "" - -def increaseTabLevel(): - global tabText - tabText += " " - -def decreaseTabLevel(): - global tabText - tabText = tabText[:-4] - -def writeOutput(outputFile, str): - outputFile.write(tabText + str) - -def getCSharpTypeFromManifestType(manifestType): - return manifestTypeToCSharpTypeMap[manifestType] - -def getManifestsToGenerate(): - return manifestsToGenerate - -def includeEvent(inclusionList, providerName, eventName): - if len(inclusionList) == 0: - return True - if providerName in inclusionList and eventName in inclusionList[providerName]: - return True - elif providerName in inclusionList and "*" in inclusionList[providerName]: - return True - elif "*" in inclusionList and eventName in inclusionList["*"]: - return True - elif "*" in inclusionList and "*" in inclusionList["*"]: - return True - else: - return False - -def generateEvent(eventNode, providerNode, outputFile, stringTable): - - # Some threading events are defined manually in NativeRuntimeEventSource.Threading.cs - symbol = eventNode.getAttribute("symbol") - if any(s in symbol for s in ["ThreadPool", "Contention", "WaitHandle"]): - return - - evtLevel = eventNode.getAttribute("level")[4:] - evtKeywords = "" - # Write the event attribute. - writeOutput(outputFile, "[Event("+ eventNode.getAttribute("value") + ", Version = " + eventNode.getAttribute("version") + ", Level = EventLevel." + evtLevel) - - # Not all events have keywords specified, and some have multiple keywords specified. - keywords = eventNode.getAttribute("keywords") - if keywords: - if " " not in keywords: - outputFile.write(", Keywords = Keywords." + keywords) - evtKeywords = "Keywords." + keywords - else: - keywords = keywords.split() - outputFile.write(", Keywords = ") - for keywordIndex in range(len(keywords)): - evtKeywords += "Keywords." + keywords[keywordIndex] - if keywordIndex < (len(keywords) - 1): - evtKeywords += " | " - outputFile.write(evtKeywords) - outputFile.write(")]\n") - - # Get the template for the event. - templateNode = None - templateKey = eventNode.getAttribute("template") - if templateKey is not None: - for node in providerNode.getElementsByTagName("templates"): - templatesNode = node - break - for node in templatesNode.getElementsByTagName("template"): - if node.getAttribute("tid") == templateKey: - templateNode = node - break - - # Write the beginning of the method signature. - writeOutput(outputFile, "private void " + eventNode.getAttribute("symbol") + "(") - - # Write the function signature. - argumentCount = 0 - if templateNode is not None: - argumentNodes = templateNode.childNodes - - # Calculate the number of arguments. - for argumentNode in argumentNodes: - if argumentNode.nodeName == "data": - if argumentNode.getAttribute("inType") != "win:Binary" and argumentNode.getAttribute("inType") != "win:AnsiString" and argumentNode.getAttribute("count") == "": - argumentCount += 1 - else: - break - elif argumentNode.nodeName == "struct": - break - - argumentsProcessed = 0 - for argumentIndex in range(len(argumentNodes)): - argumentNode = argumentNodes[argumentIndex] - if argumentNode.nodeName == "data": - argumentName = argumentNode.getAttribute("name") - argumentInType = argumentNode.getAttribute("inType") - - #### Disable enums until they are needed #### - # argumentMap = argumentNode.getAttribute("map") - # if not argumentMap: - # argumentCSharpType = getCSharpTypeFromManifestType(argumentInType) - # else: - # argumentCSharpType = argumentMap[:-3] - #### Disable enums until they are needed #### - - argumentCSharpType = getCSharpTypeFromManifestType(argumentInType) - outputFile.write(argumentCSharpType + " " + argumentName) - argumentsProcessed += 1 - if argumentsProcessed < argumentCount: - outputFile.write(", ") - if argumentsProcessed == argumentCount: - break - - outputFile.write(")\n") - writeOutput(outputFile, "{\n") - - increaseTabLevel() - writeOutput(outputFile, "// To have this event be emitted from managed side, refer to NativeRuntimeEventSource.cs\n") - writeOutput(outputFile, "throw new NotImplementedException();\n") - decreaseTabLevel() - writeOutput(outputFile, "}\n\n") - - -def generateEvents(providerNode, outputFile, stringTable, inclusion_list): - - providerName = providerNode.getAttribute("name") - - # Get the events element. - for node in providerNode.getElementsByTagName("events"): - eventsNode = node - break - - # Get the list of event nodes. - eventNodes = eventsNode.getElementsByTagName("event") - - # Build a list of events to be emitted. This is where old versions of events are stripped. - # key = eventID, value = version - eventList = dict() - for eventNode in eventNodes: - eventName = eventNode.getAttribute('symbol') - if not includeEvent(inclusion_list, providerName, eventName): - continue - - eventID = eventNode.getAttribute("value") - eventVersion = eventNode.getAttribute("version") - eventList[eventID] = eventVersion - - # Iterate over each event node and process it. - # Only emit events for the latest version of the event, otherwise EventSource initialization will fail. - for eventNode in eventNodes: - eventName = eventNode.getAttribute('symbol') - if not includeEvent(inclusion_list, providerName, eventName): - continue - - eventID = eventNode.getAttribute("value") - eventVersion = eventNode.getAttribute("version") - if eventID in eventList and eventList[eventID] == eventVersion: - generateEvent(eventNode, providerNode, outputFile, stringTable) - elif eventID not in eventList: - raise ValueError("eventID could not be found in the list of events to emit.", eventID) - -def generateValueMapEnums(providerNode, outputFile, stringTable, enumTypeMap): - - # Get the maps element. - for node in providerNode.getElementsByTagName("maps"): - mapsNode = node - break - - # Iterate over each map and create an enum out of it. - for valueMapNode in mapsNode.getElementsByTagName("valueMap"): - - # Get the backing type of the enum. - typeName = enumTypeMap[valueMapNode.getAttribute("name")] - if typeName is None: - raise ValueError("No mapping from mapName to enum backing type.", valueMapNode.getAttribute("name")) - - enumType = getCSharpTypeFromManifestType(typeName) - writeOutput(outputFile, "public enum " + valueMapNode.getAttribute("name")[:-3] + " : " + enumType + "\n") - writeOutput(outputFile, "{\n") - increaseTabLevel() - for mapNode in valueMapNode.getElementsByTagName("map"): - # Each map value has a message, which we should use as the enum value. - messageKey = mapNode.getAttribute("message")[9:-1] - writeOutput(outputFile, stringTable[messageKey] + " = " + mapNode.getAttribute("value") + ",\n") - decreaseTabLevel() - writeOutput(outputFile, "}\n\n") - -def generateBitMapEnums(providerNode, outputFile, stringTable, enumTypeMap): - - # Get the maps element. - for node in providerNode.getElementsByTagName("maps"): - mapsNode = node - break - - # Iterate over each map and create an enum out of it. - for valueMapNode in mapsNode.getElementsByTagName("bitMap"): - - # Get the backing type of the enum. - typeName = enumTypeMap[valueMapNode.getAttribute("name")] - if typeName is None: - raise ValueError("No mapping from mapName to enum backing type.", valueMapNode.getAttribute("name")) - - enumType = getCSharpTypeFromManifestType(typeName) - writeOutput(outputFile, "[Flags]\n") - writeOutput(outputFile, "public enum " + valueMapNode.getAttribute("name")[:-3] + " : " + enumType + "\n") - writeOutput(outputFile, "{\n") - increaseTabLevel() - for mapNode in valueMapNode.getElementsByTagName("map"): - # Each map value has a message, which we should use as the enum value. - messageKey = mapNode.getAttribute("message")[9:-1] - writeOutput(outputFile, stringTable[messageKey] + " = " + mapNode.getAttribute("value") + ",\n") - decreaseTabLevel() - writeOutput(outputFile, "}\n\n") - -def generateEnumTypeMap(providerNode): - - providerName = providerNode.getAttribute("name") - templatesNodes = providerNode.getElementsByTagName("templates") - templatesNode = templatesNodes[0] - mapsNodes = providerNode.getElementsByTagName("maps") - - # Keep a list of mapName -> inType. - # This map contains the first inType seen for the specified mapName. - typeMap = dict() - - # There are a couple of maps that are used by multiple events but have different backing types. - # Because only one of the uses will be consumed by EventSource/EventListener we can hack the backing type here - # and suppress the warning that we'd otherwise get. - overrideTypeMap = dict() - if providerName in overrideEnumBackingTypes: - overrideTypeMap = overrideEnumBackingTypes[providerName] - - for mapsNode in mapsNodes: - for valueMapNode in mapsNode.getElementsByTagName("valueMap"): - mapName = valueMapNode.getAttribute("name") - dataNodes = templatesNode.getElementsByTagName("data") - - # If we've never seen the map used, save its usage with the inType. - # If we have seen the map used, make sure that the inType saved previously matches the current inType. - for dataNode in dataNodes: - if dataNode.getAttribute("map") == mapName: - if mapName in overrideTypeMap: - typeMap[mapName] = overrideTypeMap[mapName] - elif mapName in typeMap and typeMap[mapName] != dataNode.getAttribute("inType"): - print("WARNING: Map " + mapName + " is used multiple times with different types. This may cause functional bugs in tracing.") - elif not mapName in typeMap: - typeMap[mapName] = dataNode.getAttribute("inType") - for bitMapNode in mapsNode.getElementsByTagName("bitMap"): - mapName = bitMapNode.getAttribute("name") - dataNodes = templatesNode.getElementsByTagName("data") - - # If we've never seen the map used, save its usage with the inType. - # If we have seen the map used, make sure that the inType saved previously matches the current inType. - for dataNode in dataNodes: - if dataNode.getAttribute("map") == mapName: - if mapName in overrideTypeMap: - typeMap[mapName] = overrideTypeMap[mapName] - elif mapName in typeMap and typeMap[mapName] != dataNode.getAttribute("inType"): - print("Map " + mapName + " is used multiple times with different types.") - elif not mapName in typeMap: - typeMap[mapName] = dataNode.getAttribute("inType") - - return typeMap - -def generateKeywordsClass(providerNode, outputFile, inclusion_list): - - providerName = providerNode.getAttribute("name") - - # Get the events element. - for node in providerNode.getElementsByTagName("events"): - eventsNode = node - break - - # Get the list of event nodes. - eventNodes = eventsNode.getElementsByTagName("event") - - # Build the list of used keywords - keywordSet = set() - for eventNode in eventNodes: - eventName = eventNode.getAttribute('symbol') - if not includeEvent(inclusion_list, providerName, eventName): - continue - - # Not all events have keywords specified, and some have multiple keywords specified. - keywords = eventNode.getAttribute("keywords") - if keywords: - keywordSet = keywordSet.union(keywords.split()) - - # Find the keywords element. - for node in providerNode.getElementsByTagName("keywords"): - keywordsNode = node - break; - - writeOutput(outputFile, "public static class Keywords\n") - writeOutput(outputFile, "{\n") - increaseTabLevel() - - for keywordNode in keywordsNode.getElementsByTagName("keyword"): - keywordName = keywordNode.getAttribute("name") - if keywordName not in keywordSet: - continue; - - writeOutput(outputFile, "public const EventKeywords " + keywordName + " = (EventKeywords)" + keywordNode.getAttribute("mask") + ";\n") - - decreaseTabLevel() - writeOutput(outputFile, "}\n\n") - -def loadStringTable(manifest): - - # Create the string table dictionary. - stringTable = dict() - - # Get the string table element. - for node in manifest.getElementsByTagName("stringTable"): - stringTableNode = node - break - - # Iterate through each string and save it. - for stringElem in stringTableNode.getElementsByTagName("string"): - stringTable[stringElem.getAttribute("id")] = stringElem.getAttribute("value") - - return stringTable - -def generateEventSources(manifestFullPath, intermediatesDirFullPath, inclusion_list): - - # Open the manifest for reading. - manifest = DOM.parse(manifestFullPath) - - # Load the string table. - stringTable = loadStringTable(manifest) - - # Iterate over each provider that we want to generate an EventSource for. - for providerName, outputFileName in getManifestsToGenerate().items(): - for node in manifest.getElementsByTagName("provider"): - if node.getAttribute("name") == providerName: - providerNode = node - break - - if providerNode is None: - raise ValueError("Unable to find provider node.", providerName) - - # Generate a full path to the output file and open the file for open_for_update. - outputFilePath = os.path.join(intermediatesDirFullPath, outputFileName) - with open_for_update(outputFilePath) as outputFile: - - # Write the license header. - writeOutput(outputFile, generatedCodeFileHeader) - - # Write the class header. - header = """ -using System; - -namespace System.Diagnostics.Tracing -{ -""" - writeOutput(outputFile, header) - increaseTabLevel() - - className = providerNameToClassNameMap[providerName] - writeOutput(outputFile, "internal sealed partial class " + className + " : EventSource\n") - writeOutput(outputFile, "{\n") - increaseTabLevel() - - # Write the keywords class. - generateKeywordsClass(providerNode, outputFile, inclusion_list) - - #### Disable enums until they are needed #### - # Generate the enum type map. - # This determines what the backing type for each enum should be. - # enumTypeMap = generateEnumTypeMap(providerNode) - - # Generate enums for value maps. - # generateValueMapEnums(providerNode, outputFile, stringTable, enumTypeMap) - - # Generate enums for bit maps. - # generateBitMapEnums(providerNode, outputFile, stringTable, enumTypeMap) - #### Disable enums until they are needed #### - - # Generate events. - generateEvents(providerNode, outputFile, stringTable, inclusion_list) - - # Write the class footer. - decreaseTabLevel() - writeOutput(outputFile, "}\n") - decreaseTabLevel() - writeOutput(outputFile, "}\n") - -def main(argv): - - # Parse command line arguments. - parser = argparse.ArgumentParser( - description="Generates C# EventSource classes that represent the runtime's native event providers.") - - required = parser.add_argument_group('required arguments') - required.add_argument('--man', type=str, required=True, - help='full path to manifest containing the description of events') - required.add_argument('--intermediate', type=str, required=True, - help='full path to eventprovider intermediate directory') - required.add_argument('--inc', type=str,default="", - help='full path to inclusion list') - args, unknown = parser.parse_known_args(argv) - if unknown: - print('Unknown argument(s): ', ', '.join(unknown)) - return 1 - - manifestFullPath = args.man - intermediatesDirFullPath = args.intermediate - inclusion_filename = args.inc - - # Ensure the intermediates directory exists. - try: - os.makedirs(intermediatesDirFullPath) - except OSError: - if not os.path.isdir(intermediatesDirFullPath): - raise - - inclusion_list = parseInclusionList(inclusion_filename) - - # Generate event sources. - generateEventSources(manifestFullPath, intermediatesDirFullPath, inclusion_list) - return 0 - -if __name__ == '__main__': - return_code = main(sys.argv[1:]) - sys.exit(return_code) diff --git a/src/libraries/System.Private.CoreLib/gen/NativeRuntimeEventSourceGenerator.cs b/src/libraries/System.Private.CoreLib/gen/NativeRuntimeEventSourceGenerator.cs new file mode 100644 index 00000000000000..5d8c66a600857e --- /dev/null +++ b/src/libraries/System.Private.CoreLib/gen/NativeRuntimeEventSourceGenerator.cs @@ -0,0 +1,424 @@ +// 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.IO; +using System.Linq; +using System.Xml.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; + +namespace Generators +{ + [Generator] + public sealed class NativeRuntimeEventSourceGenerator : IIncrementalGenerator + { + private static readonly XNamespace EventNs = "http://schemas.microsoft.com/win/2004/08/events"; + + public void Initialize(IncrementalGeneratorInitializationContext context) + { + IncrementalValuesProvider manifestFiles = context.AdditionalTextsProvider.Where(f => f.Path.EndsWith(".man", StringComparison.OrdinalIgnoreCase)); + IncrementalValuesProvider inclusionFiles = context.AdditionalTextsProvider.Where(f => f.Path.EndsWith(".lst", StringComparison.OrdinalIgnoreCase)); + + IncrementalValuesProvider<(AdditionalText Left, System.Collections.Immutable.ImmutableArray Right)> combined = manifestFiles.Combine(inclusionFiles.Collect()); + + context.RegisterSourceOutput(combined, (spc, tuple) => + { + AdditionalText manifestFile = tuple.Left; + System.Collections.Immutable.ImmutableArray inclusionFiles = tuple.Right; + string manifestText = manifestFile.GetText(spc.CancellationToken)?.ToString(); + if (string.IsNullOrEmpty(manifestText)) + { + return; + } + + var manifest = XDocument.Parse(manifestText); + + string inclusionText = inclusionFiles.FirstOrDefault()?.GetText(spc.CancellationToken)?.ToString(); + + Dictionary> inclusionList = ParseInclusionListFromString(inclusionText); + + foreach (KeyValuePair kvp in manifestsToGenerate) + { + string providerName = kvp.Key; + string className = providerNameToClassNameMap[providerName]; + XElement? providerNode = manifest + .Descendants(EventNs + "provider") + .FirstOrDefault(e => (string)e.Attribute("name") == providerName); + + if (providerNode is null) + { + continue; + } + + string source = GenerateEventSourceClass(providerNode, className, inclusionList); + spc.AddSource($"{className}.g.cs", SourceText.From(source, System.Text.Encoding.UTF8)); + } + }); + } + + private static Dictionary> ParseInclusionListFromString(string? inclusionText) + { + Dictionary> inclusionList = []; + if (string.IsNullOrEmpty(inclusionText)) + { + return inclusionList; + } + + using var reader = new StringReader(inclusionText); + string line; + while ((line = reader.ReadLine()) != null) + { + string trimmed = line.Trim(); + if (string.IsNullOrEmpty(trimmed) || trimmed.StartsWith("#")) + { + continue; + } + + string[] tokens = trimmed.Split(':'); + if (tokens.Length == 0) + { + continue; + } + + if (tokens.Length > 2) + { + continue; + } + + string providerName, eventName; + if (tokens.Length == 2) + { + providerName = tokens[0]; + eventName = tokens[1]; + } + else + { + providerName = "*"; + eventName = tokens[0]; + } + if (!inclusionList.TryGetValue(providerName, out HashSet? value)) + { + value = []; + inclusionList[providerName] = value; + } + + value.Add(eventName); + } + return inclusionList; + } + + private static bool IncludeEvent(Dictionary> inclusionList, string providerName, string eventName) + { + if (inclusionList == null || inclusionList.Count == 0) + { + return true; + } + + if (inclusionList.TryGetValue(providerName, out HashSet? events) && events.Contains(eventName)) + { + return true; + } + + if (inclusionList.TryGetValue("*", out HashSet? wildcardEvents) && wildcardEvents.Contains(eventName)) + { + return true; + } + + return false; + } + + private static string GenerateEventSourceClass(XElement providerNode, string className, Dictionary> inclusionList) + { + var sw = new StringWriter(); + + sw.WriteLine($$""" + // Licensed to the .NET Foundation under one or more agreements. + // The .NET Foundation licenses this file to you under the MIT license. + // + + using System; + + namespace System.Diagnostics.Tracing + { + internal sealed partial class {{className}} : EventSource + { + """); + + GenerateKeywordsClass(providerNode, sw, inclusionList); + GenerateEventMethods(providerNode, sw, inclusionList); + + sw.WriteLine(""" + } + } + """); + return sw.ToString(); + } + + private static void GenerateKeywordsClass(XElement providerNode, StringWriter writer, Dictionary> inclusionList) + { + string? providerName = providerNode.Attribute("name")?.Value; + + if (providerName is null) + { + return; + } + + XElement eventsNode = providerNode.Element(EventNs + "events"); + if (eventsNode is null) + { + return; + } + + IEnumerable eventNodes = eventsNode.Elements(EventNs + "event"); + var usedKeywords = new HashSet(); + foreach (XElement? eventNode in eventNodes) + { + string? eventName = eventNode.Attribute("symbol")?.Value; + + if (eventName is null + || !IncludeEvent(inclusionList, providerName, eventName)) + { + continue; + } + + string? keywords = eventNode.Attribute("keywords")?.Value; + if (!string.IsNullOrEmpty(keywords)) + { + foreach (string? kw in keywords.Split([' '], StringSplitOptions.RemoveEmptyEntries)) + { + usedKeywords.Add(kw); + } + } + } + XElement? keywordsNode = providerNode.Element(EventNs + "keywords"); + if (keywordsNode is null) + { + return; + } + + writer.WriteLine(""" + public static class Keywords + { + """); + + foreach (XElement keywordNode in keywordsNode.Elements(EventNs + "keyword")) + { + string? name = keywordNode.Attribute("name")?.Value; + string? mask = keywordNode.Attribute("mask")?.Value; + if (name is not null && mask is not null && usedKeywords.Contains(name)) + { + writer.WriteLine($" public const EventKeywords {name} = (EventKeywords){mask};"); + } + } + + writer.WriteLine(""" + } + + """); + } + + private static void GenerateEventMethods(XElement providerNode, StringWriter writer, Dictionary> inclusionList) + { + string? providerName = providerNode.Attribute("name")?.Value; + + if (providerName is null) + { + return; + } + + XElement eventsNode = providerNode.Element(EventNs + "events"); + if (eventsNode == null) + { + return; + } + + var eventNodes = eventsNode.Elements(EventNs + "event").ToList(); + XElement templatesNode = providerNode.Element(EventNs + "templates"); + var templateDict = new Dictionary(); + if (templatesNode != null) + { + foreach (XElement? template in templatesNode.Elements(EventNs + "template")) + { + string? name = template.Attribute("tid")?.Value; + if (!string.IsNullOrEmpty(name)) + { + templateDict[name] = template; + } + } + } + + // Build a dictionary of eventID -> latest version + Dictionary latestEventVersions = []; + foreach (XElement? eventNode in eventNodes) + { + string? eventName = eventNode.Attribute("symbol")?.Value; + if (eventName is null + || !IncludeEvent(inclusionList, providerName, eventName)) + { + continue; + } + + string? eventId = eventNode.Attribute("value")?.Value; + string? version = eventNode.Attribute("version")?.Value; + if (eventId is not null && version is not null) + { + if (!latestEventVersions.TryGetValue(eventId, out string? existingVersion) || string.CompareOrdinal(version, existingVersion) > 0) + { + latestEventVersions[eventId] = version; + } + } + } + + foreach (XElement? eventNode in eventNodes) + { + string? eventName = eventNode.Attribute("symbol")?.Value; + if (eventName is null + || !IncludeEvent(inclusionList, providerName, eventName)) + { + continue; + } + + if (IsEventManuallyHandled(eventName)) + { + continue; + } + + string? eventId = eventNode.Attribute("value")?.Value; + string? version = eventNode.Attribute("version")?.Value; + // Only emit the event if it is the latest version for this eventId + if (eventId is null || version is null || latestEventVersions[eventId] != version) + { + continue; + } + + string? level = eventNode.Attribute("level")?.Value; + IEnumerable? keywords = eventNode + .Attribute("keywords") + ?.Value + .ToString() + .Split([' '], StringSplitOptions.RemoveEmptyEntries) + .Select(k => $"Keywords.{k}"); + + writer.Write($" [Event({eventId}, Version = {version}, Level = EventLevel.{level?.Replace("win:", "")}"); + + if (keywords?.Any() == true) + { + writer.Write($", Keywords = {string.Join(" | ", keywords)}"); + } + + writer.WriteLine(")]"); + + // Write the method signature + writer.Write($" private void {eventName}("); + + string? templateValue = eventNode.Attribute("template")?.Value; + + if (!string.IsNullOrEmpty(templateValue) + && templateDict.TryGetValue(templateValue, out XElement? template)) + { + IEnumerable dataNodes = template.Elements(EventNs + "data").ToArray(); + var paramList = new List(); + + // Calculate the number of arguments to emit. + // COMPAT: Cut the parameter list at any binary or ansi string arguments, + // or if the count attribute is set on any of the parameters. + int numArgumentsToEmit = 0; + foreach (XElement data in dataNodes) + { + string? paramType = data.Attribute("inType")?.Value.ToString(); + + if (paramType is "win:Binary" or "win:AnsiString") + { + break; + } + + if (!string.IsNullOrEmpty(data.Attribute("count")?.Value)) + { + break; + } + + numArgumentsToEmit++; + } + + foreach (XElement data in dataNodes) + { + if (numArgumentsToEmit-- <= 0) + { + break; + } + + string? paramType = data.Attribute("inType")?.Value; + string? paramName = data.Attribute("name")?.Value; + if (paramType is not null && paramName is not null + && manifestTypeToCSharpTypeMap.TryGetValue(paramType, out string? csType)) + { + paramList.Add($"{csType} {paramName}"); + } + else if (paramType is not null && paramName is not null) + { + paramList.Add($"object {paramName}"); + } + } + writer.Write(string.Join(", ", paramList)); + } + + writer.WriteLine(""" + ) + { + // To have this event be emitted from managed side, refer to NativeRuntimeEventSource.cs + throw new NotImplementedException(); + } + + """); + } + } + + private static bool IsEventManuallyHandled(string eventName) + { + foreach (string handledEvent in manuallyHandledEventSymbols) + { + if (eventName.StartsWith(handledEvent, StringComparison.Ordinal)) + { + return true; + } + } + return false; + } + + private static readonly Dictionary manifestsToGenerate = new() + { + { "Microsoft-Windows-DotNETRuntime", "NativeRuntimeEventSource.Generated.cs" } + }; + + private static readonly Dictionary providerNameToClassNameMap = new() + { + { "Microsoft-Windows-DotNETRuntime", "NativeRuntimeEventSource" } + }; + + private static readonly Dictionary manifestTypeToCSharpTypeMap = new() + { + { "win:UInt8", "byte" }, + { "win:UInt16", "ushort" }, + { "win:UInt32", "uint" }, + { "win:UInt64", "ulong" }, + { "win:Int32", "int" }, + { "win:Int64", "long" }, + { "win:Pointer", "IntPtr" }, + { "win:UnicodeString", "string" }, + { "win:Binary", "byte[]" }, + { "win:Double", "double" }, + { "win:Boolean", "bool" }, + { "win:GUID", "Guid" }, + }; + + private static readonly List manuallyHandledEventSymbols = + [ + // Some threading events are defined manually in NativeRuntimeEventSource.Threading.cs + "ThreadPool", + "Contention", + "WaitHandle" + ]; + } +} diff --git a/src/libraries/System.Private.CoreLib/gen/System.Private.CoreLib.Generators.csproj b/src/libraries/System.Private.CoreLib/gen/System.Private.CoreLib.Generators.csproj index 9abeb30c867e03..bb2ffa89ff7f3d 100644 --- a/src/libraries/System.Private.CoreLib/gen/System.Private.CoreLib.Generators.csproj +++ b/src/libraries/System.Private.CoreLib/gen/System.Private.CoreLib.Generators.csproj @@ -2,6 +2,7 @@ netstandard2.0 $(NoWarn);CS3001 + true $(DefineConstants);STABILIZE_PACKAGE_VERSION @@ -13,6 +14,7 @@ + @@ -20,7 +22,7 @@ - + diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj index 5167fbe87959f9..3bcb19fc3ac2e7 100644 --- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -311,33 +311,7 @@ - - - - - src\System\Diagnostics\Eventing\NativeRuntimeEventSource.Generated.cs - - + + - - - - - - <_PythonWarningParameter>-Wall - <_PythonWarningParameter Condition="'$(MSBuildTreatWarningsAsErrors)' == 'true'">$(_PythonWarningParameter) -Werror - <_EventingSourceFileDirectory>%(EventingSourceFile.RootDir)%(EventingSourceFile.Directory) - <_EventingSourceFileDirectory Condition="HasTrailingSlash('$(_EventingSourceFileDirectory)')">$(_EventingSourceFileDirectory.TrimEnd('\')) - - - - - - - -