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.
6 ## This script generates the interface to ETW using MC.exe
15 import xml.dom.minidom as DOM
16 from genEventing import parseTemplateNodes
17 from utilities import open_for_update, update_directory, parseExclusionList
19 macroheader_filename = "etwmacros.h"
20 mcheader_filename = "ClrEtwAll.h"
21 clrxplat_filename = "clrxplatevents.h"
24 (r"EventEnabled", "EventXplatEnabled"),
25 (r"\bPVOID\b", "void*"),
27 counted_replacements = [
28 # There is a bug in the MC code generator that miscomputes the size of ETW events
29 # which have variable size arrays of variable size arrays. This occurred in our GCBulkType
30 # event. This workaround replaces the bad size computation with the correct one. See
31 # https://github.com/dotnet/coreclr/pull/25454 for more information"
32 (r"_Arg0 \* _Arg2_Len_", "_Arg2_Len_", 1)
36 // Licensed to the .NET Foundation under one or more agreements.
37 // The .NET Foundation licenses this file to you under the MIT license.
38 // See the LICENSE file in the project root for more information.
40 /******************************************************************
42 DO NOT MODIFY. AUTOGENERATED FILE.
43 This file is generated using the logic from <root>/src/scripts/genEtwProvider.py
45 ******************************************************************/
48 def genProviderInterface(manifest, intermediate):
49 provider_dirname = os.path.join(intermediate, etw_dirname + "_temp")
51 if not os.path.exists(provider_dirname):
52 os.makedirs(provider_dirname)
54 cmd = ['mc.exe', '-h', provider_dirname, '-r', provider_dirname, '-b', '-co', '-um', '-p', 'FireEtXplat', manifest]
55 subprocess.check_call(cmd)
58 with open(path.join(provider_dirname, mcheader_filename), 'r') as mcheader_file:
59 header_text = mcheader_file.read()
61 for pattern, replacement in replacements:
62 header_text = re.sub(pattern, replacement, header_text)
64 for pattern, replacement, expected_count in counted_replacements:
65 (header_text, actual_count) = re.subn(pattern, replacement, header_text)
66 if actual_count != expected_count:
67 raise Exception("The workaround for https://github.com/dotnet/coreclr/pull/25454 in src/scripts/genEtwProvider.py could not be applied. Has the generated code changed or perhaps the underlying issue has been fixed? ")
69 with open(path.join(provider_dirname, mcheader_filename), 'w') as mcheader_file:
70 mcheader_file.write(header_text)
72 def genXplatHeader(intermediate):
73 with open_for_update(path.join(intermediate, clrxplat_filename)) as header_file:
75 #ifndef _CLR_XPLAT_EVENTS_H_
76 #define _CLR_XPLAT_EVENTS_H_
81 #endif //_CLR_XPLAT_EVENTS_H_
82 """.format(etw_dirname, macroheader_filename, mcheader_filename))
84 def getStackWalkBit(eventProvider, taskName, eventSymbol, stackSet):
85 for entry in stackSet:
86 tokens = entry.split(':')
89 raise Exception("Error, possible error in the script which introduced the entry "+ entry)
91 eventCond = tokens[0] == eventProvider or tokens[0] == "*"
92 taskCond = tokens[1] == taskName or tokens[1] == "*"
93 symbolCond = tokens[2] == eventSymbol or tokens[2] == "*"
95 if eventCond and taskCond and symbolCond:
99 #Add the miscellaneous checks here
100 def checkConsistency(manifest, exclusion_filename):
101 tree = DOM.parse(manifest)
102 exclusionInfo = parseExclusionList(exclusion_filename)
103 for providerNode in tree.getElementsByTagName('provider'):
105 stackSupportSpecified = {}
106 eventNodes = providerNode.getElementsByTagName('event')
107 templateNodes = providerNode.getElementsByTagName('template')
108 eventProvider = providerNode.getAttribute('name')
109 allTemplates = parseTemplateNodes(templateNodes)
111 for eventNode in eventNodes:
112 taskName = eventNode.getAttribute('task')
113 eventSymbol = eventNode.getAttribute('symbol')
114 eventTemplate = eventNode.getAttribute('template')
115 eventValue = int(eventNode.getAttribute('value'))
116 clrInstanceBit = getStackWalkBit(eventProvider, taskName, eventSymbol, exclusionInfo.noclrinstance)
117 sLookupFieldName = "ClrInstanceID"
118 sLookupFieldType = "win:UInt16"
120 if clrInstanceBit and allTemplates.get(eventTemplate):
121 # check for the event template and look for a field named ClrInstanceId of type win:UInt16
122 fnParam = allTemplates[eventTemplate].getFnParam(sLookupFieldName)
124 if not(fnParam and fnParam.winType == sLookupFieldType):
125 raise Exception(exclusion_filename + ":No " + sLookupFieldName + " field of type " + sLookupFieldType + " for event symbol " + eventSymbol)
127 # If some versions of an event are on the nostack/stack lists,
128 # and some versions are not on either the nostack or stack list,
129 # then developer likely forgot to specify one of the versions
131 eventStackBitFromNoStackList = getStackWalkBit(eventProvider, taskName, eventSymbol, exclusionInfo.nostack)
132 eventStackBitFromExplicitStackList = getStackWalkBit(eventProvider, taskName, eventSymbol, exclusionInfo.explicitstack)
133 sStackSpecificityError = exclusion_filename + ": Error processing event :" + eventSymbol + "(ID" + str(eventValue) + "): This file must contain either ALL versions of this event or NO versions of this event. Currently some, but not all, versions of this event are present\n"
135 if not stackSupportSpecified.get(eventValue):
136 # Haven't checked this event before. Remember whether a preference is stated
137 if ( not eventStackBitFromNoStackList) or ( not eventStackBitFromExplicitStackList):
138 stackSupportSpecified[eventValue] = True
140 stackSupportSpecified[eventValue] = False
142 # We've checked this event before.
143 if stackSupportSpecified[eventValue]:
144 # When we last checked, a preference was previously specified, so it better be specified here
145 if eventStackBitFromNoStackList and eventStackBitFromExplicitStackList:
146 raise Exception(sStackSpecificityError)
148 # When we last checked, a preference was not previously specified, so it better not be specified here
149 if ( not eventStackBitFromNoStackList) or ( not eventStackBitFromExplicitStackList):
150 raise Exception(sStackSpecificityError)
152 def genEtwMacroHeader(manifest, exclusion_filename, intermediate):
153 provider_dirname = os.path.join(intermediate, etw_dirname + "_temp")
155 if not os.path.exists(provider_dirname):
156 os.makedirs(provider_dirname)
158 tree = DOM.parse(manifest)
159 numOfProviders = len(tree.getElementsByTagName('provider'))
160 nMaxEventBytesPerProvider = 64
162 exclusionInfo = parseExclusionList(exclusion_filename)
164 with open_for_update(os.path.join(provider_dirname, macroheader_filename)) as header_file:
165 header_file.write(stdprolog_cpp + "\n")
167 header_file.write("#define NO_OF_ETW_PROVIDERS " + str(numOfProviders) + "\n")
168 header_file.write("#define MAX_BYTES_PER_ETW_PROVIDER " + str(nMaxEventBytesPerProvider) + "\n")
169 header_file.write("EXTERN_C SELECTANY const BYTE etwStackSupportedEvents[NO_OF_ETW_PROVIDERS][MAX_BYTES_PER_ETW_PROVIDER] = \n{\n")
171 for providerNode in tree.getElementsByTagName('provider'):
172 stackSupportedEvents = [0]*nMaxEventBytesPerProvider
173 eventNodes = providerNode.getElementsByTagName('event')
174 eventProvider = providerNode.getAttribute('name')
176 for eventNode in eventNodes:
177 taskName = eventNode.getAttribute('task')
178 eventSymbol = eventNode.getAttribute('symbol')
179 eventTemplate = eventNode.getAttribute('template')
180 eventTemplate = eventNode.getAttribute('template')
181 eventValue = int(eventNode.getAttribute('value'))
182 eventIndex = eventValue // 8
183 eventBitPositionInIndex = eventValue % 8
185 eventStackBitFromNoStackList = int(getStackWalkBit(eventProvider, taskName, eventSymbol, exclusionInfo.nostack))
186 eventStackBitFromExplicitStackList = int(getStackWalkBit(eventProvider, taskName, eventSymbol, exclusionInfo.explicitstack))
188 # Shift those bits into position. For the explicit stack list, swap 0 and 1, so the eventValue* variables
189 # have 1 in the position iff we should issue a stack for the event.
190 eventValueUsingNoStackListByPosition = (eventStackBitFromNoStackList << eventBitPositionInIndex)
191 eventValueUsingExplicitStackListByPosition = ((1 - eventStackBitFromExplicitStackList) << eventBitPositionInIndex)
193 # Commit the values to the in-memory array that we'll dump into the header file
194 stackSupportedEvents[eventIndex] = stackSupportedEvents[eventIndex] | eventValueUsingNoStackListByPosition;
195 if eventStackBitFromExplicitStackList == 0:
196 stackSupportedEvents[eventIndex] = stackSupportedEvents[eventIndex] | eventValueUsingExplicitStackListByPosition
198 # print the bit array
201 for elem in stackSupportedEvents:
202 line.append(str(elem))
207 header_file.write(''.join(line) + "\n")
208 header_file.write("};\n")
210 def genFiles(manifest, intermediate, exclusion_filename):
211 if not os.path.exists(intermediate):
212 os.makedirs(intermediate)
214 genProviderInterface(manifest, intermediate)
215 genEtwMacroHeader(manifest, exclusion_filename, intermediate)
216 genXplatHeader(intermediate)
220 #parse the command line
221 parser = argparse.ArgumentParser(description="Generates the Code required to instrument ETW logging mechanism")
223 required = parser.add_argument_group('required arguments')
224 required.add_argument('--man', type=str, required=True,
225 help='full path to manifest containig the description of events')
226 required.add_argument('--exc', type=str, required=True,
227 help='full path to exclusion list')
228 required.add_argument('--intermediate', type=str, required=True,
229 help='full path to eventprovider intermediate directory')
230 args, unknown = parser.parse_known_args(argv)
232 print('Unknown argument(s): ', ', '.join(unknown))
236 exclusion_filename = args.exc
237 intermediate = args.intermediate
239 checkConsistency(manifest, exclusion_filename)
240 genFiles(manifest, intermediate, exclusion_filename)
242 # Update the final directory from temp
243 provider_temp_dirname = os.path.join(intermediate, etw_dirname + "_temp")
244 provider_dirname = os.path.join(intermediate, etw_dirname)
245 if not os.path.exists(provider_dirname):
246 os.makedirs(provider_dirname)
248 update_directory(provider_temp_dirname, provider_dirname)
249 shutil.rmtree(provider_temp_dirname)
251 if __name__ == '__main__':
252 return_code = main(sys.argv[1:])
253 sys.exit(return_code)