Merge pull request #18504 from mikedn/comp-small
[platform/upstream/coreclr.git] / src / scripts / genRuntimeEventSources.py
1 #
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.
5 #
6
7 import os
8 import xml.dom.minidom as DOM
9 from utilities import open_for_update
10 import argparse
11 import sys
12
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.
16
17 /**********************************************************************
18
19 DO NOT MODIFY. AUTOGENERATED FILE.
20 This file is generated by <root>/src/scripts/genRuntimeEventSources.py
21
22 **********************************************************************/
23 """
24
25 ########################################################################
26 # START CONFIGURATION
27 ########################################################################
28 manifestsToGenerate = {
29     "Microsoft-Windows-DotNETRuntime" : "DotNETRuntimeEventSource.cs"
30 }
31
32 providerNameToClassNameMap = {
33     "Microsoft-Windows-DotNETRuntime" : "RuntimeEventSource"
34 }
35
36 manifestTypeToCSharpTypeMap = {
37     "win:UInt8" : "byte",
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",
47     "win:GUID" : "Guid",
48 }
49
50 overrideEnumBackingTypes = {
51     "Microsoft-Windows-DotNETRuntime" : {
52         "GCSuspendEEReasonMap" : "win:UInt32",
53         "GCRootKindMap" : "win:UInt32"
54     }
55 }
56 ########################################################################
57 # END CONFIGURATION
58 ########################################################################
59
60 tabText = ""
61
62 def increaseTabLevel():
63     global tabText
64     tabText += "    "
65
66 def decreaseTabLevel():
67     global tabText
68     tabText = tabText[:-4]
69
70 def writeOutput(outputFile, str):
71     outputFile.write(tabText + str)
72
73 def getCSharpTypeFromManifestType(manifestType):
74     return manifestTypeToCSharpTypeMap[manifestType]
75
76 def generateEvent(eventNode, providerNode, outputFile, stringTable):
77
78     # Write the event attribute.
79     writeOutput(outputFile, "[Event("+ eventNode.getAttribute("value") + ", Version = " + eventNode.getAttribute("version") + ", Level = EventLevel." + eventNode.getAttribute("level")[4:])
80     
81     # Not all events have keywords specified, and some have multiple keywords specified.
82     keywords = eventNode.getAttribute("keywords")
83     if keywords:
84         if " " not in keywords:
85             outputFile.write(", Keywords = Keywords." + keywords)
86         else:
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(" | ")
93
94     outputFile.write(")]\n")
95
96     # Get the template for the event.
97     templateNode = None
98     templateKey = eventNode.getAttribute("template")
99     if templateKey is not None:
100         for node in providerNode.getElementsByTagName("templates"):
101             templatesNode = node
102             break
103         for node in templatesNode.getElementsByTagName("template"):
104             if node.getAttribute("tid") == templateKey:
105                 templateNode = node
106                 break
107
108     # Write the beginning of the method signature.
109     writeOutput(outputFile, "private void " + eventNode.getAttribute("symbol") + "(")
110
111     # Write the function signature.
112     argumentCount = 0
113     if templateNode is not None:
114         argumentNodes = templateNode.childNodes
115
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") == "":
120                     argumentCount += 1
121                 else:
122                     break
123             elif argumentNode.nodeName == "struct":
124                 break
125
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")
132
133                 #### Disable enums until they are needed ####
134                 # argumentMap = argumentNode.getAttribute("map")
135                 # if not argumentMap:
136                 #     argumentCSharpType = getCSharpTypeFromManifestType(argumentInType)
137                 # else:
138                 #     argumentCSharpType = argumentMap[:-3]
139                 #### Disable enums until they are needed ####
140
141                 argumentCSharpType = getCSharpTypeFromManifestType(argumentInType)
142                 outputFile.write(argumentCSharpType + " " + argumentName)
143                 argumentsProcessed += 1
144                 if argumentsProcessed < argumentCount:
145                     outputFile.write(", ")
146             if argumentsProcessed == argumentCount:
147                 break
148
149     outputFile.write(")\n")
150     writeOutput(outputFile, "{\n")
151
152     # Write the call to WriteEvent.
153     increaseTabLevel()
154     writeOutput(outputFile, "WriteEvent(" + eventNode.getAttribute("value"))
155
156     # Add method parameters.
157     if argumentCount > 0:
158         # A ',' is needed after the event id.
159         outputFile.write(", ")
160
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(", ")
170
171     outputFile.write(");\n")
172     decreaseTabLevel()
173
174     writeOutput(outputFile, "}\n\n")
175
176
177 def generateEvents(providerNode, outputFile, stringTable):
178
179     # Get the events element.
180     for node in providerNode.getElementsByTagName("events"):
181         eventsNode = node
182         break
183
184     # Get the list of event nodes.
185     eventNodes = eventsNode.getElementsByTagName("event")
186
187     # Build a list of events to be emitted.  This is where old versions of events are stripped.
188     # key = eventID, value = version
189     eventList = dict()
190     for eventNode in eventNodes:
191         eventID = eventNode.getAttribute("value")
192         eventVersion = eventNode.getAttribute("version")
193         eventList[eventID] = eventVersion
194
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)
204
205 def generateValueMapEnums(providerNode, outputFile, stringTable, enumTypeMap):
206
207     # Get the maps element.
208     for node in providerNode.getElementsByTagName("maps"):
209         mapsNode = node
210         break
211
212     # Iterate over each map and create an enum out of it.
213     for valueMapNode in mapsNode.getElementsByTagName("valueMap"):
214
215         # Get the backing type of the enum.
216         typeName = enumTypeMap[valueMapNode.getAttribute("name")]
217         if typeName is None:
218             raise ValueError("No mapping from mapName to enum backing type.", valueMapNode.getAttribute("name"))
219
220         enumType = getCSharpTypeFromManifestType(typeName)
221         writeOutput(outputFile, "public enum " + valueMapNode.getAttribute("name")[:-3] + " : " + enumType + "\n")
222         writeOutput(outputFile, "{\n")
223         increaseTabLevel()
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")
228         decreaseTabLevel()
229         writeOutput(outputFile, "}\n\n")
230
231 def generateBitMapEnums(providerNode, outputFile, stringTable, enumTypeMap):
232
233     # Get the maps element.
234     for node in providerNode.getElementsByTagName("maps"):
235         mapsNode = node
236         break
237
238     # Iterate over each map and create an enum out of it.
239     for valueMapNode in mapsNode.getElementsByTagName("bitMap"):
240
241         # Get the backing type of the enum.
242         typeName = enumTypeMap[valueMapNode.getAttribute("name")]
243         if typeName is None:
244             raise ValueError("No mapping from mapName to enum backing type.", valueMapNode.getAttribute("name"))
245
246         enumType = getCSharpTypeFromManifestType(typeName)
247         writeOutput(outputFile, "[Flags]\n")
248         writeOutput(outputFile, "public enum " + valueMapNode.getAttribute("name")[:-3] + " : " + enumType + "\n")
249         writeOutput(outputFile, "{\n")
250         increaseTabLevel()
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")
255         decreaseTabLevel()
256         writeOutput(outputFile, "}\n\n")
257
258 def generateEnumTypeMap(providerNode):
259
260     providerName = providerNode.getAttribute("name")
261     templatesNodes = providerNode.getElementsByTagName("templates")
262     templatesNode = templatesNodes[0]
263     mapsNodes = providerNode.getElementsByTagName("maps")
264
265     # Keep a list of mapName -> inType.
266     # This map contains the first inType seen for the specified mapName.
267     typeMap = dict()
268
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]
275
276     for mapsNode in mapsNodes:
277         for valueMapNode in mapsNode.getElementsByTagName("valueMap"):
278             mapName = valueMapNode.getAttribute("name")
279             dataNodes = templatesNode.getElementsByTagName("data")
280
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")
294
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")
305
306     return typeMap
307
308 def generateKeywordsClass(providerNode, outputFile):
309
310     # Find the keywords element.
311     for node in providerNode.getElementsByTagName("keywords"):
312         keywordsNode = node
313         break;
314
315     writeOutput(outputFile, "public class Keywords\n")
316     writeOutput(outputFile, "{\n")
317     increaseTabLevel()
318
319     for keywordNode in keywordsNode.getElementsByTagName("keyword"):
320         writeOutput(outputFile, "public const EventKeywords " + keywordNode.getAttribute("name") + " = (EventKeywords)" + keywordNode.getAttribute("mask") + ";\n")
321
322     decreaseTabLevel()
323     writeOutput(outputFile, "}\n\n")
324
325 def loadStringTable(manifest):
326
327     # Create the string table dictionary.
328     stringTable = dict()
329
330     # Get the string table element.
331     for node in manifest.getElementsByTagName("stringTable"):
332         stringTableNode = node
333         break
334
335     # Iterate through each string and save it.
336     for stringElem in stringTableNode.getElementsByTagName("string"):
337         stringTable[stringElem.getAttribute("id")] = stringElem.getAttribute("value")
338
339     return stringTable
340
341 def generateEventSources(manifestFullPath, intermediatesDirFullPath):
342
343     # Open the manifest for reading.
344     manifest = DOM.parse(manifestFullPath)
345
346     # Load the string table.
347     stringTable = loadStringTable(manifest)
348
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:
353                 providerNode = node
354                 break
355
356         if providerNode is None:
357             raise ValueError("Unable to find provider node.", providerName)
358
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:
362
363             # Write the license header.
364             writeOutput(outputFile, generatedCodeFileHeader)
365
366             # Write the class header.
367             header = """
368 using System;
369
370 namespace System.Diagnostics.Tracing
371 {
372 """
373             writeOutput(outputFile, header)
374             increaseTabLevel()
375
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")
380             increaseTabLevel()
381
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")
384
385             # Write the static Log property.
386             writeOutput(outputFile, "internal static " + className + " Log = new " + className + "();\n\n")
387
388             # Write the keywords class.
389             generateKeywordsClass(providerNode, outputFile)
390
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)
395
396             # Generate enums for value maps.
397             # generateValueMapEnums(providerNode, outputFile, stringTable, enumTypeMap)
398
399             # Generate enums for bit maps.
400             # generateBitMapEnums(providerNode, outputFile, stringTable, enumTypeMap)
401             #### Disable enums until they are needed ####
402
403             # Generate events.
404             generateEvents(providerNode, outputFile, stringTable)
405
406             # Write the class footer.
407             decreaseTabLevel()
408             writeOutput(outputFile, "}\n")
409             decreaseTabLevel()
410             writeOutput(outputFile, "}")
411
412 def main(argv):
413
414     # Parse command line arguments.
415     parser = argparse.ArgumentParser(
416         description="Generates C# EventSource classes that represent the runtime's native event providers.")
417
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)
424     if unknown:
425         print('Unknown argument(s): ', ', '.join(unknown))
426         return 1
427
428     manifestFullPath = args.man
429     intermediatesDirFullPath = args.intermediate
430
431     # Ensure the intermediates directory exists.
432     try:
433         os.makedirs(intermediatesDirFullPath)
434     except OSError:
435         if not os.path.isdir(intermediatesDirFullPath):
436             raise
437
438     # Generate event sources.
439     generateEventSources(manifestFullPath, intermediatesDirFullPath)
440     return 0
441
442 if __name__ == '__main__':
443     return_code = main(sys.argv[1:])
444     sys.exit(return_code)