diff options
Diffstat (limited to 'src/scripts/genEtwProvider.py')
-rw-r--r-- | src/scripts/genEtwProvider.py | 312 |
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 |