diff options
author | Brian Robbins <brianrob@microsoft.com> | 2018-05-16 18:59:51 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-05-16 18:59:51 -0700 |
commit | 0fbd0f535e566bcde591701adc2de51ee0f020af (patch) | |
tree | 0f9ad83b02b124bae036fe45fd659a146f63221e /src/scripts | |
parent | a8d40ac8362bacd98f7fa422190fe66916e040e8 (diff) | |
download | coreclr-0fbd0f535e566bcde591701adc2de51ee0f020af.tar.gz coreclr-0fbd0f535e566bcde591701adc2de51ee0f020af.tar.bz2 coreclr-0fbd0f535e566bcde591701adc2de51ee0f020af.zip |
Generate EventSources Representing DotNETRuntime Eventing Providers (#18007)
Diffstat (limited to 'src/scripts')
-rw-r--r-- | src/scripts/genRuntimeEventSources.py | 419 | ||||
-rw-r--r-- | src/scripts/scripts.pyproj | 38 |
2 files changed, 457 insertions, 0 deletions
diff --git a/src/scripts/genRuntimeEventSources.py b/src/scripts/genRuntimeEventSources.py new file mode 100644 index 0000000000..5795a4d22a --- /dev/null +++ b/src/scripts/genRuntimeEventSources.py @@ -0,0 +1,419 @@ +# +## Licensed to the .NET Foundation under one or more agreements. +## The .NET Foundation licenses this file to you under the MIT license. +## See the LICENSE file in the project root for more information. +# + +import os +import xml.dom.minidom as DOM +from utilities import open_for_update +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. +// See the LICENSE file in the project root for more information. + +/********************************************************************** + +DO NOT MODIFY. AUTOGENERATED FILE. +This file is generated by <root>/src/scripts/genRuntimeEventSources.py + +**********************************************************************/ +""" + +######################################################################## +# START CONFIGURATION +######################################################################## +manifestsToGenerate = { + "Microsoft-Windows-DotNETRuntime" : "DotNETRuntimeEventSource.cs" +} + +providerNameToClassNameMap = { + "Microsoft-Windows-DotNETRuntime" : "RuntimeEventSource" +} + +manifestTypeToCSharpTypeMap = { + "win:UInt8" : "byte", + "win:UInt16" : "UInt16", + "win:UInt32" : "UInt32", + "win:UInt64" : "UInt64", + "win:Int32" : "Int32", + "win:Pointer" : "UIntPtr", + "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 generateEvent(eventNode, providerNode, outputFile, stringTable): + + # Write the event attribute. + writeOutput(outputFile, "[Event("+ eventNode.getAttribute("value") + ", Version = " + eventNode.getAttribute("version") + ", Level = EventLevel." + eventNode.getAttribute("level")[4:]) + + # 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) + else: + keywords = keywords.split() + outputFile.write(", Keywords = ") + for keywordIndex in range(len(keywords)): + outputFile.write("Keywords." + keywords[keywordIndex]) + if keywordIndex < (len(keywords) - 1): + outputFile.write(" | ") + + 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": + 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") + + # Write the call to WriteEvent. + increaseTabLevel() + writeOutput(outputFile, "WriteEvent(" + eventNode.getAttribute("value")) + + # Add method parameters. + if argumentCount > 0: + # A ',' is needed after the event id. + outputFile.write(", ") + + # Write the parameter names to the method call. + argumentsProcessed = 0 + argumentNodes = templateNode.getElementsByTagName("data") + for argumentIndex in range(argumentCount): + argumentNode = argumentNodes[argumentIndex] + argumentName = argumentNode.getAttribute("name") + outputFile.write(argumentName) + if argumentIndex < (argumentCount - 1): + outputFile.write(", ") + + outputFile.write(");\n") + decreaseTabLevel() + + writeOutput(outputFile, "}\n\n") + + +def generateEvents(providerNode, outputFile, stringTable): + + # Get the events element. + for node in providerNode.getElementsByTagName("events"): + eventsNode = node + break + + # Iterate over each event node and process it. + for eventNode in eventsNode.getElementsByTagName("event"): + generateEvent(eventNode, providerNode, outputFile, stringTable) + +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): + + # Find the keywords element. + for node in providerNode.getElementsByTagName("keywords"): + keywordsNode = node + break; + + writeOutput(outputFile, "public class Keywords\n") + writeOutput(outputFile, "{\n") + increaseTabLevel() + + for keywordNode in keywordsNode.getElementsByTagName("keyword"): + writeOutput(outputFile, "public const EventKeywords " + keywordNode.getAttribute("name") + " = (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): + + # 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 manifestsToGenerate.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() + writeOutput(outputFile, "[EventSource(Name = \"" + providerName + "\", Guid = \"" + providerNode.getAttribute("guid") + "\")]\n") + writeOutput(outputFile, "internal sealed unsafe class " + providerNameToClassNameMap[providerName] + " : EventSource\n") + writeOutput(outputFile, "{\n") + increaseTabLevel() + + # Write the keywords class. + generateKeywordsClass(providerNode, outputFile) + + #### 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) + + # Write the class footer. + decreaseTabLevel() + writeOutput(outputFile, "}\n") + decreaseTabLevel() + writeOutput(outputFile, "}") + +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 containig the description of events') + required.add_argument('--intermediate', type=str, required=True, + help='full path to eventprovider intermediate directory') + args, unknown = parser.parse_known_args(argv) + if unknown: + print('Unknown argument(s): ', ', '.join(unknown)) + return 1 + + manifestFullPath = args.man + intermediatesDirFullPath = args.intermediate + + # Ensure the intermediates directory exists. + try: + os.makedirs(intermediatesDirFullPath) + except OSError: + if not os.path.isdir(intermediatesDirFullPath): + raise + + # Generate event sources. + generateEventSources(manifestFullPath, intermediatesDirFullPath) + return 0 + +if __name__ == '__main__': + return_code = main(sys.argv[1:]) + sys.exit(return_code) diff --git a/src/scripts/scripts.pyproj b/src/scripts/scripts.pyproj new file mode 100644 index 0000000000..c0b7bef641 --- /dev/null +++ b/src/scripts/scripts.pyproj @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <SchemaVersion>2.0</SchemaVersion> + <ProjectGuid>{9d304b80-696f-4873-85ca-cbd2fdb900af}</ProjectGuid> + <ProjectHome /> + <StartupFile>genRuntimeEventSources.py</StartupFile> + <SearchPath /> + <WorkingDirectory>.</WorkingDirectory> + <OutputPath>.</OutputPath> + <ProjectTypeGuids>{888888a0-9f3d-457c-b088-3a5042f75d52}</ProjectTypeGuids> + <LaunchProvider>Standard Python launcher</LaunchProvider> + <InterpreterId>Global|PythonCore|2.7</InterpreterId> + <CommandLineArguments>--man c:\src\coreclr\src\vm\clretwall.man --intermediate c:\work\runtimeeventsource</CommandLineArguments> + <EnableNativeCodeDebugging>False</EnableNativeCodeDebugging> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)' == 'Debug'" /> + <PropertyGroup Condition="'$(Configuration)' == 'Release'" /> + <PropertyGroup> + <VisualStudioVersion Condition=" '$(VisualStudioVersion)' == '' ">10.0</VisualStudioVersion> + </PropertyGroup> + <ItemGroup> + <Compile Include="check-definitions.py" /> + <Compile Include="genDummyProvider.py" /> + <Compile Include="genEtwProvider.py" /> + <Compile Include="genEventing.py" /> + <Compile Include="genEventPipe.py" /> + <Compile Include="genLttngProvider.py" /> + <Compile Include="genRuntimeEventSources.py" /> + <Compile Include="pgocheck.py" /> + <Compile Include="utilities.py" /> + </ItemGroup> + <ItemGroup> + <InterpreterReference Include="Global|PythonCore|2.7" /> + </ItemGroup> + <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.targets" /> +</Project>
\ No newline at end of file |