summaryrefslogtreecommitdiff
path: root/src/scripts/genEtwProvider.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/scripts/genEtwProvider.py')
-rw-r--r--src/scripts/genEtwProvider.py312
1 files changed, 312 insertions, 0 deletions
diff --git a/src/scripts/genEtwProvider.py b/src/scripts/genEtwProvider.py
new file mode 100644
index 0000000000..fc9d43800f
--- /dev/null
+++ b/src/scripts/genEtwProvider.py
@@ -0,0 +1,312 @@
+##
+## 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.
+##
+## This script generates the interface to ETW using MC.exe
+
+import os
+from os import path
+import shutil
+import re
+import sys
+import argparse
+import subprocess
+import xml.dom.minidom as DOM
+from genEventing import parseTemplateNodes
+from utilities import open_for_update
+
+macroheader_filename = "etwmacros.h"
+mcheader_filename = "ClrEtwAll.h"
+clrxplat_filename = "clrxplatevents.h"
+etw_dirname = "etw"
+replacements = [
+ (r"EventEnabled", "EventXplatEnabled"),
+ (r"\bPVOID\b", "void*")
+]
+
+stdprolog_cpp="""
+// 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 using the logic from <root>/src/scripts/genEtwProvider.py
+
+******************************************************************/
+"""
+stdprolog_cmake="""
+#
+#
+#******************************************************************
+
+#DO NOT MODIFY. AUTOGENERATED FILE.
+#This file is generated using the logic from <root>/src/scripts/genEtwProvider.py
+
+#******************************************************************
+"""
+
+def genProviderInterface(manifest, intermediate):
+ provider_dirname = os.path.join(intermediate, etw_dirname)
+
+ if not os.path.exists(provider_dirname):
+ os.makedirs(provider_dirname)
+
+ cmd = ['mc.exe', '-h', provider_dirname, '-r', provider_dirname, '-b', '-co', '-um', '-p', 'FireEtXplat', manifest]
+ subprocess.check_call(cmd)
+
+ header_text = None
+ with open(path.join(provider_dirname, mcheader_filename), 'r') as mcheader_file:
+ header_text = mcheader_file.read()
+
+ for pattern, replacement in replacements:
+ header_text = re.sub(pattern, replacement, header_text)
+
+ with open_for_update(path.join(provider_dirname, mcheader_filename)) as mcheader_file:
+ mcheader_file.write(header_text)
+
+def genCmake(intermediate):
+ # Top level Cmake
+
+ with open_for_update(os.path.join(intermediate, "CMakeLists.txt")) as cmake_file:
+ cmake_file.write(stdprolog_cmake)
+ cmake_file.write("""
+project(eventprovider)
+
+cmake_minimum_required(VERSION 2.8.12.2)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+include_directories({0})
+
+add_library_clr(eventprovider
+ STATIC
+ "{0}/{1}"
+ "{0}/{2}"
+)
+
+#set_target_properties(eventprovider PROPERTIES LINKER_LANGUAGE Hxx)
+""".format(etw_dirname, macroheader_filename, "ClrEtwAll.cpp"))
+
+def genXplatHeader(intermediate):
+ with open_for_update(path.join(intermediate, clrxplat_filename)) as header_file:
+ header_file.write("""
+#ifndef _CLR_XPLAT_EVENTS_H_
+#define _CLR_XPLAT_EVENTS_H_
+
+#include "{0}/{1}"
+#include "{0}/{2}"
+
+#endif //_CLR_XPLAT_EVENTS_H_
+""".format(etw_dirname, macroheader_filename, mcheader_filename))
+
+
+class EventExclusions:
+ def __init__(self):
+ self.nostack = set()
+ self.explicitstack = set()
+ self.noclrinstance = set()
+
+def parseExclusionList(exclusion_filename):
+ with open(exclusion_filename,'r') as ExclusionFile:
+ exclusionInfo = EventExclusions()
+
+ for line in ExclusionFile:
+ line = line.strip()
+
+ #remove comments
+ if not line or line.startswith('#'):
+ continue
+
+ tokens = line.split(':')
+ #entries starting with nomac are ignored
+ if "nomac" in tokens:
+ continue
+
+ if len(tokens) > 5:
+ raise Exception("Invalid Entry " + line + "in "+ exclusion_filename)
+
+ eventProvider = tokens[2]
+ eventTask = tokens[1]
+ eventSymbol = tokens[4]
+
+ if eventProvider == '':
+ eventProvider = "*"
+ if eventTask == '':
+ eventTask = "*"
+ if eventSymbol == '':
+ eventSymbol = "*"
+ entry = eventProvider + ":" + eventTask + ":" + eventSymbol
+
+ if tokens[0].lower() == "nostack":
+ exclusionInfo.nostack.add(entry)
+ if tokens[0].lower() == "stack":
+ exclusionInfo.explicitstack.add(entry)
+ if tokens[0].lower() == "noclrinstanceid":
+ exclusionInfo.noclrinstance.add(entry)
+
+ return exclusionInfo
+
+def getStackWalkBit(eventProvider, taskName, eventSymbol, stackSet):
+ for entry in stackSet:
+ tokens = entry.split(':')
+
+ if len(tokens) != 3:
+ raise Exception("Error, possible error in the script which introduced the enrty "+ entry)
+
+ eventCond = tokens[0] == eventProvider or tokens[0] == "*"
+ taskCond = tokens[1] == taskName or tokens[1] == "*"
+ symbolCond = tokens[2] == eventSymbol or tokens[2] == "*"
+
+ if eventCond and taskCond and symbolCond:
+ return False
+ return True
+
+#Add the miscelaneous checks here
+def checkConsistency(manifest, exclusion_filename):
+ tree = DOM.parse(manifest)
+ exclusionInfo = parseExclusionList(exclusion_filename)
+ for providerNode in tree.getElementsByTagName('provider'):
+
+ stackSupportSpecified = {}
+ eventNodes = providerNode.getElementsByTagName('event')
+ templateNodes = providerNode.getElementsByTagName('template')
+ eventProvider = providerNode.getAttribute('name')
+ allTemplates = parseTemplateNodes(templateNodes)
+
+ for eventNode in eventNodes:
+ taskName = eventNode.getAttribute('task')
+ eventSymbol = eventNode.getAttribute('symbol')
+ eventTemplate = eventNode.getAttribute('template')
+ eventValue = int(eventNode.getAttribute('value'))
+ clrInstanceBit = getStackWalkBit(eventProvider, taskName, eventSymbol, exclusionInfo.noclrinstance)
+ sLookupFieldName = "ClrInstanceID"
+ sLookupFieldType = "win:UInt16"
+
+ if clrInstanceBit and allTemplates.get(eventTemplate):
+ # check for the event template and look for a field named ClrInstanceId of type win:UInt16
+ fnParam = allTemplates[eventTemplate].getFnParam(sLookupFieldName)
+
+ if not(fnParam and fnParam.winType == sLookupFieldType):
+ raise Exception(exclusion_filename + ":No " + sLookupFieldName + " field of type " + sLookupFieldType + " for event symbol " + eventSymbol)
+
+ # If some versions of an event are on the nostack/stack lists,
+ # and some versions are not on either the nostack or stack list,
+ # then developer likely forgot to specify one of the versions
+
+ eventStackBitFromNoStackList = getStackWalkBit(eventProvider, taskName, eventSymbol, exclusionInfo.nostack)
+ eventStackBitFromExplicitStackList = getStackWalkBit(eventProvider, taskName, eventSymbol, exclusionInfo.explicitstack)
+ 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"
+
+ if not stackSupportSpecified.get(eventValue):
+ # Haven't checked this event before. Remember whether a preference is stated
+ if ( not eventStackBitFromNoStackList) or ( not eventStackBitFromExplicitStackList):
+ stackSupportSpecified[eventValue] = True
+ else:
+ stackSupportSpecified[eventValue] = False
+ else:
+ # We've checked this event before.
+ if stackSupportSpecified[eventValue]:
+ # When we last checked, a preference was previously specified, so it better be specified here
+ if eventStackBitFromNoStackList and eventStackBitFromExplicitStackList:
+ raise Exception(sStackSpecificityError)
+ else:
+ # When we last checked, a preference was not previously specified, so it better not be specified here
+ if ( not eventStackBitFromNoStackList) or ( not eventStackBitFromExplicitStackList):
+ raise Exception(sStackSpecificityError)
+
+def genEtwMacroHeader(manifest, exclusion_filename, intermediate):
+ provider_dirname = os.path.join(intermediate, etw_dirname)
+
+ if not os.path.exists(provider_dirname):
+ os.makedirs(provider_dirname)
+
+ tree = DOM.parse(manifest)
+ numOfProviders = len(tree.getElementsByTagName('provider'))
+ nMaxEventBytesPerProvider = 64
+
+ exclusionInfo = parseExclusionList(exclusion_filename)
+
+ with open_for_update(os.path.join(provider_dirname, macroheader_filename)) as header_file:
+ header_file.write(stdprolog_cpp + "\n")
+
+ header_file.write("#define NO_OF_ETW_PROVIDERS " + str(numOfProviders) + "\n")
+ header_file.write("#define MAX_BYTES_PER_ETW_PROVIDER " + str(nMaxEventBytesPerProvider) + "\n")
+ header_file.write("EXTERN_C __declspec(selectany) const BYTE etwStackSupportedEvents[NO_OF_ETW_PROVIDERS][MAX_BYTES_PER_ETW_PROVIDER] = \n{\n")
+
+ for providerNode in tree.getElementsByTagName('provider'):
+ stackSupportedEvents = [0]*nMaxEventBytesPerProvider
+ eventNodes = providerNode.getElementsByTagName('event')
+ eventProvider = providerNode.getAttribute('name')
+
+ for eventNode in eventNodes:
+ taskName = eventNode.getAttribute('task')
+ eventSymbol = eventNode.getAttribute('symbol')
+ eventTemplate = eventNode.getAttribute('template')
+ eventTemplate = eventNode.getAttribute('template')
+ eventValue = int(eventNode.getAttribute('value'))
+ eventIndex = eventValue // 8
+ eventBitPositionInIndex = eventValue % 8
+
+ eventStackBitFromNoStackList = int(getStackWalkBit(eventProvider, taskName, eventSymbol, exclusionInfo.nostack))
+ eventStackBitFromExplicitStackList = int(getStackWalkBit(eventProvider, taskName, eventSymbol, exclusionInfo.explicitstack))
+
+ # Shift those bits into position. For the explicit stack list, swap 0 and 1, so the eventValue* variables
+ # have 1 in the position iff we should issue a stack for the event.
+ eventValueUsingNoStackListByPosition = (eventStackBitFromNoStackList << eventBitPositionInIndex)
+ eventValueUsingExplicitStackListByPosition = ((1 - eventStackBitFromExplicitStackList) << eventBitPositionInIndex)
+
+ # Commit the values to the in-memory array that we'll dump into the header file
+ stackSupportedEvents[eventIndex] = stackSupportedEvents[eventIndex] | eventValueUsingNoStackListByPosition;
+ if eventStackBitFromExplicitStackList == 0:
+ stackSupportedEvents[eventIndex] = stackSupportedEvents[eventIndex] | eventValueUsingExplicitStackListByPosition
+
+ # print the bit array
+ line = []
+ line.append("\t{")
+ for elem in stackSupportedEvents:
+ line.append(str(elem))
+ line.append(", ")
+
+ del line[-1]
+ line.append("},")
+ header_file.write(''.join(line) + "\n")
+ header_file.write("};\n")
+
+def genFiles(manifest, intermediate, exclusion_filename):
+ if not os.path.exists(intermediate):
+ os.makedirs(intermediate)
+
+ genProviderInterface(manifest, intermediate)
+ genEtwMacroHeader(manifest, exclusion_filename, intermediate)
+ genXplatHeader(intermediate)
+
+
+def main(argv):
+ #parse the command line
+ parser = argparse.ArgumentParser(description="Generates the Code required to instrument ETW logging mechanism")
+
+ 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('--exc', type=str, required=True,
+ help='full path to exclusion list')
+ 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
+
+ manifest = args.man
+ exclusion_filename = args.exc
+ intermediate = args.intermediate
+
+ checkConsistency(manifest, exclusion_filename)
+ genFiles(manifest, intermediate, exclusion_filename)
+
+if __name__ == '__main__':
+ return_code = main(sys.argv[1:])
+ sys.exit(return_code) \ No newline at end of file