2 ## Licensed to the .NET Foundation under one or more agreements.
3 ## The .NET Foundation licenses this file to you under the MIT license.
4 ## See the LICENSE file in the project root for more information.
8 import xml.dom.minidom as DOM
9 from utilities import open_for_update
13 generatedCodeFileHeader="""// Licensed to the .NET Foundation under one or more agreements.
14 // The .NET Foundation licenses this file to you under the MIT license.
15 // See the LICENSE file in the project root for more information.
17 /**********************************************************************
19 DO NOT MODIFY. AUTOGENERATED FILE.
20 This file is generated by <root>/src/scripts/genRuntimeEventSources.py
22 **********************************************************************/
25 ########################################################################
27 ########################################################################
28 manifestsToGenerate = {
29 "Microsoft-Windows-DotNETRuntime" : "DotNETRuntimeEventSource.cs"
32 providerNameToClassNameMap = {
33 "Microsoft-Windows-DotNETRuntime" : "RuntimeEventSource"
36 manifestTypeToCSharpTypeMap = {
38 "win:UInt16" : "UInt16",
39 "win:UInt32" : "UInt32",
40 "win:UInt64" : "UInt64",
41 "win:Int32" : "Int32",
42 "win:Pointer" : "IntPtr",
43 "win:UnicodeString" : "string",
44 "win:Binary" : "byte[]",
45 "win:Double" : "double",
46 "win:Boolean" : "bool",
50 overrideEnumBackingTypes = {
51 "Microsoft-Windows-DotNETRuntime" : {
52 "GCSuspendEEReasonMap" : "win:UInt32",
53 "GCRootKindMap" : "win:UInt32"
56 ########################################################################
58 ########################################################################
62 def increaseTabLevel():
66 def decreaseTabLevel():
68 tabText = tabText[:-4]
70 def writeOutput(outputFile, str):
71 outputFile.write(tabText + str)
73 def getCSharpTypeFromManifestType(manifestType):
74 return manifestTypeToCSharpTypeMap[manifestType]
76 def generateEvent(eventNode, providerNode, outputFile, stringTable):
78 # Write the event attribute.
79 writeOutput(outputFile, "[Event("+ eventNode.getAttribute("value") + ", Version = " + eventNode.getAttribute("version") + ", Level = EventLevel." + eventNode.getAttribute("level")[4:])
81 # Not all events have keywords specified, and some have multiple keywords specified.
82 keywords = eventNode.getAttribute("keywords")
84 if " " not in keywords:
85 outputFile.write(", Keywords = Keywords." + keywords)
87 keywords = keywords.split()
88 outputFile.write(", Keywords = ")
89 for keywordIndex in range(len(keywords)):
90 outputFile.write("Keywords." + keywords[keywordIndex])
91 if keywordIndex < (len(keywords) - 1):
92 outputFile.write(" | ")
94 outputFile.write(")]\n")
96 # Get the template for the event.
98 templateKey = eventNode.getAttribute("template")
99 if templateKey is not None:
100 for node in providerNode.getElementsByTagName("templates"):
103 for node in templatesNode.getElementsByTagName("template"):
104 if node.getAttribute("tid") == templateKey:
108 # Write the beginning of the method signature.
109 writeOutput(outputFile, "private void " + eventNode.getAttribute("symbol") + "(")
111 # Write the function signature.
113 if templateNode is not None:
114 argumentNodes = templateNode.childNodes
116 # Calculate the number of arguments.
117 for argumentNode in argumentNodes:
118 if argumentNode.nodeName == "data":
119 if argumentNode.getAttribute("inType") != "win:Binary" and argumentNode.getAttribute("inType") != "win:AnsiString" and argumentNode.getAttribute("count") == "":
123 elif argumentNode.nodeName == "struct":
126 argumentsProcessed = 0
127 for argumentIndex in range(len(argumentNodes)):
128 argumentNode = argumentNodes[argumentIndex]
129 if argumentNode.nodeName == "data":
130 argumentName = argumentNode.getAttribute("name")
131 argumentInType = argumentNode.getAttribute("inType")
133 #### Disable enums until they are needed ####
134 # argumentMap = argumentNode.getAttribute("map")
135 # if not argumentMap:
136 # argumentCSharpType = getCSharpTypeFromManifestType(argumentInType)
138 # argumentCSharpType = argumentMap[:-3]
139 #### Disable enums until they are needed ####
141 argumentCSharpType = getCSharpTypeFromManifestType(argumentInType)
142 outputFile.write(argumentCSharpType + " " + argumentName)
143 argumentsProcessed += 1
144 if argumentsProcessed < argumentCount:
145 outputFile.write(", ")
146 if argumentsProcessed == argumentCount:
149 outputFile.write(")\n")
150 writeOutput(outputFile, "{\n")
152 # Write the call to WriteEvent.
154 writeOutput(outputFile, "WriteEvent(" + eventNode.getAttribute("value"))
156 # Add method parameters.
157 if argumentCount > 0:
158 # A ',' is needed after the event id.
159 outputFile.write(", ")
161 # Write the parameter names to the method call.
162 argumentsProcessed = 0
163 argumentNodes = templateNode.getElementsByTagName("data")
164 for argumentIndex in range(argumentCount):
165 argumentNode = argumentNodes[argumentIndex]
166 argumentName = argumentNode.getAttribute("name")
167 outputFile.write(argumentName)
168 if argumentIndex < (argumentCount - 1):
169 outputFile.write(", ")
171 outputFile.write(");\n")
174 writeOutput(outputFile, "}\n\n")
177 def generateEvents(providerNode, outputFile, stringTable):
179 # Get the events element.
180 for node in providerNode.getElementsByTagName("events"):
184 # Get the list of event nodes.
185 eventNodes = eventsNode.getElementsByTagName("event")
187 # Build a list of events to be emitted. This is where old versions of events are stripped.
188 # key = eventID, value = version
190 for eventNode in eventNodes:
191 eventID = eventNode.getAttribute("value")
192 eventVersion = eventNode.getAttribute("version")
193 eventList[eventID] = eventVersion
195 # Iterate over each event node and process it.
196 # Only emit events for the latest version of the event, otherwise EventSource initialization will fail.
197 for eventNode in eventNodes:
198 eventID = eventNode.getAttribute("value")
199 eventVersion = eventNode.getAttribute("version")
200 if eventID in eventList and eventList[eventID] == eventVersion:
201 generateEvent(eventNode, providerNode, outputFile, stringTable)
202 elif eventID not in eventList:
203 raise ValueError("eventID could not be found in the list of events to emit.", eventID)
205 def generateValueMapEnums(providerNode, outputFile, stringTable, enumTypeMap):
207 # Get the maps element.
208 for node in providerNode.getElementsByTagName("maps"):
212 # Iterate over each map and create an enum out of it.
213 for valueMapNode in mapsNode.getElementsByTagName("valueMap"):
215 # Get the backing type of the enum.
216 typeName = enumTypeMap[valueMapNode.getAttribute("name")]
218 raise ValueError("No mapping from mapName to enum backing type.", valueMapNode.getAttribute("name"))
220 enumType = getCSharpTypeFromManifestType(typeName)
221 writeOutput(outputFile, "public enum " + valueMapNode.getAttribute("name")[:-3] + " : " + enumType + "\n")
222 writeOutput(outputFile, "{\n")
224 for mapNode in valueMapNode.getElementsByTagName("map"):
225 # Each map value has a message, which we should use as the enum value.
226 messageKey = mapNode.getAttribute("message")[9:-1]
227 writeOutput(outputFile, stringTable[messageKey] + " = " + mapNode.getAttribute("value") + ",\n")
229 writeOutput(outputFile, "}\n\n")
231 def generateBitMapEnums(providerNode, outputFile, stringTable, enumTypeMap):
233 # Get the maps element.
234 for node in providerNode.getElementsByTagName("maps"):
238 # Iterate over each map and create an enum out of it.
239 for valueMapNode in mapsNode.getElementsByTagName("bitMap"):
241 # Get the backing type of the enum.
242 typeName = enumTypeMap[valueMapNode.getAttribute("name")]
244 raise ValueError("No mapping from mapName to enum backing type.", valueMapNode.getAttribute("name"))
246 enumType = getCSharpTypeFromManifestType(typeName)
247 writeOutput(outputFile, "[Flags]\n")
248 writeOutput(outputFile, "public enum " + valueMapNode.getAttribute("name")[:-3] + " : " + enumType + "\n")
249 writeOutput(outputFile, "{\n")
251 for mapNode in valueMapNode.getElementsByTagName("map"):
252 # Each map value has a message, which we should use as the enum value.
253 messageKey = mapNode.getAttribute("message")[9:-1]
254 writeOutput(outputFile, stringTable[messageKey] + " = " + mapNode.getAttribute("value") + ",\n")
256 writeOutput(outputFile, "}\n\n")
258 def generateEnumTypeMap(providerNode):
260 providerName = providerNode.getAttribute("name")
261 templatesNodes = providerNode.getElementsByTagName("templates")
262 templatesNode = templatesNodes[0]
263 mapsNodes = providerNode.getElementsByTagName("maps")
265 # Keep a list of mapName -> inType.
266 # This map contains the first inType seen for the specified mapName.
269 # There are a couple of maps that are used by multiple events but have different backing types.
270 # Because only one of the uses will be consumed by EventSource/EventListener we can hack the backing type here
271 # and suppress the warning that we'd otherwise get.
272 overrideTypeMap = dict()
273 if providerName in overrideEnumBackingTypes:
274 overrideTypeMap = overrideEnumBackingTypes[providerName]
276 for mapsNode in mapsNodes:
277 for valueMapNode in mapsNode.getElementsByTagName("valueMap"):
278 mapName = valueMapNode.getAttribute("name")
279 dataNodes = templatesNode.getElementsByTagName("data")
281 # If we've never seen the map used, save its usage with the inType.
282 # If we have seen the map used, make sure that the inType saved previously matches the current inType.
283 for dataNode in dataNodes:
284 if dataNode.getAttribute("map") == mapName:
285 if mapName in overrideTypeMap:
286 typeMap[mapName] = overrideTypeMap[mapName]
287 elif mapName in typeMap and typeMap[mapName] != dataNode.getAttribute("inType"):
288 print("WARNING: Map " + mapName + " is used multiple times with different types. This may cause functional bugs in tracing.")
289 elif not mapName in typeMap:
290 typeMap[mapName] = dataNode.getAttribute("inType")
291 for bitMapNode in mapsNode.getElementsByTagName("bitMap"):
292 mapName = bitMapNode.getAttribute("name")
293 dataNodes = templatesNode.getElementsByTagName("data")
295 # If we've never seen the map used, save its usage with the inType.
296 # If we have seen the map used, make sure that the inType saved previously matches the current inType.
297 for dataNode in dataNodes:
298 if dataNode.getAttribute("map") == mapName:
299 if mapName in overrideTypeMap:
300 typeMap[mapName] = overrideTypeMap[mapName]
301 elif mapName in typeMap and typeMap[mapName] != dataNode.getAttribute("inType"):
302 print("Map " + mapName + " is used multiple times with different types.")
303 elif not mapName in typeMap:
304 typeMap[mapName] = dataNode.getAttribute("inType")
308 def generateKeywordsClass(providerNode, outputFile):
310 # Find the keywords element.
311 for node in providerNode.getElementsByTagName("keywords"):
315 writeOutput(outputFile, "public class Keywords\n")
316 writeOutput(outputFile, "{\n")
319 for keywordNode in keywordsNode.getElementsByTagName("keyword"):
320 writeOutput(outputFile, "public const EventKeywords " + keywordNode.getAttribute("name") + " = (EventKeywords)" + keywordNode.getAttribute("mask") + ";\n")
323 writeOutput(outputFile, "}\n\n")
325 def loadStringTable(manifest):
327 # Create the string table dictionary.
330 # Get the string table element.
331 for node in manifest.getElementsByTagName("stringTable"):
332 stringTableNode = node
335 # Iterate through each string and save it.
336 for stringElem in stringTableNode.getElementsByTagName("string"):
337 stringTable[stringElem.getAttribute("id")] = stringElem.getAttribute("value")
341 def generateEventSources(manifestFullPath, intermediatesDirFullPath):
343 # Open the manifest for reading.
344 manifest = DOM.parse(manifestFullPath)
346 # Load the string table.
347 stringTable = loadStringTable(manifest)
349 # Iterate over each provider that we want to generate an EventSource for.
350 for providerName, outputFileName in manifestsToGenerate.items():
351 for node in manifest.getElementsByTagName("provider"):
352 if node.getAttribute("name") == providerName:
356 if providerNode is None:
357 raise ValueError("Unable to find provider node.", providerName)
359 # Generate a full path to the output file and open the file for open_for_update.
360 outputFilePath = os.path.join(intermediatesDirFullPath, outputFileName)
361 with open_for_update(outputFilePath) as outputFile:
363 # Write the license header.
364 writeOutput(outputFile, generatedCodeFileHeader)
366 # Write the class header.
370 namespace System.Diagnostics.Tracing
373 writeOutput(outputFile, header)
376 className = providerNameToClassNameMap[providerName]
377 writeOutput(outputFile, "[EventSource(Name = \"" + providerName + "\")]\n")
378 writeOutput(outputFile, "internal sealed partial class " + className + " : EventSource\n")
379 writeOutput(outputFile, "{\n")
382 # Create a static property for the EventSource name so that we don't have to initialize the EventSource to get its name.
383 writeOutput(outputFile, "internal const string EventSourceName = \"" + providerName + "\";\n")
385 # Write the static Log property.
386 writeOutput(outputFile, "internal static " + className + " Log = new " + className + "();\n\n")
388 # Write the keywords class.
389 generateKeywordsClass(providerNode, outputFile)
391 #### Disable enums until they are needed ####
392 # Generate the enum type map.
393 # This determines what the backing type for each enum should be.
394 # enumTypeMap = generateEnumTypeMap(providerNode)
396 # Generate enums for value maps.
397 # generateValueMapEnums(providerNode, outputFile, stringTable, enumTypeMap)
399 # Generate enums for bit maps.
400 # generateBitMapEnums(providerNode, outputFile, stringTable, enumTypeMap)
401 #### Disable enums until they are needed ####
404 generateEvents(providerNode, outputFile, stringTable)
406 # Write the class footer.
408 writeOutput(outputFile, "}\n")
410 writeOutput(outputFile, "}")
414 # Parse command line arguments.
415 parser = argparse.ArgumentParser(
416 description="Generates C# EventSource classes that represent the runtime's native event providers.")
418 required = parser.add_argument_group('required arguments')
419 required.add_argument('--man', type=str, required=True,
420 help='full path to manifest containig the description of events')
421 required.add_argument('--intermediate', type=str, required=True,
422 help='full path to eventprovider intermediate directory')
423 args, unknown = parser.parse_known_args(argv)
425 print('Unknown argument(s): ', ', '.join(unknown))
428 manifestFullPath = args.man
429 intermediatesDirFullPath = args.intermediate
431 # Ensure the intermediates directory exists.
433 os.makedirs(intermediatesDirFullPath)
435 if not os.path.isdir(intermediatesDirFullPath):
438 # Generate event sources.
439 generateEventSources(manifestFullPath, intermediatesDirFullPath)
442 if __name__ == '__main__':
443 return_code = main(sys.argv[1:])
444 sys.exit(return_code)