diff options
author | Victor "Nate" Graf <nategraf1@gmail.com> | 2018-01-02 09:47:20 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-01-02 09:47:20 -0800 |
commit | c1bbdae7964b19b7063074d36e6af960f0cdc3a0 (patch) | |
tree | e554fc74fbb22c6ce9dd488fb02ba4f08ee89e89 /src | |
parent | 35bba0c5b29e9dfd2744b09e577f6111ef7de9d7 (diff) | |
download | coreclr-c1bbdae7964b19b7063074d36e6af960f0cdc3a0.tar.gz coreclr-c1bbdae7964b19b7063074d36e6af960f0cdc3a0.tar.bz2 coreclr-c1bbdae7964b19b7063074d36e6af960f0cdc3a0.zip |
Retry: Enable EventPipe across Unix and Windows (#15611)
* Revert "Revert "Enable EventPipe across Unix and Windows (#14772)" (#15609)"
This reverts commit 302005ca8ae14eade37ddf4ac6e900617c1c166a.
* Fix ARM build break
* Use more explicit references to resolve build failures
* Fix compat with python3
* Disable FeaturePerfTracing on Windows as it is not ready
* Disable test for incomplete functionality
* Fix test diabled patterns
* Add license header
* Use keyword types for managed code
* Add message prefix
* More precisly condition generation of eventing sources
* Remove erroneously added changes
Diffstat (limited to 'src')
31 files changed, 1587 insertions, 912 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4c999b184d..0412226dfc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -10,70 +10,10 @@ include_directories("classlibnative/cryptography") include_directories("classlibnative/inc") include_directories("${GENERATED_INCLUDE_DIR}") -# The Following Logic is used to wire up Build dependencies for Generated files in Event Logging -# ClrEtwAll.man - Event Schema -# ClrEtwAllMeta.lst - MetaData list [provided to ensure Windows Desktop is not broken] -# genXplatEventing.py - has the core logic for parsing Event Schema -# genWinEtw.py - Uses genXplatEventing to generate Windows Specific ETW Files -# clretwallmain.h and etmdummy.h - Provides the Event Logging Functionality to the VM -# clrxplatevents.h - Used by clretwallmain.h on Non Windows platform -# ClrEtwAll.h - Used by clretwallmain.h on Windows -# ClrEtwAll.rc - Used by src/dlls/clretwrc/clretrw.rc on Windows - -set (ScriptGeneratedEventFiles - ${GENERATED_INCLUDE_DIR}/clretwallmain.h - ${GENERATED_INCLUDE_DIR}/etmdummy.h -) -set (GeneratedEventFiles) +if(WIN32 AND FEATURE_EVENT_TRACE) + include_directories("${GENERATED_INCLUDE_DIR}/etw") +endif(WIN32 AND FEATURE_EVENT_TRACE) -if(WIN32) - set (GenEventFilesScript "${CLR_DIR}/src/scripts/genWinEtw.py") - set (GenEventArgs --eventheader "${GENERATED_INCLUDE_DIR}/ClrEtwAll.h" --macroheader "${GENERATED_INCLUDE_DIR}/clretwallmain.h") - - list (APPEND ScriptGeneratedEventFiles - ${GENERATED_INCLUDE_DIR}/ClrEtwAll.h - ) - - list (APPEND GeneratedEventFiles - ${GENERATED_INCLUDE_DIR}/ClrEtwAll.rc - ) - - add_custom_command( - COMMENT "Generating ETW resource Files" - COMMAND ${MC} -h ${GENERATED_INCLUDE_DIR} -r ${GENERATED_INCLUDE_DIR} -b -co -um -p FireEtw "${VM_DIR}/ClrEtwAll.man" - OUTPUT ${GENERATED_INCLUDE_DIR}/ClrEtwAll.h - DEPENDS "${VM_DIR}/ClrEtwAll.man" - ) -else() - set (GenEventFilesScript "${CLR_DIR}/src/scripts/genXplatEventing.py") - set (GenEventArgs --inc "${GENERATED_INCLUDE_DIR}") - - list (APPEND ScriptGeneratedEventFiles - ${GENERATED_INCLUDE_DIR}/clrxplatevents.h - ) -endif(WIN32) - -if(CLR_CMAKE_WARNINGS_ARE_ERRORS) - set(PYTHON_WARNING_FLAGS -Wall -Werror) -else() - set(PYTHON_WARNING_FLAGS -Wall) -endif(CLR_CMAKE_WARNINGS_ARE_ERRORS) - -add_custom_command( - COMMENT "Generating Eventing Files" - COMMAND ${PYTHON} -B ${PYTHON_WARNING_FLAGS} ${GenEventFilesScript} ${GenEventArgs} --man "${VM_DIR}/ClrEtwAll.man" --exc "${VM_DIR}/ClrEtwAllMeta.lst" --dummy "${GENERATED_INCLUDE_DIR}/etmdummy.h" - OUTPUT ${ScriptGeneratedEventFiles} - DEPENDS ${GenEventFilesScript} "${VM_DIR}/ClrEtwAll.man" "${VM_DIR}/ClrEtwAllMeta.lst" "${CLR_DIR}/src/scripts/genXplatEventing.py" -) - -list (APPEND GeneratedEventFiles - ${ScriptGeneratedEventFiles} -) - -add_custom_target( - GeneratedEventingFiles - DEPENDS ${GeneratedEventFiles} -) if(CLR_CMAKE_PLATFORM_UNIX) if(CLR_CMAKE_PLATFORM_LINUX) diff --git a/src/dlls/mscoree/coreclr/CMakeLists.txt b/src/dlls/mscoree/coreclr/CMakeLists.txt index 8796cc16a5..8043f7733a 100644 --- a/src/dlls/mscoree/coreclr/CMakeLists.txt +++ b/src/dlls/mscoree/coreclr/CMakeLists.txt @@ -4,8 +4,8 @@ if (WIN32) list(APPEND CLR_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/coreclr.def) set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /ENTRY:CoreDllMain") - - # Incremental linking results in the linker inserting extra padding and routing function calls via thunks that can break the + + # Incremental linking results in the linker inserting extra padding and routing function calls via thunks that can break the # invariants (e.g. size of region between Jit_PatchedCodeLast-Jit_PatchCodeStart needs to fit in a page). set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /INCREMENTAL:NO") @@ -14,7 +14,7 @@ if (WIN32) set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DELAYLOAD:api-ms-win-core-winrt-l1-1-0.dll") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DELAYLOAD:api-ms-win-core-winrt-roparameterizediid-l1-1-0.dll") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DELAYLOAD:api-ms-win-ro-typeresolution-l1-1-0.dll") - + # No library groups for Win32 set(START_LIBRARY_GROUP) set(END_LIBRARY_GROUP) @@ -30,14 +30,14 @@ else() # of the utilcode will be used instead of the standard library delete operator. set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Xlinker -Bsymbolic -Xlinker -Bsymbolic-functions") - # The following linked options can be inserted into the linker libraries list to + # The following linked options can be inserted into the linker libraries list to # ensure proper resolving of circular references between a subset of the libraries. set(START_LIBRARY_GROUP -Wl,--start-group) set(END_LIBRARY_GROUP -Wl,--end-group) # These options are used to force every object to be included even if it's unused. set(START_WHOLE_ARCHIVE -Wl,--whole-archive) - set(END_WHOLE_ARCHIVE -Wl,--no-whole-archive) + set(END_WHOLE_ARCHIVE -Wl,--no-whole-archive) set(EXPORTS_LINKER_OPTION -Wl,--version-script=${EXPORTS_FILE}) endif(CMAKE_SYSTEM_NAME STREQUAL Linux OR CMAKE_SYSTEM_NAME STREQUAL FreeBSD OR CMAKE_SYSTEM_NAME STREQUAL NetBSD) @@ -74,7 +74,7 @@ if(FEATURE_MERGE_JIT_AND_ENGINE) endif(FEATURE_MERGE_JIT_AND_ENGINE) # IMPORTANT! Please do not rearrange the order of the libraries. The linker on Linux is -# order dependent and changing the order can result in undefined symbols in the shared +# order dependent and changing the order can result in undefined symbols in the shared # library. set(CORECLR_LIBRARIES utilcode @@ -119,28 +119,34 @@ if(WIN32) ) else() list(APPEND CORECLR_LIBRARIES - ${START_WHOLE_ARCHIVE} # force all PAL objects to be included so all exports are available + ${START_WHOLE_ARCHIVE} # force all PAL objects to be included so all exports are available coreclrpal - tracepointprovider - ${END_WHOLE_ARCHIVE} + ${END_WHOLE_ARCHIVE} mscorrc_debug palrt ) endif(WIN32) -if(CLR_CMAKE_PLATFORM_UNIX AND FEATURE_EVENT_TRACE) - list(APPEND CORECLR_LIBRARIES - eventprovider - ) -endif(CLR_CMAKE_PLATFORM_UNIX AND FEATURE_EVENT_TRACE) - -if(CLR_CMAKE_PLATFORM_LINUX) +if(FEATURE_PERFTRACING) list(APPEND CORECLR_LIBRARIES eventpipe - ) -endif(CLR_CMAKE_PLATFORM_LINUX) - -target_link_libraries(coreclr ${CORECLR_LIBRARIES}) + ) +endif(FEATURE_PERFTRACING) + +if(FEATURE_EVENT_TRACE) + if(CLR_CMAKE_PLATFORM_UNIX) + list(APPEND CORECLR_LIBRARIES + eventprovider # On Windows this library contains only macros + ) + endif(CLR_CMAKE_PLATFORM_UNIX) + if(CLR_CMAKE_PLATFORM_LINUX) + list(APPEND CORECLR_LIBRARIES + tracepointprovider + ) + endif(CLR_CMAKE_PLATFORM_LINUX) +endif(FEATURE_EVENT_TRACE) + +target_link_libraries(coreclr ${CORECLR_LIBRARIES}) if(WIN32) # Add dac table & debug resource to coreclr diff --git a/src/mscorlib/System.Private.CoreLib.csproj b/src/mscorlib/System.Private.CoreLib.csproj index 0729772035..7dbe9109c7 100644 --- a/src/mscorlib/System.Private.CoreLib.csproj +++ b/src/mscorlib/System.Private.CoreLib.csproj @@ -622,7 +622,7 @@ <Target Name="CDefineChecker" BeforeTargets="Build" Condition="'$(CheckCDefines)'=='true'"> <!-- Compiler Definition Verification --> <PropertyGroup> - <CMakeDefinitionSaveFile>$(IntermediateOutputPath)\cmake.definitions</CMakeDefinitionSaveFile> + <CMakeDefinitionSaveFile>$(IntermediateOutputPath)..\cmake.definitions</CMakeDefinitionSaveFile> </PropertyGroup> <Exec Command="python $(MSBuildThisFileDirectory)..\scripts\check-definitions.py "$(CMakeDefinitionSaveFile)" "$(DefineConstants)" "$(IgnoreDefineConstants)" " /> </Target> diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/EventPipe.cs b/src/mscorlib/src/System/Diagnostics/Eventing/EventPipe.cs index 539c60b55f..5363b2aece 100644 --- a/src/mscorlib/src/System/Diagnostics/Eventing/EventPipe.cs +++ b/src/mscorlib/src/System/Diagnostics/Eventing/EventPipe.cs @@ -14,12 +14,12 @@ namespace System.Diagnostics.Tracing { [MarshalAs(UnmanagedType.LPWStr)] private string m_providerName; - private UInt64 m_keywords; + private ulong m_keywords; private uint m_loggingLevel; internal EventPipeProviderConfiguration( string providerName, - UInt64 keywords, + ulong keywords, uint loggingLevel) { if(string.IsNullOrEmpty(providerName)) @@ -40,7 +40,7 @@ namespace System.Diagnostics.Tracing get { return m_providerName; } } - internal UInt64 Keywords + internal ulong Keywords { get { return m_keywords; } } @@ -96,7 +96,7 @@ namespace System.Diagnostics.Tracing get { return m_minTimeBetweenSamples.Ticks * 100; } } - internal void EnableProvider(string providerName, UInt64 keywords, uint loggingLevel) + internal void EnableProvider(string providerName, ulong keywords, uint loggingLevel) { m_providers.Add(new EventPipeProviderConfiguration( providerName, @@ -124,6 +124,11 @@ namespace System.Diagnostics.Tracing throw new ArgumentNullException(nameof(configuration)); } + if(configuration.Providers == null) + { + throw new ArgumentNullException(nameof(configuration.Providers)); + } + EventPipeProviderConfiguration[] providers = configuration.Providers; EventPipeInternal.Enable( @@ -158,7 +163,7 @@ namespace System.Diagnostics.Tracing internal static extern IntPtr CreateProvider(string providerName, UnsafeNativeMethods.ManifestEtw.EtwEnableCallback callbackFunc); [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] - internal static extern unsafe IntPtr DefineEvent(IntPtr provHandle, uint eventID, Int64 keywords, uint eventVersion, uint level, void *pMetadata, uint metadataLength); + internal static extern unsafe IntPtr DefineEvent(IntPtr provHandle, uint eventID, long keywords, uint eventVersion, uint level, void *pMetadata, uint metadataLength); [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] internal static extern void DeleteProvider(IntPtr provHandle); diff --git a/src/pal/src/CMakeLists.txt b/src/pal/src/CMakeLists.txt index 2ade61b3d6..feb434cd3e 100644 --- a/src/pal/src/CMakeLists.txt +++ b/src/pal/src/CMakeLists.txt @@ -239,10 +239,14 @@ add_library(coreclrpal ${PLATFORM_SOURCES} ) -add_library(tracepointprovider - STATIC - misc/tracepointprovider.cpp -) +# This builds in functionality to load LTTng tracepoints at runtime +# Needed when using LTTng to support event tracing on Linux +if(CLR_CMAKE_PLATFORM_LINUX) + add_library(tracepointprovider + STATIC + misc/tracepointprovider.cpp + ) +endif(CLR_CMAKE_PLATFORM_LINUX) if(CMAKE_SYSTEM_NAME STREQUAL Darwin) find_library(COREFOUNDATION CoreFoundation) @@ -376,7 +380,7 @@ endif(CMAKE_SYSTEM_NAME STREQUAL NetBSD) add_subdirectory(examples) if(FEATURE_EVENT_TRACE) - add_subdirectory($ENV{__IntermediatesDir}/Generated/eventprovider ${CMAKE_CURRENT_BINARY_DIR}/eventprovider) + add_subdirectory(${GENERATED_EVENTING_DIR}/eventprovider ${CMAKE_CURRENT_BINARY_DIR}/eventprovider) endif(FEATURE_EVENT_TRACE) # Install the static PAL library for VS diff --git a/src/pal/tests/palsuite/CMakeLists.txt b/src/pal/tests/palsuite/CMakeLists.txt index 0ea8969bab..6f36025d1a 100644 --- a/src/pal/tests/palsuite/CMakeLists.txt +++ b/src/pal/tests/palsuite/CMakeLists.txt @@ -31,4 +31,7 @@ add_subdirectory(miscellaneous) add_subdirectory(pal_specific) add_subdirectory(samples) add_subdirectory(threading) -add_subdirectory($ENV{__IntermediatesDir}/Generated/eventprovider/tests ${CMAKE_CURRENT_BINARY_DIR}/eventprovider ) + +if(FEATURE_EVENT_TRACE) + add_subdirectory(${GENERATED_EVENTING_DIR}/eventprovider/tests ${CMAKE_CURRENT_BINARY_DIR}/eventprovider) +endif(FEATURE_EVENT_TRACE) diff --git a/src/scripts/Utilities.py b/src/scripts/Utilities.py deleted file mode 100644 index c1ceec8e9f..0000000000 --- a/src/scripts/Utilities.py +++ /dev/null @@ -1,49 +0,0 @@ -from filecmp import dircmp -import shutil -import os - -def walk_recursively_and_update(dcmp): - #for different Files Copy from right to left - for name in dcmp.diff_files: - srcpath = dcmp.right + "/" + name - destpath = dcmp.left + "/" + name - print("Updating %s" % (destpath)) - if os.path.isfile(srcpath): - shutil.copyfile(srcpath, destpath) - else : - raise Exception("path: " + srcpath + "is neither a file or folder") - - #copy right only files - for name in dcmp.right_only: - srcpath = dcmp.right + "/" + name - destpath = dcmp.left + "/" + name - print("Updating %s" % (destpath)) - if os.path.isfile(srcpath): - shutil.copyfile(srcpath, destpath) - elif os.path.isdir(srcpath): - shutil.copytree(srcpath, destpath) - else : - raise Exception("path: " + srcpath + "is neither a file or folder") - - #delete left only files - for name in dcmp.left_only: - path = dcmp.left + "/" + name - print("Deleting %s" % (path)) - if os.path.isfile(path): - os.remove(path) - elif os.path.isdir(path): - shutil.rmtree(path) - else : - raise Exception("path: " + path + "is neither a file or folder") - - #call recursively - for sub_dcmp in dcmp.subdirs.values(): - walk_recursively_and_update(sub_dcmp) - -def UpdateDirectory(destpath,srcpath): - - print("Updating %s with %s" % (destpath,srcpath)) - if not os.path.exists(destpath): - os.makedirs(destpath) - dcmp = dircmp(destpath,srcpath) - walk_recursively_and_update(dcmp) diff --git a/src/scripts/check-definitions.py b/src/scripts/check-definitions.py index 59b309a3e6..4f1026d4ef 100644 --- a/src/scripts/check-definitions.py +++ b/src/scripts/check-definitions.py @@ -33,18 +33,17 @@ debug = 0 # For the native part, return the sorted definition array. def loadDefinitionFile(filename): result = [] + try: - f = open(filename, 'r') - except: + with open(filename, 'r') as f: + for line in f: + line = line.strip() + if line: + result.append(line) + except IOError: + # If cmake was not used, this script won't work, and that's ok sys.exit(0) - # if cmake was not used (because of skipnative or systems that do not use cmake), this script won't work. - - for line in f: - theLine = line.rstrip("\r\n").strip() - if (len(theLine) > 0): - result.append(theLine) - f.close() result = sorted(result) return result @@ -108,9 +107,10 @@ def getDiff(arrNative, arrManaged): def printPotentiallyCritical(arrDefinitions, referencedFilename, arrIgnore): - f = open(referencedFilename, 'r') - content = f.read() - f.close() + content = None + with open(referencedFilename, 'r') as f: + content = f.read() + for keyword in arrDefinitions: skip = 0 diff --git a/src/scripts/genDummyProvider.py b/src/scripts/genDummyProvider.py new file mode 100644 index 0000000000..0704554263 --- /dev/null +++ b/src/scripts/genDummyProvider.py @@ -0,0 +1,211 @@ +## +## 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 exists to create a dummy implementaion of the eventprovider +## interface from a manifest file +## +## The intended use if for platforms which support event pipe +## but do not have a an eventing platform to recieve report events + +import os +from genEventing import * +from utilities import open_for_update + +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/genDummyProvider.py + +******************************************************************/ +""" +stdprolog_cmake=""" +# +# +#****************************************************************** + +#DO NOT MODIFY. AUTOGENERATED FILE. +#This file is generated using the logic from <root>/src/scripts/genDummyProvider.py + +#****************************************************************** +""" + +def trimProvName(name): + name = name.replace("Windows-",'') + name = name.replace("Microsoft-",'') + name = name.replace('-','_') + return name + +def escapeProvFilename(name): + name = name.replace('_','') + name = name.lower() + return name + +def generateDummyProvider(providerName, eventNodes, allTemplates, extern): + impl = [] + for eventNode in eventNodes: + eventName = eventNode.getAttribute('symbol') + templateName = eventNode.getAttribute('template') + + #generate EventXplatEnabled + if extern: impl.append('extern "C" ') + impl.append("BOOL EventXplatEnabled%s(){ return FALSE; }\n\n" % (eventName,)) + + #generate FireEtw functions + fnptype = [] + linefnptype = [] + if extern: fnptype.append('extern "C" ') + fnptype.append("ULONG FireEtXplat") + fnptype.append(eventName) + fnptype.append("(\n") + + + if templateName: + template = allTemplates[templateName] + else: + template = None + + if template: + fnSig = template.signature + for paramName in fnSig.paramlist: + fnparam = fnSig.getParam(paramName) + wintypeName = fnparam.winType + typewName = palDataTypeMapping[wintypeName] + winCount = fnparam.count + countw = palDataTypeMapping[winCount] + + if paramName in template.structs: + linefnptype.append("%sint %s_ElementSize,\n" % (lindent, paramName)) + + linefnptype.append(lindent) + linefnptype.append(typewName) + if countw != " ": + linefnptype.append(countw) + + linefnptype.append(" ") + linefnptype.append(fnparam.name) + linefnptype.append(",\n") + + if len(linefnptype) > 0 : + del linefnptype[-1] + + fnptype.extend(linefnptype) + fnptype.append(")\n{\n") + impl.extend(fnptype) + + #start of fn body + impl.append(" return ERROR_SUCCESS;\n") + impl.append("}\n\n") + + return ''.join(impl) + +def generateDummyFiles(etwmanifest, out_dirname, extern): + tree = DOM.parse(etwmanifest) + + #keep these relative + dummy_directory = "dummy" + dummyevntprovPre = os.path.join(dummy_directory, "eventprov") + + if not os.path.exists(out_dirname): + os.makedirs(out_dirname) + + if not os.path.exists(os.path.join(out_dirname, dummy_directory)): + os.makedirs(os.path.join(out_dirname, dummy_directory)) + + # Cmake + with open_for_update(os.path.join(out_dirname, "CMakeLists.txt")) as cmake: + cmake.write(stdprolog_cmake + "\n") + cmake.write("\ncmake_minimum_required(VERSION 2.8.12.2)\n") + if extern: cmake.write("\nproject(eventprovider)\n") + cmake.write(""" + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +if(FEATURE_PAL) + add_definitions(-DPAL_STDCPP_COMPAT=1) + include_directories(${COREPAL_SOURCE_DIR}/inc/rt) +endif(FEATURE_PAL) +include_directories(dummy) + +""") + if extern: cmake.write("add_library") + else: cmake.write("add_library_clr") + cmake.write("""(eventprovider + STATIC\n""") + + for providerNode in tree.getElementsByTagName('provider'): + providerName = trimProvName(providerNode.getAttribute('name')) + providerName_File = escapeProvFilename(providerName) + + cmake.write(' "%s%s.cpp"\n' % (dummyevntprovPre, providerName_File)) + + cmake.write(")") + if extern: cmake.write(""" + +# Install the static eventprovider library +install(TARGETS eventprovider DESTINATION lib) +""") + + # Dummy Instrumentation + for providerNode in tree.getElementsByTagName('provider'): + providerName = trimProvName(providerNode.getAttribute('name')) + providerName_File = escapeProvFilename(providerName) + + dummyevntprov = os.path.join(out_dirname, dummyevntprovPre + providerName_File + ".cpp") + + with open_for_update(dummyevntprov) as impl: + impl.write(stdprolog_cpp + "\n") + + impl.write(""" +#ifdef PLATFORM_UNIX +#include "pal_mstypes.h" +#include "pal_error.h" +#include "pal.h" +#define PAL_free free +#define PAL_realloc realloc +#include "pal/stackstring.hpp" +#endif + + +""") + + templateNodes = providerNode.getElementsByTagName('template') + eventNodes = providerNode.getElementsByTagName('event') + + allTemplates = parseTemplateNodes(templateNodes) + + #create the implementation of eventing functions : dummyeventprov*.cp + impl.write(generateDummyProvider(providerName, eventNodes, allTemplates, extern) + "\n") + +def main(argv): + + #parse the command line + parser = argparse.ArgumentParser(description="Generates the Code required to instrument LTTtng 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('--intermediate', type=str, required=True, + help='full path to eventprovider intermediate directory') + required.add_argument('--nonextern', action='store_true', + help='if specified, will generate files to be compiled into the CLR rather than externaly' ) + args, unknown = parser.parse_known_args(argv) + if unknown: + print('Unknown argument(s): ', ', '.join(unknown)) + return 1 + + sClrEtwAllMan = args.man + intermediate = args.intermediate + extern = not args.nonextern + + generateDummyFiles(sClrEtwAllMan, intermediate, extern) + +if __name__ == '__main__': + return_code = main(sys.argv[1:]) + sys.exit(return_code)
\ No newline at end of file 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 diff --git a/src/scripts/genEventPipe.py b/src/scripts/genEventPipe.py index 8a970794c0..96755ea459 100644 --- a/src/scripts/genEventPipe.py +++ b/src/scripts/genEventPipe.py @@ -1,10 +1,11 @@ from __future__ import print_function -from genXplatEventing import * -from genXplatLttng import * +from genEventing import * +from genLttngProvider import * import os import xml.dom.minidom as DOM +from utilities import open_for_update -stdprolog = """// Licensed to the .NET Foundation under one or more agreements. +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. @@ -14,6 +15,7 @@ DO NOT MODIFY. AUTOGENERATED FILE. This file is generated using the logic from <root>/src/scripts/genEventPipe.py ******************************************************************/ + """ stdprolog_cmake = """# @@ -24,11 +26,54 @@ stdprolog_cmake = """# #This file is generated using the logic from <root>/src/scripts/genEventPipe.py #****************************************************************** + """ +eventpipe_dirname = "eventpipe" + +def generateMethodSignatureEnabled(eventName): + return "BOOL EventPipeEventEnabled%s()" % (eventName,) + +def generateMethodSignatureWrite(eventName, template, extern): + sig_pieces = [] + + if extern: sig_pieces.append('extern "C" ') + sig_pieces.append("ULONG EventPipeWriteEvent") + sig_pieces.append(eventName) + sig_pieces.append("(") + + if template: + sig_pieces.append("\n") + fnSig = template.signature + for paramName in fnSig.paramlist: + fnparam = fnSig.getParam(paramName) + wintypeName = fnparam.winType + typewName = palDataTypeMapping[wintypeName] + winCount = fnparam.count + countw = palDataTypeMapping[winCount] + + if paramName in template.structs: + sig_pieces.append( + "%sint %s_ElementSize,\n" % + (lindent, paramName)) + + sig_pieces.append(lindent) + sig_pieces.append(typewName) + if countw != " ": + sig_pieces.append(countw) + + sig_pieces.append(" ") + sig_pieces.append(fnparam.name) + sig_pieces.append(",\n") + + if len(sig_pieces) > 0: + del sig_pieces[-1] + + sig_pieces.append(")") + return ''.join(sig_pieces) def generateClrEventPipeWriteEventsImpl( - providerName, eventNodes, allTemplates, exclusionListFile): + providerName, eventNodes, allTemplates, extern): providerPrettyName = providerName.replace("Windows-", '') providerPrettyName = providerPrettyName.replace("Microsoft-", '') providerPrettyName = providerPrettyName.replace('-', '_') @@ -47,54 +92,24 @@ def generateClrEventPipeWriteEventsImpl( templateName = eventNode.getAttribute('template') # generate EventPipeEventEnabled function - eventEnabledImpl = """bool EventPipeEventEnabled%s() + eventEnabledImpl = generateMethodSignatureEnabled(eventName) + """ { return EventPipeEvent%s->IsEnabled(); } -""" % (eventName, eventName) +""" % eventName WriteEventImpl.append(eventEnabledImpl) # generate EventPipeWriteEvent function fnptype = [] - linefnptype = [] - fnptype.append("extern \"C\" ULONG EventPipeWriteEvent") - fnptype.append(eventName) - fnptype.append("(\n") if templateName: template = allTemplates[templateName] else: template = None - if template: - fnSig = template.signature - for paramName in fnSig.paramlist: - fnparam = fnSig.getParam(paramName) - wintypeName = fnparam.winType - typewName = palDataTypeMapping[wintypeName] - winCount = fnparam.count - countw = palDataTypeMapping[winCount] - - if paramName in template.structs: - linefnptype.append( - "%sint %s_ElementSize,\n" % - (lindent, paramName)) - - linefnptype.append(lindent) - linefnptype.append(typewName) - if countw != " ": - linefnptype.append(countw) - - linefnptype.append(" ") - linefnptype.append(fnparam.name) - linefnptype.append(",\n") - - if len(linefnptype) > 0: - del linefnptype[-1] - - fnptype.extend(linefnptype) - fnptype.append(")\n{\n") + fnptype.append(generateMethodSignatureWrite(eventName, template, extern)) + fnptype.append("\n{\n") checking = """ if (!EventPipeEventEnabled%s()) return ERROR_SUCCESS; """ % (eventName) @@ -115,8 +130,9 @@ def generateClrEventPipeWriteEventsImpl( WriteEventImpl.append("\n return ERROR_SUCCESS;\n}\n\n") # EventPipeProvider and EventPipeEvent initialization + if extern: WriteEventImpl.append('extern "C" ') WriteEventImpl.append( - "extern \"C\" void Init" + + "void Init" + providerPrettyName + "()\n{\n") WriteEventImpl.append( @@ -134,7 +150,6 @@ def generateClrEventPipeWriteEventsImpl( eventVersion = eventNode.getAttribute('version') eventLevel = eventNode.getAttribute('level') eventLevel = eventLevel.replace("win:", "EventPipeEventLevel::") - exclusionInfo = parseExclusionList(exclusionListFile) taskName = eventNode.getAttribute('task') initEvent = """ EventPipeEvent%s = EventPipeProvider%s->AddEvent(%s,%s,%s,%s); @@ -150,8 +165,8 @@ def generateWriteEventBody(template, providerName, eventName): header = """ char stackBuffer[%s]; char *buffer = stackBuffer; - unsigned int offset = 0; - unsigned int size = %s; + size_t offset = 0; + size_t size = %s; bool fixedBuffer = true; bool success = true; @@ -198,7 +213,7 @@ def generateWriteEventBody(template, providerName, eventName): }\n\n""" body = " EventPipe::WriteEvent(*EventPipeEvent" + \ - eventName + ", (BYTE *)buffer, offset);\n" + eventName + ", (BYTE *)buffer, (unsigned int)offset);\n" footer = """ if (!fixedBuffer) @@ -221,20 +236,23 @@ def generateEventKeywords(eventKeywords): return mask -def generateEventPipeCmakeFile(etwmanifest, eventpipe_directory): +def generateEventPipeCmakeFile(etwmanifest, eventpipe_directory, extern): tree = DOM.parse(etwmanifest) - with open(eventpipe_directory + "CMakeLists.txt", 'w') as topCmake: - topCmake.write(stdprolog_cmake + "\n") - topCmake.write("""cmake_minimum_required(VERSION 2.8.12.2) + with open_for_update(os.path.join(eventpipe_directory, "CMakeLists.txt")) as cmake_file: + cmake_file.write(stdprolog_cmake) + cmake_file.write("cmake_minimum_required(VERSION 2.8.12.2)\n") + if extern: cmake_file.write("\nproject(eventpipe)\n") + cmake_file.write(""" - project(eventpipe) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +include_directories(${CLR_DIR}/src/vm) - set(CMAKE_INCLUDE_CURRENT_DIR ON) - include_directories(${CLR_DIR}/src/vm) - - add_library(eventpipe - STATIC\n""") +""") + if extern: cmake_file.write("add_library") + else: cmake_file.write("add_library_clr") + cmake_file.write("""(eventpipe + STATIC\n""") for providerNode in tree.getElementsByTagName('provider'): providerName = providerNode.getAttribute('name') @@ -244,34 +262,41 @@ def generateEventPipeCmakeFile(etwmanifest, eventpipe_directory): providerName_File = providerName.replace('-', '') providerName_File = providerName_File.lower() - topCmake.write(' "%s.cpp"\n' % (providerName_File)) - topCmake.write(' "eventpipehelpers.cpp"\n') - topCmake.write(""" ) - - add_dependencies(eventpipe GeneratedEventingFiles) - - # Install the static eventpipe library - install(TARGETS eventpipe DESTINATION lib) - """) - - topCmake.close() + cmake_file.write(' "%s/%s.cpp"\n' % (eventpipe_dirname, providerName_File)) + cmake_file.write(' "%s/eventpipehelpers.cpp"\n)' % (eventpipe_dirname,)) + if extern: cmake_file.write(""" +# Install the static eventpipe library +install(TARGETS eventpipe DESTINATION lib) +""") -def generateEventPipeHelperFile(etwmanifest, eventpipe_directory): - with open(eventpipe_directory + "eventpipehelpers.cpp", 'w') as helper: - helper.write(stdprolog) +def generateEventPipeHelperFile(etwmanifest, eventpipe_directory, extern): + with open_for_update(os.path.join(eventpipe_directory, "eventpipehelpers.cpp")) as helper: + helper.write(stdprolog_cpp) helper.write(""" -#include "stdlib.h" - -bool ResizeBuffer(char *&buffer, unsigned int& size, unsigned int currLen, unsigned int newSize, bool &fixedBuffer) +#include "common.h" +#include <stdlib.h> +#include <string.h> + +#ifndef FEATURE_PAL +#include <windef.h> +#include <crtdbg.h> +#else +#include "pal.h" +#endif //FEATURE_PAL + +bool ResizeBuffer(char *&buffer, size_t& size, size_t currLen, size_t newSize, bool &fixedBuffer) { - newSize *= 1.5; + newSize = (size_t)(newSize * 1.5); _ASSERTE(newSize > size); // check for overflow if (newSize < 32) newSize = 32; - char *newBuffer = new char[newSize]; + char *newBuffer = new (nothrow) char[newSize]; + + if (newBuffer == NULL) + return false; memcpy(newBuffer, buffer, currLen); @@ -285,7 +310,7 @@ bool ResizeBuffer(char *&buffer, unsigned int& size, unsigned int currLen, unsig return true; } -bool WriteToBuffer(const BYTE *src, unsigned int len, char *&buffer, unsigned int& offset, unsigned int& size, bool &fixedBuffer) +bool WriteToBuffer(const BYTE *src, size_t len, char *&buffer, size_t& offset, size_t& size, bool &fixedBuffer) { if(!src) return true; if (offset + len > size) @@ -299,10 +324,10 @@ bool WriteToBuffer(const BYTE *src, unsigned int len, char *&buffer, unsigned in return true; } -bool WriteToBuffer(PCWSTR str, char *&buffer, unsigned int& offset, unsigned int& size, bool &fixedBuffer) +bool WriteToBuffer(PCWSTR str, char *&buffer, size_t& offset, size_t& size, bool &fixedBuffer) { if(!str) return true; - unsigned int byteCount = (PAL_wcslen(str) + 1) * sizeof(*str); + size_t byteCount = (wcslen(str) + 1) * sizeof(*str); if (offset + byteCount > size) { @@ -315,10 +340,10 @@ bool WriteToBuffer(PCWSTR str, char *&buffer, unsigned int& offset, unsigned int return true; } -bool WriteToBuffer(const char *str, char *&buffer, unsigned int& offset, unsigned int& size, bool &fixedBuffer) +bool WriteToBuffer(const char *str, char *&buffer, size_t& offset, size_t& size, bool &fixedBuffer) { if(!str) return true; - unsigned int len = strlen(str) + 1; + size_t len = strlen(str) + 1; if (offset + len > size) { if (!ResizeBuffer(buffer, size, offset, size + len, fixedBuffer)) @@ -339,12 +364,18 @@ bool WriteToBuffer(const char *str, char *&buffer, unsigned int& offset, unsigne providerPrettyName = providerName.replace("Windows-", '') providerPrettyName = providerPrettyName.replace("Microsoft-", '') providerPrettyName = providerPrettyName.replace('-', '_') + if extern: helper.write( + 'extern "C" ' + ) helper.write( - "extern \"C\" void Init" + + "void Init" + providerPrettyName + "();\n\n") - helper.write("extern \"C\" void InitProvidersAndEvents()\n{\n") + if extern: helper.write( + 'extern "C" ' + ) + helper.write("void InitProvidersAndEvents()\n{\n") for providerNode in tree.getElementsByTagName('provider'): providerName = providerNode.getAttribute('name') providerPrettyName = providerName.replace("Windows-", '') @@ -355,11 +386,20 @@ bool WriteToBuffer(const char *str, char *&buffer, unsigned int& offset, unsigne helper.close() - def generateEventPipeImplFiles( - etwmanifest, eventpipe_directory, exclusionListFile): + etwmanifest, eventpipe_directory, extern): tree = DOM.parse(etwmanifest) - coreclrRoot = os.getcwd() + + # Find the src directory starting with the assumption that + # A) It is named 'src' + # B) This script lives in it + src_dirname = os.path.dirname(__file__) + while os.path.basename(src_dirname) != "src": + src_dirname = os.path.dirname(src_dirname) + + if os.path.basename(src_dirname) == "": + raise IOError("Could not find the Core CLR 'src' directory") + for providerNode in tree.getElementsByTagName('provider'): providerName = providerNode.getAttribute('name') @@ -368,74 +408,77 @@ def generateEventPipeImplFiles( providerName_File = providerPrettyName.replace('-', '') providerName_File = providerName_File.lower() providerPrettyName = providerPrettyName.replace('-', '_') - eventpipefile = eventpipe_directory + providerName_File + ".cpp" - eventpipeImpl = open(eventpipefile, 'w') - eventpipeImpl.write(stdprolog) + eventpipefile = os.path.join(eventpipe_directory, providerName_File + ".cpp") + with open_for_update(eventpipefile) as eventpipeImpl: + eventpipeImpl.write(stdprolog_cpp) + + header = """ +#include "{root:s}/vm/common.h" +#include "{root:s}/vm/eventpipeprovider.h" +#include "{root:s}/vm/eventpipeevent.h" +#include "{root:s}/vm/eventpipe.h" - header = """ -#include \"%s/src/vm/common.h\" -#include \"%s/src/vm/eventpipeprovider.h\" -#include \"%s/src/vm/eventpipeevent.h\" -#include \"%s/src/vm/eventpipe.h\" +#if defined(FEATURE_PAL) +#define wcslen PAL_wcslen +#endif -bool ResizeBuffer(char *&buffer, unsigned int& size, unsigned int currLen, unsigned int newSize, bool &fixedBuffer); -bool WriteToBuffer(PCWSTR str, char *&buffer, unsigned int& offset, unsigned int& size, bool &fixedBuffer); -bool WriteToBuffer(const char *str, char *&buffer, unsigned int& offset, unsigned int& size, bool &fixedBuffer); -bool WriteToBuffer(const BYTE *src, unsigned int len, char *&buffer, unsigned int& offset, unsigned int& size, bool &fixedBuffer); +bool ResizeBuffer(char *&buffer, size_t& size, size_t currLen, size_t newSize, bool &fixedBuffer); +bool WriteToBuffer(PCWSTR str, char *&buffer, size_t& offset, size_t& size, bool &fixedBuffer); +bool WriteToBuffer(const char *str, char *&buffer, size_t& offset, size_t& size, bool &fixedBuffer); +bool WriteToBuffer(const BYTE *src, size_t len, char *&buffer, size_t& offset, size_t& size, bool &fixedBuffer); template <typename T> -bool WriteToBuffer(const T &value, char *&buffer, unsigned int& offset, unsigned int& size, bool &fixedBuffer) -{ +bool WriteToBuffer(const T &value, char *&buffer, size_t& offset, size_t& size, bool &fixedBuffer) +{{ if (sizeof(T) + offset > size) - { - if (!ResizeBuffer(buffer, size, offset, size + sizeof(T), fixedBuffer)) - return false; - } + {{ + if (!ResizeBuffer(buffer, size, offset, size + sizeof(T), fixedBuffer)) + return false; + }} *(T *)(buffer + offset) = value; offset += sizeof(T); return true; -} +}} -""" % (coreclrRoot, coreclrRoot, coreclrRoot, coreclrRoot) +""".format(root=src_dirname.replace('\\', '/')) - eventpipeImpl.write(header) - eventpipeImpl.write( - "const WCHAR* %sName = W(\"%s\");\n" % ( - providerPrettyName, - providerName + eventpipeImpl.write(header) + eventpipeImpl.write( + "const WCHAR* %sName = W(\"%s\");\n" % ( + providerPrettyName, + providerName + ) ) - ) - eventpipeImpl.write( - "EventPipeProvider *EventPipeProvider%s = nullptr;\n" % ( - providerPrettyName, + eventpipeImpl.write( + "EventPipeProvider *EventPipeProvider%s = nullptr;\n" % ( + providerPrettyName, + ) ) - ) - templateNodes = providerNode.getElementsByTagName('template') - allTemplates = parseTemplateNodes(templateNodes) - eventNodes = providerNode.getElementsByTagName('event') - eventpipeImpl.write( - generateClrEventPipeWriteEventsImpl( - providerName, - eventNodes, - allTemplates, - exclusionListFile) + "\n") - eventpipeImpl.close() + templateNodes = providerNode.getElementsByTagName('template') + allTemplates = parseTemplateNodes(templateNodes) + eventNodes = providerNode.getElementsByTagName('event') + eventpipeImpl.write( + generateClrEventPipeWriteEventsImpl( + providerName, + eventNodes, + allTemplates, + extern) + "\n") def generateEventPipeFiles( - etwmanifest, eventpipe_directory, exclusionListFile): - eventpipe_directory = eventpipe_directory + "/" + etwmanifest, intermediate, extern): + eventpipe_directory = os.path.join(intermediate, eventpipe_dirname) tree = DOM.parse(etwmanifest) if not os.path.exists(eventpipe_directory): os.makedirs(eventpipe_directory) - # generate Cmake file - generateEventPipeCmakeFile(etwmanifest, eventpipe_directory) + # generate CMake file + generateEventPipeCmakeFile(etwmanifest, intermediate, extern) # generate helper file - generateEventPipeHelperFile(etwmanifest, eventpipe_directory) + generateEventPipeHelperFile(etwmanifest, eventpipe_directory, extern) # generate all keywords for keywordNode in tree.getElementsByTagName('keyword'): @@ -447,12 +490,12 @@ def generateEventPipeFiles( generateEventPipeImplFiles( etwmanifest, eventpipe_directory, - exclusionListFile) + extern + ) import argparse import sys - def main(argv): # parse the command line @@ -464,19 +507,19 @@ def main(argv): help='full path to manifest containig the description of events') required.add_argument('--intermediate', type=str, required=True, help='full path to eventprovider intermediate directory') - required.add_argument('--exc', type=str, required=True, - help='full path to exclusion list') + required.add_argument('--nonextern', action='store_true', + help='if specified, will generate files to be compiled into the CLR rather than extern' ) args, unknown = parser.parse_known_args(argv) if unknown: print('Unknown argument(s): ', ', '.join(unknown)) - return const.UnknownArguments + return 1 sClrEtwAllMan = args.man intermediate = args.intermediate - exclusionListFile = args.exc + extern = not args.nonextern - generateEventPipeFiles(sClrEtwAllMan, intermediate, exclusionListFile) + generateEventPipeFiles(sClrEtwAllMan, intermediate, extern) if __name__ == '__main__': return_code = main(sys.argv[1:]) - sys.exit(return_code) + sys.exit(return_code)
\ No newline at end of file diff --git a/src/scripts/genXplatEventing.py b/src/scripts/genEventing.py index 4c9ce873b7..d6d6afbb90 100644 --- a/src/scripts/genXplatEventing.py +++ b/src/scripts/genEventing.py @@ -6,7 +6,7 @@ # #USAGE: #Add Events: modify <root>src/vm/ClrEtwAll.man -#Look at the Code in <root>/src/scripts/genXplatLttng.py for using subroutines in this file +#Look at the Code in <root>/src/scripts/genLttngProvider.py for using subroutines in this file # # Python 2 compatibility @@ -14,6 +14,7 @@ from __future__ import print_function import os import xml.dom.minidom as DOM +from utilities import open_for_update stdprolog=""" // Licensed to the .NET Foundation under one or more agreements. @@ -23,7 +24,7 @@ stdprolog=""" /****************************************************************** DO NOT MODIFY. AUTOGENERATED FILE. -This file is generated using the logic from <root>/src/scripts/genXplatEventing.py +This file is generated using the logic from <root>/src/scripts/genEventing.py ******************************************************************/ """ @@ -34,7 +35,7 @@ stdprolog_cmake=""" #****************************************************************** #DO NOT MODIFY. AUTOGENERATED FILE. -#This file is generated using the logic from <root>/src/scripts/genXplatEventing.py +#This file is generated using the logic from <root>/src/scripts/genEventing.py #****************************************************************** """ @@ -260,7 +261,7 @@ def parseTemplateNodes(templateNodes): assert(countVarName in fnPrototypes.paramlist) if not countVarName: raise ValueError("Struct '%s' in template '%s' does not have an attribute count." % (structName, templateName)) - + names = [x.attributes['name'].value for x in structToBeMarshalled.getElementsByTagName("data")] types = [x.attributes['inType'].value for x in structToBeMarshalled.getElementsByTagName("data")] @@ -305,8 +306,8 @@ def generateClrallEvents(eventNodes,allTemplates): typewName = palDataTypeMapping[wintypeName] winCount = fnparam.count countw = palDataTypeMapping[winCount] - - + + if params in template.structs: fnptypeline.append("%sint %s_ElementSize,\n" % (lindent, params)) @@ -321,7 +322,7 @@ def generateClrallEvents(eventNodes,allTemplates): for params in fnSig.paramlist: fnparam = fnSig.getParam(params) - if params in template.structs: + if params in template.structs: line.append(fnparam.name + "_ElementSize") line.append(", ") @@ -349,20 +350,23 @@ def generateClrallEvents(eventNodes,allTemplates): return ''.join(clrallEvents) -def generateClrXplatEvents(eventNodes, allTemplates): +def generateClrXplatEvents(eventNodes, allTemplates, extern): clrallEvents = [] for eventNode in eventNodes: eventName = eventNode.getAttribute('symbol') templateName = eventNode.getAttribute('template') #generate EventEnabled - clrallEvents.append("extern \"C\" BOOL EventXplatEnabled") + if extern: clrallEvents.append('extern "C" ') + clrallEvents.append("BOOL EventXplatEnabled") clrallEvents.append(eventName) clrallEvents.append("();\n") + #generate FireEtw functions fnptype = [] fnptypeline = [] - fnptype.append("extern \"C\" ULONG FireEtXplat") + if extern: fnptype.append('extern "C" ') + fnptype.append("ULONG FireEtXplat") fnptype.append(eventName) fnptype.append("(\n") @@ -377,7 +381,7 @@ def generateClrXplatEvents(eventNodes, allTemplates): winCount = fnparam.count countw = palDataTypeMapping[winCount] - + if params in template.structs: fnptypeline.append("%sint %s_ElementSize,\n" % (lindent, params)) @@ -398,7 +402,7 @@ def generateClrXplatEvents(eventNodes, allTemplates): return ''.join(clrallEvents) -def generateClrEventPipeWriteEvents(eventNodes, allTemplates): +def generateClrEventPipeWriteEvents(eventNodes, allTemplates, extern): clrallEvents = [] for eventNode in eventNodes: eventName = eventNode.getAttribute('symbol') @@ -409,11 +413,13 @@ def generateClrEventPipeWriteEvents(eventNodes, allTemplates): writeevent = [] fnptypeline = [] - eventenabled.append("extern \"C\" bool EventPipeEventEnabled") + if extern:eventenabled.append('extern "C" ') + eventenabled.append("BOOL EventPipeEventEnabled") eventenabled.append(eventName) eventenabled.append("();\n") - writeevent.append("extern \"C\" ULONG EventPipeWriteEvent") + if extern: writeevent.append('extern "C" ') + writeevent.append("ULONG EventPipeWriteEvent") writeevent.append(eventName) writeevent.append("(\n") @@ -554,63 +560,70 @@ def generateSanityTest(sClrEtwAllMan,testDir): cmake_file = testDir + "/CMakeLists.txt" test_cpp = "clralltestevents.cpp" testinfo = testDir + "/testinfo.dat" - Cmake_file = open(cmake_file,'w') - Test_cpp = open(testDir + "/" + test_cpp,'w') - Testinfo = open(testinfo,'w') #CMake File: - Cmake_file.write(stdprolog_cmake) - Cmake_file.write(""" - cmake_minimum_required(VERSION 2.8.12.2) - set(CMAKE_INCLUDE_CURRENT_DIR ON) - set(SOURCES - """) - Cmake_file.write(test_cpp) - Cmake_file.write(""" - ) - include_directories(${GENERATED_INCLUDE_DIR}) + with open_for_update(cmake_file) as Cmake_file: + Cmake_file.write(stdprolog_cmake) + Cmake_file.write(""" +cmake_minimum_required(VERSION 2.8.12.2) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(SOURCES +""") + Cmake_file.write(test_cpp) + Cmake_file.write(""" + ) +include_directories(${GENERATED_INCLUDE_DIR}) + +if(FEATURE_PAL) include_directories(${COREPAL_SOURCE_DIR}/inc/rt) +endif(FEATURE_PAL) + +add_executable(eventprovidertest + ${SOURCES} + ) +set(EVENT_PROVIDER_DEPENDENCIES "") +set(EVENT_PROVIDER_LINKER_OTPTIONS "") +if(FEATURE_EVENT_TRACE) + add_definitions(-DFEATURE_EVENT_TRACE=1) + list(APPEND EVENT_PROVIDER_DEPENDENCIES + eventprovider + ) + if(CLR_CMAKE_PLATFORM_LINUX) + list(APPEND EVENT_PROVIDER_DEPENDENCIES + coreclrtraceptprovider + ) + endif(CLR_CMAKE_PLATFORM_LINUX) + list(APPEND EVENT_PROVIDER_LINKER_OTPTIONS + ${EVENT_PROVIDER_DEPENDENCIES} + ) +endif(FEATURE_EVENT_TRACE) + +add_dependencies(eventprovidertest ${EVENT_PROVIDER_DEPENDENCIES} coreclrpal) +target_link_libraries(eventprovidertest + coreclrpal + ${EVENT_PROVIDER_LINKER_OTPTIONS} + ) +""") + + + with open_for_update(testinfo) as Testinfo: + Testinfo.write(""" +Copyright (c) Microsoft Corporation. All rights reserved. +# - add_executable(eventprovidertest - ${SOURCES} - ) - set(EVENT_PROVIDER_DEPENDENCIES "") - set(EVENT_PROVIDER_LINKER_OTPTIONS "") - if(FEATURE_EVENT_TRACE) - add_definitions(-DFEATURE_EVENT_TRACE=1) - list(APPEND EVENT_PROVIDER_DEPENDENCIES - coreclrtraceptprovider - eventprovider - ) - list(APPEND EVENT_PROVIDER_LINKER_OTPTIONS - ${EVENT_PROVIDER_DEPENDENCIES} - ) - - endif(FEATURE_EVENT_TRACE) - - add_dependencies(eventprovidertest ${EVENT_PROVIDER_DEPENDENCIES} coreclrpal) - target_link_libraries(eventprovidertest - coreclrpal - ${EVENT_PROVIDER_LINKER_OTPTIONS} - ) - """) - Testinfo.write(""" - Copyright (c) Microsoft Corporation. All rights reserved. - # - - Version = 1.0 - Section = EventProvider - Function = EventProvider - Name = PAL test for FireEtW* and EventEnabled* functions - TYPE = DEFAULT - EXE1 = eventprovidertest - Description - =This is a sanity test to check that there are no crashes in Xplat eventing - """) +Version = 1.0 +Section = EventProvider +Function = EventProvider +Name = PAL test for FireEtW* and EventEnabled* functions +TYPE = DEFAULT +EXE1 = eventprovidertest +Description = This is a sanity test to check that there are no crashes in Xplat eventing +""") #Test.cpp - Test_cpp.write(stdprolog) - Test_cpp.write(""" + with open_for_update(testDir + "/" + test_cpp) as Test_cpp: + Test_cpp.write(stdprolog) + Test_cpp.write(""" /*===================================================================== ** ** Source: clralltestevents.cpp @@ -619,7 +632,9 @@ def generateSanityTest(sClrEtwAllMan,testDir): ** ** **===================================================================*/ +#if FEATURE_PAL #include <palsuite.h> +#endif //FEATURE_PAL #include <clrxplatevents.h> typedef struct _Struct1 { @@ -646,39 +661,42 @@ int win_Int32 = 12; BYTE* win_Binary =(BYTE*)var21 ; int __cdecl main(int argc, char **argv) { - +#if defined(FEATURE_PAL) /* Initialize the PAL. */ if(0 != PAL_Initialize(argc, argv)) { - return FAIL; + return FAIL; } +#endif ULONG Error = ERROR_SUCCESS; #if defined(FEATURE_EVENT_TRACE) Trace("\\n Starting functional eventing APIs tests \\n"); """) - Test_cpp.write(generateClralltestEvents(sClrEtwAllMan)) - Test_cpp.write(""" -/* Shutdown the PAL. - */ - - if (Error != ERROR_SUCCESS) - { - Fail("One or more eventing Apis failed\\n "); - return FAIL; - } - Trace("\\n All eventing APIs were fired succesfully \\n"); + Test_cpp.write(generateClralltestEvents(sClrEtwAllMan)) + Test_cpp.write(""" + + if (Error != ERROR_SUCCESS) + { + Fail("One or more eventing Apis failed\\n "); + return FAIL; + } + Trace("\\n All eventing APIs were fired succesfully \\n"); #endif //defined(FEATURE_EVENT_TRACE) - PAL_Terminate(); - return PASS; - } +#if defined(FEATURE_PAL) + +/* Shutdown the PAL. +*/ + + PAL_Terminate(); +#endif + return PASS; + } """) - Cmake_file.close() - Test_cpp.close() Testinfo.close() def generateEtmDummyHeader(sClrEtwAllMan,clretwdummy): @@ -693,19 +711,17 @@ def generateEtmDummyHeader(sClrEtwAllMan,clretwdummy): if not os.path.exists(incDir): os.makedirs(incDir) - Clretwdummy = open(clretwdummy,'w') - Clretwdummy.write(stdprolog + "\n") - - for providerNode in tree.getElementsByTagName('provider'): - templateNodes = providerNode.getElementsByTagName('template') - allTemplates = parseTemplateNodes(templateNodes) - eventNodes = providerNode.getElementsByTagName('event') - #pal: create etmdummy.h - Clretwdummy.write(generateclrEtwDummy(eventNodes, allTemplates) + "\n") + with open_for_update(clretwdummy) as Clretwdummy: + Clretwdummy.write(stdprolog + "\n") - Clretwdummy.close() + for providerNode in tree.getElementsByTagName('provider'): + templateNodes = providerNode.getElementsByTagName('template') + allTemplates = parseTemplateNodes(templateNodes) + eventNodes = providerNode.getElementsByTagName('event') + #pal: create etmdummy.h + Clretwdummy.write(generateclrEtwDummy(eventNodes, allTemplates) + "\n") -def generatePlformIndependentFiles(sClrEtwAllMan,incDir,etmDummyFile): +def generatePlatformIndependentFiles(sClrEtwAllMan, incDir, etmDummyFile, extern): generateEtmDummyHeader(sClrEtwAllMan,etmDummyFile) tree = DOM.parse(sClrEtwAllMan) @@ -717,152 +733,43 @@ def generatePlformIndependentFiles(sClrEtwAllMan,incDir,etmDummyFile): if not os.path.exists(incDir): os.makedirs(incDir) - clrallevents = incDir + "/clretwallmain.h" - clrxplatevents = incDir + "/clrxplatevents.h" - clreventpipewriteevents = incDir + "/clreventpipewriteevents.h" - - Clrallevents = open(clrallevents,'w') - Clrxplatevents = open(clrxplatevents,'w') - Clreventpipewriteevents = open(clreventpipewriteevents,'w') + # Write the main header for FireETW* functions + clrallevents = os.path.join(incDir, "clretwallmain.h") + with open_for_update(clrallevents) as Clrallevents: + Clrallevents.write(stdprolog) + Clrallevents.write(""" +#include "clrxplatevents.h" +#include "clreventpipewriteevents.h" - Clrallevents.write(stdprolog + "\n") - Clrxplatevents.write(stdprolog + "\n") - Clreventpipewriteevents.write(stdprolog + "\n") +""") + for providerNode in tree.getElementsByTagName('provider'): + templateNodes = providerNode.getElementsByTagName('template') + allTemplates = parseTemplateNodes(templateNodes) + eventNodes = providerNode.getElementsByTagName('event') - Clrallevents.write("\n#include \"clrxplatevents.h\"\n") - Clrallevents.write("#include \"clreventpipewriteevents.h\"\n\n") - - for providerNode in tree.getElementsByTagName('provider'): - templateNodes = providerNode.getElementsByTagName('template') - allTemplates = parseTemplateNodes(templateNodes) - eventNodes = providerNode.getElementsByTagName('event') - #vm header: - Clrallevents.write(generateClrallEvents(eventNodes, allTemplates) + "\n") + #vm header: + Clrallevents.write(generateClrallEvents(eventNodes, allTemplates) + "\n") - #pal: create clrallevents.h - Clrxplatevents.write(generateClrXplatEvents(eventNodes, allTemplates) + "\n") - #eventpipe: create clreventpipewriteevents.h - Clreventpipewriteevents.write(generateClrEventPipeWriteEvents(eventNodes, allTemplates) + "\n") + # Write secondary headers for FireEtXplat* and EventPipe* functions + clrxplatevents = os.path.join(incDir, "clrxplatevents.h") + clreventpipewriteevents = os.path.join(incDir, "clreventpipewriteevents.h") + with open_for_update(clrxplatevents) as Clrxplatevents: + with open_for_update(clreventpipewriteevents) as Clreventpipewriteevents: + Clrxplatevents.write(stdprolog + "\n") + Clreventpipewriteevents.write(stdprolog + "\n") - Clrxplatevents.close() - Clrallevents.close() - Clreventpipewriteevents.close() + for providerNode in tree.getElementsByTagName('provider'): + templateNodes = providerNode.getElementsByTagName('template') + allTemplates = parseTemplateNodes(templateNodes) + eventNodes = providerNode.getElementsByTagName('event') -class EventExclusions: - def __init__(self): - self.nostack = set() - self.explicitstack = set() - self.noclrinstance = set() - -def parseExclusionList(exclusionListFile): - ExclusionFile = open(exclusionListFile,'r') - 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 "+ exclusionListFile) - - 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) - ExclusionFile.close() - - 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(sClrEtwAllMan,exclusionListFile): - tree = DOM.parse(sClrEtwAllMan) - exclusionInfo = parseExclusionList(exclusionListFile) - for providerNode in tree.getElementsByTagName('provider'): + #pal: create clrallevents.h + Clrxplatevents.write(generateClrXplatEvents(eventNodes, allTemplates, extern) + "\n") - stackSupportSpecified = {} - eventNodes = providerNode.getElementsByTagName('event') - templateNodes = providerNode.getElementsByTagName('template') - eventProvider = providerNode.getAttribute('name') - allTemplates = parseTemplateNodes(templateNodes) + #eventpipe: create clreventpipewriteevents.h + Clreventpipewriteevents.write(generateClrEventPipeWriteEvents(eventNodes, allTemplates, extern) + "\n") - 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(exclusionListFile + ":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 = exclusionListFile + ": 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) import argparse import sys @@ -874,28 +781,28 @@ def main(argv): 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('--inc', type=str, default=None, help='full path to directory where the header files will be generated') required.add_argument('--dummy', type=str,default=None, help='full path to file that will have dummy definitions of FireEtw functions') required.add_argument('--testdir', type=str, default=None, help='full path to directory where the test assets will be deployed' ) + required.add_argument('--nonextern', action='store_true', + help='if specified, will not generated extern function stub headers' ) args, unknown = parser.parse_known_args(argv) if unknown: print('Unknown argument(s): ', ', '.join(unknown)) - return const.UnknownArguments + return 1 sClrEtwAllMan = args.man - exclusionListFile = args.exc incdir = args.inc etmDummyFile = args.dummy testDir = args.testdir + extern = not args.nonextern + + generatePlatformIndependentFiles(sClrEtwAllMan, incdir, etmDummyFile, extern) + generateSanityTest(sClrEtwAllMan, testDir) - checkConsistency(sClrEtwAllMan, exclusionListFile) - generatePlformIndependentFiles(sClrEtwAllMan,incdir,etmDummyFile) - generateSanityTest(sClrEtwAllMan,testDir) if __name__ == '__main__': return_code = main(sys.argv[1:]) - sys.exit(return_code) + sys.exit(return_code)
\ No newline at end of file diff --git a/src/scripts/genXplatLttng.py b/src/scripts/genLttngProvider.py index 9959895f5a..382cb74675 100644 --- a/src/scripts/genXplatLttng.py +++ b/src/scripts/genLttngProvider.py @@ -50,7 +50,8 @@ # import os -from genXplatEventing import * +from genEventing import * +from utilities import open_for_update stdprolog=""" // Licensed to the .NET Foundation under one or more agreements. @@ -60,7 +61,7 @@ stdprolog=""" /****************************************************************** DO NOT MODIFY. AUTOGENERATED FILE. -This file is generated using the logic from <root>/src/scripts/genXplatLttng.py +This file is generated using the logic from <root>/src/scripts/genLttngProvider.py ******************************************************************/ """ @@ -70,7 +71,7 @@ stdprolog_cmake=""" #****************************************************************** #DO NOT MODIFY. AUTOGENERATED FILE. -#This file is generated using the logic from <root>/src/scripts/genXplatLttng.py +#This file is generated using the logic from <root>/src/scripts/genLttngProvider.py #****************************************************************** """ @@ -159,7 +160,7 @@ def generateArgList(template): def generateFieldList(template): header = " " + " TP_FIELDS(\n" footer = "\n )\n)\n" - + if shouldPackTemplate(template): field_list = " ctf_integer(ULONG, length, length)\n" field_list += " ctf_sequence(char, __data__, __data__, ULONG, length)" @@ -209,7 +210,7 @@ def generateLttngHeader(providerName, allTemplates, eventNodes): for templateName in allTemplates: template = allTemplates[templateName] fnSig = allTemplates[templateName].signature - + lTTngHdr.append("\n#define " + templateName + "_TRACEPOINT_ARGS \\\n") #TP_ARGS @@ -289,7 +290,7 @@ def generateMethodBody(template, providerName, eventName): return "\n do_tracepoint(%s, %s);\n" % (providerName, eventName) fnSig = template.signature - + for paramName in fnSig.paramlist: fnparam = fnSig.getParam(paramName) paramname = fnparam.name @@ -298,7 +299,7 @@ def generateMethodBody(template, providerName, eventName): result.append(" INT " + paramname + "_path_size = -1;\n") result.append(" PathCharString " + paramname + "_PS;\n") result.append(" INT " + paramname + "_full_name_path_size") - result.append(" = (PAL_wcslen(" + paramname + ") + 1)*sizeof(WCHAR);\n") + result.append(" = (wcslen(" + paramname + ") + 1)*sizeof(WCHAR);\n") result.append(" CHAR* " + paramname + "_full_name = ") result.append(paramname + "_PS.OpenStringBuffer(" + paramname + "_full_name_path_size );\n") result.append(" if (" + paramname + "_full_name == NULL )") @@ -308,7 +309,7 @@ def generateMethodBody(template, providerName, eventName): #emit tracepoints fnSig = template.signature - + if not shouldPackTemplate(template): linefnbody = [" do_tracepoint(%s,\n %s" % (providerName, eventName)] @@ -360,8 +361,8 @@ def generateMethodBody(template, providerName, eventName): header = """ char stackBuffer[%s]; char *buffer = stackBuffer; - int offset = 0; - int size = %s; + size_t offset = 0; + size_t size = %s; bool fixedBuffer = true; bool success = true; @@ -401,7 +402,7 @@ def generateMethodBody(template, providerName, eventName): do_tracepoint(%s, %s, offset, buffer);\n""" % (providerName, eventName) return header + code + tracepoint + footer - + @@ -420,7 +421,7 @@ def generateLttngTpProvider(providerName, eventNodes, allTemplates): fnptype.append(eventName) fnptype.append("(\n") - + if templateName: template = allTemplates[templateName] else: @@ -489,7 +490,7 @@ def generateLttngFiles(etwmanifest,eventprovider_directory): os.makedirs(eventprovider_directory + tracepointprovider_directory) #Top level Cmake - with open(eventprovider_directory + "CMakeLists.txt", 'w') as topCmake: + with open_for_update(eventprovider_directory + "CMakeLists.txt") as topCmake: topCmake.write(stdprolog_cmake + "\n") topCmake.write("""cmake_minimum_required(VERSION 2.8.12.2) @@ -514,7 +515,7 @@ def generateLttngFiles(etwmanifest,eventprovider_directory): providerName_File = providerName_File.lower() topCmake.write(' "%s%s.cpp"\n' % (lttngevntprovPre, providerName_File)) - + topCmake.write(' "%shelpers.cpp"\n' % (lttngevntprovPre,)) topCmake.write(""") add_subdirectory(tracepointprovider) @@ -525,7 +526,7 @@ def generateLttngFiles(etwmanifest,eventprovider_directory): #TracepointProvider Cmake - with open(eventprovider_directory + tracepointprovider_directory + "/CMakeLists.txt", 'w') as tracepointprovider_Cmake: + with open_for_update(eventprovider_directory + tracepointprovider_directory + "/CMakeLists.txt") as tracepointprovider_Cmake: tracepointprovider_Cmake.write(stdprolog_cmake + "\n") tracepointprovider_Cmake.write("""cmake_minimum_required(VERSION 2.8.12.2) @@ -562,7 +563,7 @@ def generateLttngFiles(etwmanifest,eventprovider_directory): install_clr(coreclrtraceptprovider) """) - with open(eventprovider_directory + lttng_directory + "/eventprovhelpers.cpp", 'w') as helper: + with open_for_update(eventprovider_directory + lttng_directory + "/eventprovhelpers.cpp") as helper: helper.write(""" #include "palrt.h" #include "pal.h" @@ -572,72 +573,81 @@ def generateLttngFiles(etwmanifest,eventprovider_directory): #include <new> #include <memory.h> -bool ResizeBuffer(char *&buffer, int& size, int currLen, int newSize, bool &fixedBuffer) +#define wcslen PAL_wcslen + +bool ResizeBuffer(char *&buffer, size_t& size, size_t currLen, size_t newSize, bool &fixedBuffer) { - newSize *= 1.5; - _ASSERTE(newSize > size); // check for overflow + newSize = (size_t)(newSize * 1.5); + _ASSERTE(newSize > size); // check for overflow if (newSize < 32) newSize = 32; - char *newBuffer = new char[newSize]; + // We can't use coreclr includes here so we use std::nothrow + // rather than the coreclr version + char *newBuffer = new (std::nothrow) char[newSize]; - memcpy(newBuffer, buffer, currLen); + if (newBuffer == NULL) + return false; - if (!fixedBuffer) - delete[] buffer; + memcpy(newBuffer, buffer, currLen); - buffer = newBuffer; - size = newSize; - fixedBuffer = false; + if (!fixedBuffer) + delete[] buffer; - return true; + buffer = newBuffer; + size = newSize; + fixedBuffer = false; + + return true; } -bool WriteToBuffer(const BYTE *src, int len, char *&buffer, int& offset, int& size, bool &fixedBuffer) +bool WriteToBuffer(const BYTE *src, size_t len, char *&buffer, size_t& offset, size_t& size, bool &fixedBuffer) { - if (!src) return true; - if (offset + len > size) - { - if (!ResizeBuffer(buffer, size, offset, size + len, fixedBuffer)) - return false; - } - - memcpy(buffer + offset, src, len); - offset += len; - return true; + if(!src) return true; + if (offset + len > size) + { + if (!ResizeBuffer(buffer, size, offset, size + len, fixedBuffer)) + return false; + } + + memcpy(buffer + offset, src, len); + offset += len; + return true; } -bool WriteToBuffer(PCWSTR str, char *&buffer, int& offset, int& size, bool &fixedBuffer) +bool WriteToBuffer(PCWSTR str, char *&buffer, size_t& offset, size_t& size, bool &fixedBuffer) { - if (!str) return true; - int byteCount = (PAL_wcslen(str) + 1) * sizeof(*str); - - if (offset + byteCount > size) - { - if (!ResizeBuffer(buffer, size, offset, size + byteCount, fixedBuffer)) - return false; - } - - memcpy(buffer + offset, str, byteCount); - offset += byteCount; - return true; + if(!str) return true; + size_t byteCount = (wcslen(str) + 1) * sizeof(*str); + + if (offset + byteCount > size) + { + if (!ResizeBuffer(buffer, size, offset, size + byteCount, fixedBuffer)) + return false; + } + + memcpy(buffer + offset, str, byteCount); + offset += byteCount; + return true; } -bool WriteToBuffer(const char *str, char *&buffer, int& offset, int& size, bool &fixedBuffer) +bool WriteToBuffer(const char *str, char *&buffer, size_t& offset, size_t& size, bool &fixedBuffer) { - if (!str) return true; - int len = strlen(str) + 1; - if (offset + len > size) - { - if (!ResizeBuffer(buffer, size, offset, size + len, fixedBuffer)) - return false; - } - - memcpy(buffer + offset, str, len); - offset += len; - return true; -}""") + if(!str) return true; + size_t len = strlen(str) + 1; + if (offset + len > size) + { + if (!ResizeBuffer(buffer, size, offset, size + len, fixedBuffer)) + return false; + } + + memcpy(buffer + offset, str, len); + offset += len; + return true; +} + +""") # Generate Lttng specific instrumentation for providerNode in tree.getElementsByTagName('provider'): @@ -650,24 +660,19 @@ bool WriteToBuffer(const char *str, char *&buffer, int& offset, int& size, bool providerName_File = providerName_File.lower() providerName = providerName.replace('-','_') - lttngevntheadershortname = "tp" + providerName_File +".h"; + lttngevntheadershortname = "tp" + providerName_File +".h" lttngevntheader = eventprovider_directory + "lttng/" + lttngevntheadershortname lttngevntprov = eventprovider_directory + lttngevntprovPre + providerName_File + ".cpp" lttngevntprovTp = eventprovider_directory + lttngevntprovTpPre + providerName_File +".cpp" - lTTngHdr = open(lttngevntheader, 'w') - lTTngImpl = open(lttngevntprov, 'w') - lTTngTpImpl = open(lttngevntprovTp, 'w') - - lTTngHdr.write(stdprolog + "\n") - lTTngImpl.write(stdprolog + "\n") - lTTngTpImpl.write(stdprolog + "\n") - - lTTngTpImpl.write("\n#define TRACEPOINT_CREATE_PROBES\n") + templateNodes = providerNode.getElementsByTagName('template') + eventNodes = providerNode.getElementsByTagName('event') + allTemplates = parseTemplateNodes(templateNodes) - lTTngTpImpl.write("#include \"./"+lttngevntheadershortname + "\"\n") - lTTngHdr.write(""" + with open_for_update(lttngevntheader) as lttnghdr_file: + lttnghdr_file.write(stdprolog + "\n") + lttnghdr_file.write(""" #include "palrt.h" #include "pal.h" @@ -675,20 +680,24 @@ bool WriteToBuffer(const char *str, char *&buffer, int& offset, int& size, bool """) - lTTngHdr.write("#define TRACEPOINT_PROVIDER " + providerName + "\n") - lTTngHdr.write(""" + lttnghdr_file.write("#define TRACEPOINT_PROVIDER " + providerName + "\n") + lttnghdr_file.write(""" #undef TRACEPOINT_INCLUDE """) - lTTngHdr.write("#define TRACEPOINT_INCLUDE \"./" + lttngevntheadershortname + "\"\n\n") + lttnghdr_file.write("#define TRACEPOINT_INCLUDE \"./" + lttngevntheadershortname + "\"\n\n") + + lttnghdr_file.write("#if !defined(LTTNG_CORECLR_H" + providerName + ") || defined(TRACEPOINT_HEADER_MULTI_READ)\n\n") + lttnghdr_file.write("#define LTTNG_CORECLR_H" + providerName + "\n") - lTTngHdr.write("#if !defined(LTTNG_CORECLR_H" + providerName + ") || defined(TRACEPOINT_HEADER_MULTI_READ)\n\n") - lTTngHdr.write("#define LTTNG_CORECLR_H" + providerName + "\n") + lttnghdr_file.write("\n#include <lttng/tracepoint.h>\n\n") - lTTngHdr.write("\n#include <lttng/tracepoint.h>\n\n") + lttnghdr_file.write(generateLttngHeader(providerName,allTemplates,eventNodes) + "\n") - lTTngImpl.write(""" + with open_for_update(lttngevntprov) as lttngimpl_file: + lttngimpl_file.write(stdprolog + "\n") + lttngimpl_file.write(""" #define TRACEPOINT_DEFINE #define TRACEPOINT_PROBE_DYNAMIC_LINKAGE @@ -700,48 +709,43 @@ bool WriteToBuffer(const char *str, char *&buffer, int& offset, int& size, bool #define PAL_realloc realloc #include "pal/stackstring.hpp" """) - lTTngImpl.write("#include \"" + lttngevntheadershortname + "\"\n\n") + lttngimpl_file.write("#include \"" + lttngevntheadershortname + "\"\n\n") - lTTngImpl.write("""#ifndef tracepoint_enabled + lttngimpl_file.write("""#ifndef tracepoint_enabled #define tracepoint_enabled(provider, name) TRUE #define do_tracepoint tracepoint #endif +#define wcslen PAL_wcslen -bool ResizeBuffer(char *&buffer, int& size, int currLen, int newSize, bool &fixedBuffer); -bool WriteToBuffer(PCWSTR str, char *&buffer, int& offset, int& size, bool &fixedBuffer); -bool WriteToBuffer(const char *str, char *&buffer, int& offset, int& size, bool &fixedBuffer); -bool WriteToBuffer(const BYTE *src, int len, char *&buffer, int& offset, int& size, bool &fixedBuffer); +bool ResizeBuffer(char *&buffer, size_t& size, size_t currLen, size_t newSize, bool &fixedBuffer); +bool WriteToBuffer(PCWSTR str, char *&buffer, size_t& offset, size_t& size, bool &fixedBuffer); +bool WriteToBuffer(const char *str, char *&buffer, size_t& offset, size_t& size, bool &fixedBuffer); +bool WriteToBuffer(const BYTE *src, size_t len, char *&buffer, size_t& offset, size_t& size, bool &fixedBuffer); template <typename T> -bool WriteToBuffer(const T &value, char *&buffer, int& offset, int& size, bool &fixedBuffer) +bool WriteToBuffer(const T &value, char *&buffer, size_t& offset, size_t& size, bool &fixedBuffer) { - if (sizeof(T) + offset > size) - { - if (!ResizeBuffer(buffer, size, offset, size + sizeof(T), fixedBuffer)) - return false; - } - - *(T *)(buffer + offset) = value; - offset += sizeof(T); - return true; + if (sizeof(T) + offset > size) + { + if (!ResizeBuffer(buffer, size, offset, size + sizeof(T), fixedBuffer)) + return false; + } + + *(T *)(buffer + offset) = value; + offset += sizeof(T); + return true; } """) + lttngimpl_file.write(generateLttngTpProvider(providerName,eventNodes,allTemplates) + "\n") - templateNodes = providerNode.getElementsByTagName('template') - eventNodes = providerNode.getElementsByTagName('event') - - allTemplates = parseTemplateNodes(templateNodes) - #generate the header - lTTngHdr.write(generateLttngHeader(providerName,allTemplates,eventNodes) + "\n") + with open_for_update(lttngevntprovTp) as tpimpl_file: + tpimpl_file.write(stdprolog + "\n") - #create the implementation of eventing functions : lttngeventprov*.cp - lTTngImpl.write(generateLttngTpProvider(providerName,eventNodes,allTemplates) + "\n") + tpimpl_file.write("\n#define TRACEPOINT_CREATE_PROBES\n") - lTTngHdr.close() - lTTngImpl.close() - lTTngTpImpl.close() + tpimpl_file.write("#include \"./"+lttngevntheadershortname + "\"\n") import argparse import sys @@ -759,7 +763,7 @@ def main(argv): args, unknown = parser.parse_known_args(argv) if unknown: print('Unknown argument(s): ', ', '.join(unknown)) - return const.UnknownArguments + return 1 sClrEtwAllMan = args.man intermediate = args.intermediate @@ -768,4 +772,4 @@ def main(argv): if __name__ == '__main__': return_code = main(sys.argv[1:]) - sys.exit(return_code) + sys.exit(return_code)
\ No newline at end of file diff --git a/src/scripts/genWinEtw.py b/src/scripts/genWinEtw.py deleted file mode 100644 index aa75f680cd..0000000000 --- a/src/scripts/genWinEtw.py +++ /dev/null @@ -1,125 +0,0 @@ - -## 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. -# - -import os -from genXplatEventing import * - -stdprolog=""" -// 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/genWinEtw.py - -******************************************************************/ - -""" -import argparse -import sys -import xml.dom.minidom as DOM - -def generateEtwMacroHeader(sClrEtwAllMan, sExcludeFile,macroHeader,inHeader): - tree = DOM.parse(sClrEtwAllMan) - numOfProviders = len(tree.getElementsByTagName('provider')) - nMaxEventBytesPerProvider = 64 - - exclusionInfo = parseExclusionList(sExcludeFile) - incDir = os.path.dirname(os.path.realpath(macroHeader)) - if not os.path.exists(incDir): - os.makedirs(incDir) - - outHeader = open(macroHeader,'w') - outHeader.write(stdprolog + "\n") - - outHeader.write("#include \"" + os.path.basename(inHeader) + '"\n') - outHeader.write("#define NO_OF_ETW_PROVIDERS " + str(numOfProviders) + "\n") - outHeader.write("#define MAX_BYTES_PER_ETW_PROVIDER " + str(nMaxEventBytesPerProvider) + "\n") - outHeader.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("},") - outHeader.write(''.join(line) + "\n") - outHeader.write("};\n") - - outHeader.close() - - -def generateEtwFiles(sClrEtwAllMan, exclusionListFile, etmdummyHeader, macroHeader, inHeader): - - checkConsistency(sClrEtwAllMan, exclusionListFile) - generateEtmDummyHeader(sClrEtwAllMan, etmdummyHeader) - generateEtwMacroHeader(sClrEtwAllMan, exclusionListFile, macroHeader, inHeader) - -def main(argv): - - #parse the command line - parser = argparse.ArgumentParser(description="Generates the Code required to instrument LTTtng 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('--eventheader', type=str, required=True, - help='full path to the header file') - required.add_argument('--macroheader', type=str, required=True, - help='full path to the macro header file') - required.add_argument('--dummy', type=str, required=True, - help='full path to file that will have dummy definitions of FireEtw functions') - - args, unknown = parser.parse_known_args(argv) - if unknown: - print('Unknown argument(s): ', ', '.join(unknown)) - return const.UnknownArguments - - sClrEtwAllMan = args.man - exclusionListFile = args.exc - inHeader = args.eventheader - macroHeader = args.macroheader - etmdummyHeader = args.dummy - - generateEtwFiles(sClrEtwAllMan, exclusionListFile, etmdummyHeader, macroHeader, inHeader) - -if __name__ == '__main__': - return_code = main(sys.argv[1:]) - sys.exit(return_code) diff --git a/src/scripts/utilities.py b/src/scripts/utilities.py new file mode 100644 index 0000000000..82e6c83346 --- /dev/null +++ b/src/scripts/utilities.py @@ -0,0 +1,115 @@ +## +## 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 file provides utility functions to the adjacent python scripts + +from filecmp import dircmp +from hashlib import sha256 +from io import StringIO +import shutil +import sys +import os + +class WrappedStringIO(StringIO): + """A wrapper around StringIO to allow writing str objects""" + def write(self, s): + if sys.version_info < (3, 0, 0): + if isinstance(s, str): + s = unicode(s) + super(WrappedStringIO, self).write(s) + +class UpdateFileWriter: + """A file-like context object which will only write to a file if the result would be different + + Attributes: + filename (str): The name of the file to update + stream (WrappedStringIO): The file-like stream provided upon context enter + + Args: + filename (str): Sets the filename attribute + """ + filemode = 'w' + + def __init__(self, filename): + self.filename = filename + self.stream = None + + def __enter__(self): + self.stream = WrappedStringIO() + return self.stream + + def __exit__(self, exc_type, exc_value, traceback): + if exc_value is None: + new_content = self.stream.getvalue() + new_hash = sha256() + cur_hash = sha256() + + try: + with open(self.filename, 'r') as fstream: + cur_hash.update(fstream.read().encode('utf-8')) + file_found = True + except IOError: + file_found = False + + if file_found: + new_hash.update(new_content.encode('utf-8')) + update = new_hash.digest() != cur_hash.digest() + else: + update = True + + if update: + with open(self.filename, 'w') as fstream: + fstream.write(new_content) + + self.stream.close() + +def open_for_update(filename): + return UpdateFileWriter(filename) + +def walk_recursively_and_update(dcmp): + #for different Files Copy from right to left + for name in dcmp.diff_files: + srcpath = dcmp.right + "/" + name + destpath = dcmp.left + "/" + name + print("Updating %s" % (destpath)) + if os.path.isfile(srcpath): + shutil.copyfile(srcpath, destpath) + else : + raise Exception("path: " + srcpath + "is neither a file or folder") + + #copy right only files + for name in dcmp.right_only: + srcpath = dcmp.right + "/" + name + destpath = dcmp.left + "/" + name + print("Updating %s" % (destpath)) + if os.path.isfile(srcpath): + shutil.copyfile(srcpath, destpath) + elif os.path.isdir(srcpath): + shutil.copytree(srcpath, destpath) + else : + raise Exception("path: " + srcpath + "is neither a file or folder") + + #delete left only files + for name in dcmp.left_only: + path = dcmp.left + "/" + name + print("Deleting %s" % (path)) + if os.path.isfile(path): + os.remove(path) + elif os.path.isdir(path): + shutil.rmtree(path) + else : + raise Exception("path: " + path + "is neither a file or folder") + + #call recursively + for sub_dcmp in dcmp.subdirs.values(): + walk_recursively_and_update(sub_dcmp) + +def UpdateDirectory(destpath,srcpath): + + print("Updating %s with %s" % (destpath,srcpath)) + if not os.path.exists(destpath): + os.makedirs(destpath) + dcmp = dircmp(destpath,srcpath) + walk_recursively_and_update(dcmp) diff --git a/src/vm/CMakeLists.txt b/src/vm/CMakeLists.txt index f49d01bc91..97ab656f81 100644 --- a/src/vm/CMakeLists.txt +++ b/src/vm/CMakeLists.txt @@ -1,7 +1,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) # Needed due to the cmunged files being in the binary folders, the set(CMAKE_INCLUDE_CURRENT_DIR ON) is not enough -include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}) include_directories(${ARCH_SOURCES_DIR}) add_definitions(-DUNICODE) @@ -9,7 +9,7 @@ add_definitions(-D_UNICODE) if(CMAKE_CONFIGURATION_TYPES) # multi-configuration generator? - foreach (Config DEBUG CHECKED) + foreach (Config DEBUG CHECKED) set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS $<$<CONFIG:${Config}>:WRITE_BARRIER_CHECK=1>) endforeach (Config) else() @@ -291,7 +291,7 @@ set(VM_SOURCES_DAC_AND_WKS_WIN32 winrttypenameconverter.cpp ) -list(APPEND VM_SOURCES_WKS +list(APPEND VM_SOURCES_WKS ${VM_SOURCES_DAC_AND_WKS_WIN32} # These should not be included for Linux appxutil.cpp @@ -313,17 +313,17 @@ list(APPEND VM_SOURCES_WKS extensibleclassfactory.cpp mngstdinterfaces.cpp notifyexternals.cpp - olecontexthelpers.cpp + olecontexthelpers.cpp rcwrefcache.cpp rtlfunctions.cpp runtimecallablewrapper.cpp stacksampler.cpp stdinterfaces.cpp stdinterfaces_wrapper.cpp - winrthelpers.cpp + winrthelpers.cpp ) -list(APPEND VM_SOURCES_DAC +list(APPEND VM_SOURCES_DAC ${VM_SOURCES_DAC_AND_WKS_WIN32} # These should not be included for Linux clrprivbinderwinrt.cpp @@ -423,7 +423,7 @@ else(WIN32) ${ARCH_SOURCES_DIR}/pinvokestubs.S ) endif() - + endif(WIN32) @@ -434,7 +434,7 @@ if(CLR_CMAKE_TARGET_ARCH_AMD64) ${ARCH_SOURCES_DIR}/gmsamd64.cpp ${ARCH_SOURCES_DIR}/stublinkeramd64.cpp ) - + set(VM_SOURCES_WKS_ARCH ${ARCH_SOURCES_DIR}/jithelpersamd64.cpp ${ARCH_SOURCES_DIR}/jitinterfaceamd64.cpp @@ -452,7 +452,7 @@ elseif(CLR_CMAKE_TARGET_ARCH_I386) ${ARCH_SOURCES_DIR}/gmsx86.cpp ${ARCH_SOURCES_DIR}/stublinkerx86.cpp ) - + set(VM_SOURCES_WKS_ARCH ${ARCH_SOURCES_DIR}/jitinterfacex86.cpp ${ARCH_SOURCES_DIR}/profiler.cpp @@ -465,7 +465,7 @@ elseif(CLR_CMAKE_TARGET_ARCH_ARM) ${ARCH_SOURCES_DIR}/stubs.cpp ${ARCH_SOURCES_DIR}/armsinglestepper.cpp ) - + set(VM_SOURCES_WKS_ARCH ${ARCH_SOURCES_DIR}/jithelpersarm.cpp ${ARCH_SOURCES_DIR}/profiler.cpp @@ -492,7 +492,7 @@ set(VM_SOURCES_DAC_ARCH exceptionhandling.cpp ) -list(APPEND VM_SOURCES_WKS +list(APPEND VM_SOURCES_WKS ${VM_SOURCES_WKS_ARCH} ${VM_SOURCES_DAC_AND_WKS_ARCH} ) @@ -519,6 +519,6 @@ convert_to_absolute_path(VM_SOURCES_DAC ${VM_SOURCES_DAC}) add_subdirectory(dac) add_subdirectory(wks) -if(CLR_CMAKE_PLATFORM_LINUX) - add_subdirectory($ENV{__IntermediatesDir}/Generated/eventpipe ${CMAKE_CURRENT_BINARY_DIR}/eventpipe) -endif(CLR_CMAKE_PLATFORM_LINUX) +if(FEATURE_PERFTRACING) + add_subdirectory(${GENERATED_EVENTING_DIR}/eventpipe ${CMAKE_CURRENT_BINARY_DIR}/eventpipe) +endif(FEATURE_PERFTRACING)
\ No newline at end of file diff --git a/src/vm/eventpipe.cpp b/src/vm/eventpipe.cpp index 8f2e8ff937..37de4c3b36 100644 --- a/src/vm/eventpipe.cpp +++ b/src/vm/eventpipe.cpp @@ -2,9 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#include "common.h" #include "clrtypes.h" #include "safemath.h" -#include "common.h" #include "eventpipe.h" #include "eventpipebuffermanager.h" #include "eventpipeconfiguration.h" @@ -33,11 +33,8 @@ EventPipeJsonFile* EventPipe::s_pJsonFile = NULL; #ifdef FEATURE_PAL // This function is auto-generated from /src/scripts/genEventPipe.py extern "C" void InitProvidersAndEvents(); -#endif - -#ifdef FEATURE_PAL -// This function is auto-generated from /src/scripts/genEventPipe.py -extern "C" void InitProvidersAndEvents(); +#else +void InitProvidersAndEvents(); #endif EventPipeEventPayload::EventPipeEventPayload(BYTE *pData, unsigned int length) @@ -97,7 +94,7 @@ EventPipeEventPayload::~EventPipeEventPayload() CONTRACTL { NOTHROW; - GC_TRIGGERS; + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; @@ -114,7 +111,7 @@ void EventPipeEventPayload::Flatten() CONTRACTL { NOTHROW; - GC_TRIGGERS; + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; @@ -168,7 +165,7 @@ BYTE* EventPipeEventPayload::GetFlatData() CONTRACTL { NOTHROW; - GC_TRIGGERS; + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; @@ -193,11 +190,9 @@ void EventPipe::Initialize() s_pBufferManager = new EventPipeBufferManager(); -#ifdef FEATURE_PAL // This calls into auto-generated code to initialize the runtime providers // and events so that the EventPipe configuration lock isn't taken at runtime InitProvidersAndEvents(); -#endif } void EventPipe::EnableOnStartup() @@ -227,13 +222,19 @@ void EventPipe::Shutdown() { CONTRACTL { - THROWS; + NOTHROW; GC_TRIGGERS; MODE_ANY; } CONTRACTL_END; - Disable(); + // We are shutting down, so if diasabling EventPipe throws, we need to move along anyway + EX_TRY + { + Disable(); + } + EX_CATCH { } + EX_END_CATCH(SwallowAllExceptions); if(s_pConfig != NULL) { @@ -262,7 +263,13 @@ void EventPipe::Enable( CONTRACTL_END; // If tracing is not initialized or is already enabled, bail here. - if(!s_tracingInitialized || s_pConfig->Enabled()) + if(!s_tracingInitialized || s_pConfig == NULL || s_pConfig->Enabled()) + { + return; + } + + // If the state or aurguments are invalid, bail + if(pProviders == NULL || numProviders <= 0) { return; } @@ -312,7 +319,7 @@ void EventPipe::Disable() // Take the lock before disabling tracing. CrstHolder _crst(GetLock()); - if(s_pConfig->Enabled()) + if(s_pConfig != NULL && s_pConfig->Enabled()) { // Disable the profiler. SampleProfiler::Disable(); @@ -468,7 +475,6 @@ void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload NOTHROW; GC_NOTRIGGER; MODE_ANY; - PRECONDITION(s_pBufferManager != NULL); } CONTRACTL_END; @@ -486,6 +492,12 @@ void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload return; } + if(s_pConfig == NULL) + { + // We can't procede without a configuration + return; + } + if(!s_pConfig->RundownEnabled() && s_pBufferManager != NULL) { if(!s_pBufferManager->WriteEvent(pThread, event, payload, pActivityId, pRelatedActivityId)) @@ -501,6 +513,10 @@ void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload { // Write synchronously to the file. // We're under lock and blocking the disabling thread. + // This copy occurs here (rather than at file write) because + // A) The FastSerializer API would need to change if we waited + // B) It is unclear there is a benefit to multiple file write calls + // as opposed a a buffer copy here EventPipeEventInstance instance( event, pThread->GetOSThreadId(), @@ -511,12 +527,22 @@ void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload if(s_pFile != NULL) { - s_pFile->WriteEvent(instance); + // EventPipeFile::WriteEvent needs to allocate a metadata event + // and can therefore throw. In this context we will silently + // fail rather than disrupt the caller + EX_TRY + { + s_pFile->WriteEvent(instance); + } + EX_CATCH { } + EX_END_CATCH(SwallowAllExceptions); } } } -#ifdef _DEBUG +// This section requires a call to GCX_PREEMP which violates the GC_NOTRIGGER contract +// It should only be enabled when debugging this specific component and contracts are off +#ifdef DEBUG_JSON_EVENT_FILE { GCX_PREEMP(); @@ -537,7 +563,7 @@ void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload { s_pSyncFile->WriteEvent(instance); } - + // Write to the EventPipeJsonFile if it exists. if(s_pJsonFile != NULL) { @@ -545,7 +571,7 @@ void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload } } } -#endif // _DEBUG +#endif // DEBUG_JSON_EVENT_FILE } void EventPipe::WriteSampleProfileEvent(Thread *pSamplingThread, EventPipeEvent *pEvent, Thread *pTargetThread, StackContents &stackContents, BYTE *pData, unsigned int length) @@ -641,7 +667,7 @@ StackWalkAction EventPipe::StackWalkCallback(CrawlFrame *pCf, StackContents *pDa { NOTHROW; GC_NOTRIGGER; - MODE_PREEMPTIVE; + MODE_ANY; PRECONDITION(pCf != NULL); PRECONDITION(pData != NULL); } @@ -686,15 +712,15 @@ CrstStatic* EventPipe::GetLock() void QCALLTYPE EventPipeInternal::Enable( __in_z LPCWSTR outputFile, - unsigned int circularBufferSizeInMB, - long profilerSamplingRateInNanoseconds, + UINT32 circularBufferSizeInMB, + INT64 profilerSamplingRateInNanoseconds, EventPipeProviderConfiguration *pProviders, - int numProviders) + INT32 numProviders) { QCALL_CONTRACT; BEGIN_QCALL; - SampleProfiler::SetSamplingRate(profilerSamplingRateInNanoseconds); + SampleProfiler::SetSamplingRate((unsigned long)profilerSamplingRateInNanoseconds); EventPipe::Enable(outputFile, circularBufferSizeInMB, pProviders, numProviders); END_QCALL; } @@ -727,12 +753,12 @@ INT_PTR QCALLTYPE EventPipeInternal::CreateProvider( INT_PTR QCALLTYPE EventPipeInternal::DefineEvent( INT_PTR provHandle, - unsigned int eventID, + UINT32 eventID, __int64 keywords, - unsigned int eventVersion, - unsigned int level, + UINT32 eventVersion, + UINT32 level, void *pMetadata, - unsigned int metadataLength) + UINT32 metadataLength) { QCALL_CONTRACT; @@ -768,9 +794,9 @@ void QCALLTYPE EventPipeInternal::DeleteProvider( void QCALLTYPE EventPipeInternal::WriteEvent( INT_PTR eventHandle, - unsigned int eventID, + UINT32 eventID, void *pData, - unsigned int length, + UINT32 length, LPCGUID pActivityId, LPCGUID pRelatedActivityId) { @@ -786,9 +812,9 @@ void QCALLTYPE EventPipeInternal::WriteEvent( void QCALLTYPE EventPipeInternal::WriteEventData( INT_PTR eventHandle, - unsigned int eventID, + UINT32 eventID, EventData **pEventData, - unsigned int eventDataCount, + UINT32 eventDataCount, LPCGUID pActivityId, LPCGUID pRelatedActivityId) { diff --git a/src/vm/eventpipe.h b/src/vm/eventpipe.h index bac7be6ac8..4fd3d88127 100644 --- a/src/vm/eventpipe.h +++ b/src/vm/eventpipe.h @@ -6,8 +6,10 @@ #define __EVENTPIPE_H__ #ifdef FEATURE_PERFTRACING +#include "common.h" class CrstStatic; +class CrawlFrame; class EventPipeConfiguration; class EventPipeEvent; class EventPipeFile; @@ -58,7 +60,7 @@ public: // If a buffer was allocated internally, delete it ~EventPipeEventPayload(); - + // Copy the data (whether flat or array of objects) into a flat buffer at pDst // Assumes that pDst points to an appropriatly sized buffer void CopyData(BYTE *pDst); @@ -259,7 +261,7 @@ class EventPipe // Write out a sample profile event. static void WriteSampleProfileEvent(Thread *pSamplingThread, EventPipeEvent *pEvent, Thread *pTargetThread, StackContents &stackContents, BYTE *pData = NULL, unsigned int length = 0); - + // Get the managed call stack for the current thread. static bool WalkManagedStackForCurrentThread(StackContents &stackContents); @@ -301,7 +303,7 @@ private: LPCWSTR m_pProviderName; UINT64 m_keywords; - unsigned int m_loggingLevel; + UINT32 m_loggingLevel; public: @@ -316,7 +318,7 @@ public: EventPipeProviderConfiguration( LPCWSTR pProviderName, UINT64 keywords, - unsigned int loggingLevel) + UINT32 loggingLevel) { LIMITED_METHOD_CONTRACT; m_pProviderName = pProviderName; @@ -336,7 +338,7 @@ public: return m_keywords; } - unsigned int GetLevel() const + UINT32 GetLevel() const { LIMITED_METHOD_CONTRACT; return m_loggingLevel; @@ -350,10 +352,10 @@ public: static void QCALLTYPE Enable( __in_z LPCWSTR outputFile, - unsigned int circularBufferSizeInMB, - long profilerSamplingRateInNanoseconds, + UINT32 circularBufferSizeInMB, + INT64 profilerSamplingRateInNanoseconds, EventPipeProviderConfiguration *pProviders, - int numProviders); + INT32 numProviders); static void QCALLTYPE Disable(); @@ -363,28 +365,28 @@ public: static INT_PTR QCALLTYPE DefineEvent( INT_PTR provHandle, - unsigned int eventID, + UINT32 eventID, __int64 keywords, - unsigned int eventVersion, - unsigned int level, + UINT32 eventVersion, + UINT32 level, void *pMetadata, - unsigned int metadataLength); + UINT32 metadataLength); static void QCALLTYPE DeleteProvider( INT_PTR provHandle); static void QCALLTYPE WriteEvent( INT_PTR eventHandle, - unsigned int eventID, + UINT32 eventID, void *pData, - unsigned int length, + UINT32 length, LPCGUID pActivityId, LPCGUID pRelatedActivityId); static void QCALLTYPE WriteEventData( INT_PTR eventHandle, - unsigned int eventID, + UINT32 eventID, EventData **pEventData, - unsigned int eventDataCount, + UINT32 eventDataCount, LPCGUID pActivityId, LPCGUID pRelatedActivityId); }; diff --git a/src/vm/eventpipebuffer.cpp b/src/vm/eventpipebuffer.cpp index 80b4a4f1b7..407c875fa6 100644 --- a/src/vm/eventpipebuffer.cpp +++ b/src/vm/eventpipebuffer.cpp @@ -35,7 +35,7 @@ EventPipeBuffer::~EventPipeBuffer() { CONTRACTL { - THROWS; + NOTHROW; GC_NOTRIGGER; MODE_ANY; } @@ -52,7 +52,7 @@ bool EventPipeBuffer::WriteEvent(Thread *pThread, EventPipeEvent &event, EventPi CONTRACTL { NOTHROW; - GC_TRIGGERS; + GC_NOTRIGGER; MODE_ANY; PRECONDITION(pThread != NULL); } @@ -143,7 +143,7 @@ EventPipeEventInstance* EventPipeBuffer::GetNext(EventPipeEventInstance *pEvent, CONTRACTL { NOTHROW; - GC_TRIGGERS; + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; diff --git a/src/vm/eventpipebuffermanager.cpp b/src/vm/eventpipebuffermanager.cpp index e7d97d5732..045e1d999a 100644 --- a/src/vm/eventpipebuffermanager.cpp +++ b/src/vm/eventpipebuffermanager.cpp @@ -37,7 +37,7 @@ EventPipeBufferManager::~EventPipeBufferManager() { CONTRACTL { - THROWS; + NOTHROW; GC_TRIGGERS; MODE_ANY; } @@ -80,7 +80,7 @@ EventPipeBuffer* EventPipeBufferManager::AllocateBufferForThread(Thread *pThread { CONTRACTL { - THROWS; + NOTHROW; GC_NOTRIGGER; MODE_ANY; PRECONDITION(pThread != NULL); @@ -97,8 +97,19 @@ EventPipeBuffer* EventPipeBufferManager::AllocateBufferForThread(Thread *pThread EventPipeBufferList *pThreadBufferList = pThread->GetEventPipeBufferList(); if(pThreadBufferList == NULL) { - pThreadBufferList = new EventPipeBufferList(this); - m_pPerThreadBufferList->InsertTail(new SListElem<EventPipeBufferList*>(pThreadBufferList)); + pThreadBufferList = new (nothrow) EventPipeBufferList(this); + if (pThreadBufferList == NULL) + { + return NULL; + } + + SListElem<EventPipeBufferList*> *pElem = new (nothrow) SListElem<EventPipeBufferList*>(pThreadBufferList); + if (pElem == NULL) + { + return NULL; + } + + m_pPerThreadBufferList->InsertTail(pElem); pThread->SetEventPipeBufferList(pThreadBufferList); allocateNewBuffer = true; } @@ -181,7 +192,24 @@ EventPipeBuffer* EventPipeBufferManager::AllocateBufferForThread(Thread *pThread bufferSize = requestSize; } - pNewBuffer = new EventPipeBuffer(bufferSize); + // EX_TRY is used here as opposed to new (nothrow) because + // the constructor also allocates a private buffer, which + // could throw, and cannot be easily checked + EX_TRY + { + pNewBuffer = new EventPipeBuffer(bufferSize); + } + EX_CATCH + { + pNewBuffer = NULL; + } + EX_END_CATCH(SwallowAllExceptions); + + if (pNewBuffer == NULL) + { + return NULL; + } + m_sizeOfAllBuffers += bufferSize; #ifdef _DEBUG m_numBuffersAllocated++; @@ -202,7 +230,7 @@ EventPipeBufferList* EventPipeBufferManager::FindThreadToStealFrom() { CONTRACTL { - THROWS; + NOTHROW; GC_NOTRIGGER; MODE_ANY; PRECONDITION(m_lock.OwnedByCurrentThread()); @@ -362,7 +390,7 @@ void EventPipeBufferManager::WriteAllBuffersToFile(EventPipeFile *pFile, LARGE_I { CONTRACTL { - NOTHROW; + THROWS; GC_NOTRIGGER; MODE_ANY; PRECONDITION(pFile != NULL); diff --git a/src/vm/eventpipeconfiguration.cpp b/src/vm/eventpipeconfiguration.cpp index ae1dd4e099..a74bdbc28f 100644 --- a/src/vm/eventpipeconfiguration.cpp +++ b/src/vm/eventpipeconfiguration.cpp @@ -20,6 +20,7 @@ EventPipeConfiguration::EventPipeConfiguration() m_rundownEnabled = false; m_circularBufferSizeInBytes = 1024 * 1024 * 1000; // Default to 1000MB. m_pEnabledProviderList = NULL; + m_pConfigProvider = NULL; m_pProviderList = new SList<SListElem<EventPipeProvider*>>(); } @@ -27,7 +28,7 @@ EventPipeConfiguration::~EventPipeConfiguration() { CONTRACTL { - THROWS; + NOTHROW; GC_TRIGGERS; MODE_ANY; } @@ -35,8 +36,15 @@ EventPipeConfiguration::~EventPipeConfiguration() if(m_pConfigProvider != NULL) { - delete(m_pConfigProvider); - m_pConfigProvider = NULL; + // This unregisters the provider, which takes a + // HOST_BREAKABLE lock + EX_TRY + { + DeleteProvider(m_pConfigProvider); + m_pConfigProvider = NULL; + } + EX_CATCH { } + EX_END_CATCH(SwallowAllExceptions); } if(m_pEnabledProviderList != NULL) @@ -47,19 +55,28 @@ EventPipeConfiguration::~EventPipeConfiguration() if(m_pProviderList != NULL) { - // Take the lock before manipulating the provider list. - CrstHolder _crst(EventPipe::GetLock()); - - SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead(); - while(pElem != NULL) + // We swallow exceptions here because the HOST_BREAKABLE + // lock may throw and this destructor gets called in throw + // intolerant places. If that happens the provider list will leak + EX_TRY { - // We don't delete provider itself because it can be in-use - SListElem<EventPipeProvider*> *pCurElem = pElem; - pElem = m_pProviderList->GetNext(pElem); - delete(pCurElem); + // Take the lock before manipulating the list. + CrstHolder _crst(EventPipe::GetLock()); + + SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead(); + while(pElem != NULL) + { + // We don't delete provider itself because it can be in-use + SListElem<EventPipeProvider*> *pCurElem = pElem; + pElem = m_pProviderList->GetNext(pElem); + delete(pCurElem); + } + + delete(m_pProviderList); } + EX_CATCH { } + EX_END_CATCH(SwallowAllExceptions); - delete(m_pProviderList); m_pProviderList = NULL; } } @@ -110,7 +127,7 @@ void EventPipeConfiguration::DeleteProvider(EventPipeProvider *pProvider) CONTRACTL { THROWS; - GC_NOTRIGGER; + GC_TRIGGERS; MODE_ANY; PRECONDITION(pProvider != NULL); } @@ -177,7 +194,7 @@ bool EventPipeConfiguration::UnregisterProvider(EventPipeProvider &provider) CONTRACTL { THROWS; - GC_NOTRIGGER; + GC_TRIGGERS; MODE_ANY; } CONTRACTL_END; @@ -271,7 +288,7 @@ size_t EventPipeConfiguration::GetCircularBufferSize() const void EventPipeConfiguration::SetCircularBufferSize(size_t circularBufferSize) { LIMITED_METHOD_CONTRACT; - + if(!m_enabled) { m_circularBufferSizeInBytes = circularBufferSize; @@ -484,9 +501,7 @@ void EventPipeConfiguration::DeleteDeferredProviders() pElem = m_pProviderList->GetNext(pElem); if(pProvider->GetDeleteDeferred()) { - // The act of deleting the provider unregisters it, - // removes it from the list, and deletes the list element - delete(pProvider); + DeleteProvider(pProvider); } } } @@ -525,7 +540,7 @@ EventPipeEnabledProviderList::EventPipeEnabledProviderList( } m_pProviders = new EventPipeEnabledProvider[m_numProviders]; - for(int i=0; i<m_numProviders; i++) + for(unsigned int i=0; i<m_numProviders; i++) { m_pProviders[i].Set( pConfigs[i].GetProviderName(), @@ -538,7 +553,7 @@ EventPipeEnabledProviderList::~EventPipeEnabledProviderList() { CONTRACTL { - THROWS; + NOTHROW; GC_NOTRIGGER; MODE_ANY; } @@ -582,7 +597,7 @@ EventPipeEnabledProvider* EventPipeEnabledProviderList::GetEnabledProvider( LPCWSTR providerName = providerNameStr.GetUnicode(); EventPipeEnabledProvider *pEnabledProvider = NULL; - for(int i=0; i<m_numProviders; i++) + for(unsigned int i=0; i<m_numProviders; i++) { EventPipeEnabledProvider *pCandidate = &m_pProviders[i]; if(pCandidate != NULL) @@ -609,7 +624,7 @@ EventPipeEnabledProvider::~EventPipeEnabledProvider() { CONTRACTL { - THROWS; + NOTHROW; GC_NOTRIGGER; MODE_ANY; } @@ -640,7 +655,7 @@ void EventPipeEnabledProvider::Set(LPCWSTR providerName, UINT64 keywords, EventP if(providerName != NULL) { - unsigned int bufSize = wcslen(providerName) + 1; + size_t bufSize = wcslen(providerName) + 1; m_pProviderName = new WCHAR[bufSize]; wcscpy_s(m_pProviderName, bufSize, providerName); } diff --git a/src/vm/eventpipeevent.cpp b/src/vm/eventpipeevent.cpp index abf942b253..1b2d4ebe40 100644 --- a/src/vm/eventpipeevent.cpp +++ b/src/vm/eventpipeevent.cpp @@ -40,6 +40,14 @@ EventPipeEvent::EventPipeEvent(EventPipeProvider &provider, INT64 keywords, unsi EventPipeEvent::~EventPipeEvent() { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + if (m_pMetadata != NULL) { delete[] m_pMetadata; diff --git a/src/vm/eventpipeeventinstance.cpp b/src/vm/eventpipeeventinstance.cpp index 305b6dac04..6cb7438639 100644 --- a/src/vm/eventpipeeventinstance.cpp +++ b/src/vm/eventpipeeventinstance.cpp @@ -20,8 +20,8 @@ EventPipeEventInstance::EventPipeEventInstance( { CONTRACTL { - THROWS; - GC_TRIGGERS; + NOTHROW; + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; @@ -102,8 +102,8 @@ void EventPipeEventInstance::FastSerialize(FastSerializer *pSerializer, StreamLa { CONTRACTL { - THROWS; - GC_TRIGGERS; + NOTHROW; + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; diff --git a/src/vm/eventpipefile.cpp b/src/vm/eventpipefile.cpp index 26e04480ee..ba4eb39b29 100644 --- a/src/vm/eventpipefile.cpp +++ b/src/vm/eventpipefile.cpp @@ -62,7 +62,7 @@ EventPipeFile::~EventPipeFile() { CONTRACTL { - THROWS; + NOTHROW; GC_TRIGGERS; MODE_ANY; } @@ -87,7 +87,7 @@ void EventPipeFile::WriteEvent(EventPipeEventInstance &instance) CONTRACTL { THROWS; - GC_TRIGGERS; + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; @@ -126,8 +126,8 @@ StreamLabel EventPipeFile::GetMetadataLabel(EventPipeEvent &event) { CONTRACTL { - THROWS; - GC_TRIGGERS; + NOTHROW; + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; @@ -147,7 +147,7 @@ void EventPipeFile::SaveMetadataLabel(EventPipeEvent &event, StreamLabel label) CONTRACTL { THROWS; - GC_TRIGGERS; + GC_NOTRIGGER; MODE_ANY; PRECONDITION(label > 0); } diff --git a/src/vm/eventpipejsonfile.cpp b/src/vm/eventpipejsonfile.cpp index 2edd6f4366..aa60df7b07 100644 --- a/src/vm/eventpipejsonfile.cpp +++ b/src/vm/eventpipejsonfile.cpp @@ -38,7 +38,7 @@ EventPipeJsonFile::~EventPipeJsonFile() { CONTRACTL { - THROWS; + NOTHROW; GC_TRIGGERS; MODE_ANY; } @@ -59,14 +59,26 @@ EventPipeJsonFile::~EventPipeJsonFile() void EventPipeJsonFile::WriteEvent(EventPipeEventInstance &instance) { - STANDARD_VM_CONTRACT; + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; instance.SerializeToJsonFile(this); } void EventPipeJsonFile::WriteEvent(LARGE_INTEGER timeStamp, DWORD threadID, SString &message, StackContents &stackContents) { - STANDARD_VM_CONTRACT; + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; if(m_pFileStream == NULL || m_writeErrorEncountered) { @@ -98,7 +110,7 @@ void EventPipeJsonFile::Write(SString &str) { CONTRACTL { - THROWS; + NOTHROW; GC_TRIGGERS; MODE_ANY; } @@ -106,19 +118,35 @@ void EventPipeJsonFile::Write(SString &str) StackScratchBuffer scratch; const char * charStr = str.GetANSI(scratch); - ULONG inCount = str.GetCount(); - ULONG outCount; - m_pFileStream->Write(charStr, inCount, &outCount); - if(inCount != outCount) + EX_TRY + { + ULONG inCount = str.GetCount(); + ULONG outCount; + + m_pFileStream->Write(charStr, inCount, &outCount); + + if(inCount != outCount) + { + m_writeErrorEncountered = true; + } + } + EX_CATCH { m_writeErrorEncountered = true; } + EX_END_CATCH(SwallowAllExceptions); } void EventPipeJsonFile::FormatCallStack(StackContents &stackContents, SString &resultStr) { - STANDARD_VM_CONTRACT; + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; StackScratchBuffer scratch; SString frameStr; diff --git a/src/vm/eventpipeprovider.cpp b/src/vm/eventpipeprovider.cpp index c10dd33638..84a90e4abb 100644 --- a/src/vm/eventpipeprovider.cpp +++ b/src/vm/eventpipeprovider.cpp @@ -24,6 +24,7 @@ EventPipeProvider::EventPipeProvider(EventPipeConfiguration *pConfig, const SStr m_providerName = providerName; m_enabled = false; + m_deleteDeferred = false; m_keywords = 0; m_providerLevel = EventPipeEventLevel::Critical; m_pEventList = new SList<SListElem<EventPipeEvent*>>(); @@ -36,8 +37,8 @@ EventPipeProvider::~EventPipeProvider() { CONTRACTL { - THROWS; - GC_NOTRIGGER; + NOTHROW; + GC_TRIGGERS; MODE_ANY; } CONTRACTL_END; @@ -45,21 +46,30 @@ EventPipeProvider::~EventPipeProvider() // Free all of the events. if(m_pEventList != NULL) { - // Take the lock before manipulating the list. - CrstHolder _crst(EventPipe::GetLock()); - - SListElem<EventPipeEvent*> *pElem = m_pEventList->GetHead(); - while(pElem != NULL) + // We swallow exceptions here because the HOST_BREAKABLE + // lock may throw and this destructor gets called in throw + // intolerant places. If that happens the event list will leak + EX_TRY { - EventPipeEvent *pEvent = pElem->GetValue(); - delete pEvent; + // Take the lock before manipulating the list. + CrstHolder _crst(EventPipe::GetLock()); + + SListElem<EventPipeEvent*> *pElem = m_pEventList->GetHead(); + while(pElem != NULL) + { + EventPipeEvent *pEvent = pElem->GetValue(); + delete pEvent; + + SListElem<EventPipeEvent*> *pCurElem = pElem; + pElem = m_pEventList->GetNext(pElem); + delete pCurElem; + } - SListElem<EventPipeEvent*> *pCurElem = pElem; - pElem = m_pEventList->GetNext(pElem); - delete pCurElem; + delete m_pEventList; } + EX_CATCH { } + EX_END_CATCH(SwallowAllExceptions); - delete m_pEventList; m_pEventList = NULL; } } diff --git a/src/vm/eventpipeprovider.h b/src/vm/eventpipeprovider.h index 405ce32154..0ffe46f887 100644 --- a/src/vm/eventpipeprovider.h +++ b/src/vm/eventpipeprovider.h @@ -31,9 +31,6 @@ class EventPipeProvider friend class SampleProfiler; private: - // The GUID of the provider. - GUID m_providerID; - // The name of the provider. SString m_providerName; diff --git a/src/vm/fastserializer.cpp b/src/vm/fastserializer.cpp index 8e0e0ad768..66fce30793 100644 --- a/src/vm/fastserializer.cpp +++ b/src/vm/fastserializer.cpp @@ -40,7 +40,7 @@ FastSerializer::~FastSerializer() { CONTRACTL { - THROWS; + NOTHROW; GC_TRIGGERS; MODE_ANY; } @@ -98,7 +98,7 @@ void FastSerializer::WriteBuffer(BYTE *pBuffer, unsigned int length) CONTRACTL { NOTHROW; - GC_TRIGGERS; + GC_NOTRIGGER; MODE_PREEMPTIVE; PRECONDITION(pBuffer != NULL); PRECONDITION(length > 0); @@ -263,7 +263,7 @@ void FastSerializer::WriteFileHeader() { CONTRACTL { - THROWS; + NOTHROW; GC_TRIGGERS; MODE_ANY; } @@ -278,7 +278,7 @@ void FastSerializer::WriteString(const char *strContents, unsigned int length) { CONTRACTL { - THROWS; + NOTHROW; GC_TRIGGERS; MODE_PREEMPTIVE; } @@ -295,7 +295,7 @@ StreamLabel FastSerializer::WriteForwardReferenceTable() { CONTRACTL { - THROWS; + NOTHROW; GC_TRIGGERS; MODE_PREEMPTIVE; } @@ -317,7 +317,7 @@ void FastSerializer::WriteTrailer(StreamLabel forwardReferencesTableStart) { CONTRACTL { - THROWS; + NOTHROW; GC_TRIGGERS; MODE_PREEMPTIVE; } diff --git a/src/vm/mscorlib.cpp b/src/vm/mscorlib.cpp index 3e2d478bbf..75c1f058f0 100644 --- a/src/vm/mscorlib.cpp +++ b/src/vm/mscorlib.cpp @@ -96,6 +96,10 @@ #include "eventpipe.h" #endif //defined(FEATURE_EVENTSOURCE_XPLAT) +#ifdef FEATURE_PERFTRACING +#include "eventpipe.h" +#endif //FEATURE_PERFTRACING + #endif // CROSSGEN_MSCORLIB diff --git a/src/vm/sampleprofiler.cpp b/src/vm/sampleprofiler.cpp index d99667eea2..ade21669c4 100644 --- a/src/vm/sampleprofiler.cpp +++ b/src/vm/sampleprofiler.cpp @@ -11,6 +11,13 @@ #ifdef FEATURE_PERFTRACING +#ifndef FEATURE_PAL +#include <mmsystem.h> +#endif //FEATURE_PAL + +// To avoid counting zeros in conversions +#define MILLION * 1000000 + Volatile<BOOL> SampleProfiler::s_profilingEnabled = false; Thread* SampleProfiler::s_pSamplingThread = NULL; const WCHAR* SampleProfiler::s_providerName = W("Microsoft-DotNETCore-SampleProfiler"); @@ -19,7 +26,16 @@ EventPipeEvent* SampleProfiler::s_pThreadTimeEvent = NULL; BYTE* SampleProfiler::s_pPayloadExternal = NULL; BYTE* SampleProfiler::s_pPayloadManaged = NULL; CLREventStatic SampleProfiler::s_threadShutdownEvent; -long SampleProfiler::s_samplingRateInNs = 1000000; // 1ms +unsigned long SampleProfiler::s_samplingRateInNs = 1 MILLION; // 1ms +bool SampleProfiler::s_timePeriodIsSet = FALSE; + +#ifndef FEATURE_PAL +PVOID SampleProfiler::s_timeBeginPeriodFn = NULL; +PVOID SampleProfiler::s_timeEndPeriodFn = NULL; +HINSTANCE SampleProfiler::s_hMultimediaLib = NULL; + +typedef MMRESULT (WINAPI *TimePeriodFnPtr) (UINT uPeriod); +#endif //FEATURE_PAL void SampleProfiler::Enable() { @@ -33,6 +49,8 @@ void SampleProfiler::Enable() PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread()); } CONTRACTL_END; + + LoadDependencies(); if(s_pEventPipeProvider == NULL) { @@ -72,6 +90,10 @@ void SampleProfiler::Enable() { _ASSERT(!"Unable to create sample profiler thread."); } + + s_threadShutdownEvent.CreateManualEvent(FALSE); + + SetTimeGranularity(); } void SampleProfiler::Disable() @@ -101,12 +123,32 @@ void SampleProfiler::Disable() // Wait for the sampling thread to clean itself up. s_threadShutdownEvent.Wait(0, FALSE /* bAlertable */); + + if(s_timePeriodIsSet) + { + ResetTimeGranularity(); + } + UnloadDependencies(); } -void SampleProfiler::SetSamplingRate(long nanoseconds) +void SampleProfiler::SetSamplingRate(unsigned long nanoseconds) { LIMITED_METHOD_CONTRACT; + + // If the time period setting was modified by us, + // make sure to change it back before changing our period + // and losing track of what we set it to + if(s_timePeriodIsSet) + { + ResetTimeGranularity(); + } + s_samplingRateInNs = nanoseconds; + + if(!s_timePeriodIsSet) + { + SetTimeGranularity(); + } } DWORD WINAPI SampleProfiler::ThreadProc(void *args) @@ -132,7 +174,7 @@ DWORD WINAPI SampleProfiler::ThreadProc(void *args) if(ThreadSuspend::SysIsSuspendInProgress() || (ThreadSuspend::GetSuspensionThread() != 0)) { // Skip the current sample. - PAL_nanosleep(s_samplingRateInNs); + PlatformSleep(s_samplingRateInNs); continue; } @@ -146,7 +188,7 @@ DWORD WINAPI SampleProfiler::ThreadProc(void *args) ThreadSuspend::RestartEE(FALSE /* bFinishedGC */, TRUE /* SuspendSucceeded */); // Wait until it's time to sample again. - PAL_nanosleep(s_samplingRateInNs); + PlatformSleep(s_samplingRateInNs); } } @@ -156,7 +198,7 @@ DWORD WINAPI SampleProfiler::ThreadProc(void *args) // Signal Disable() that the thread has been destroyed. s_threadShutdownEvent.Set(); - + return S_OK; } @@ -201,4 +243,115 @@ void SampleProfiler::WalkManagedThreads() } } +void SampleProfiler::PlatformSleep(unsigned long nanoseconds) +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + +#ifdef FEATURE_PAL + PAL_nanosleep(nanoseconds); +#else //FEATURE_PAL + ClrSleepEx(s_samplingRateInNs / 1 MILLION, FALSE); +#endif //FEATURE_PAL +} + +void SampleProfiler::SetTimeGranularity() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + +#ifndef FEATURE_PAL + // Attempt to set the systems minimum timer period to the sampling rate + // If the sampling rate is lower than the current system setting (16ms by default), + // this will cause the OS to wake more often for scheduling descsion, allowing us to take samples + // Note that is effects a system-wide setting and when set low will increase the amount of time + // the OS is on-CPU, decreasing overall system performance and increasing power consumption + if(s_timeBeginPeriodFn != NULL) + { + if(((TimePeriodFnPtr) s_timeBeginPeriodFn)(s_samplingRateInNs / 1 MILLION) == TIMERR_NOERROR) + { + s_timePeriodIsSet = TRUE; + } + } +#endif //FEATURE_PAL +} + +void SampleProfiler::ResetTimeGranularity() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + +#ifndef FEATURE_PAL + // End the modifications we had to the timer period in Enable + if(s_timeEndPeriodFn != NULL) + { + if(((TimePeriodFnPtr) s_timeEndPeriodFn)(s_samplingRateInNs / 1 MILLION) == TIMERR_NOERROR) + { + s_timePeriodIsSet = FALSE; + } + } +#endif //FEATURE_PAL +} + +bool SampleProfiler::LoadDependencies() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + +#ifndef FEATURE_PAL + s_hMultimediaLib = WszLoadLibrary(W("winmm.dll")); + + if (s_hMultimediaLib != NULL) + { + s_timeBeginPeriodFn = (PVOID) GetProcAddress(s_hMultimediaLib, "timeBeginPeriod"); + s_timeEndPeriodFn = (PVOID) GetProcAddress(s_hMultimediaLib, "timeEndPeriod"); + } + + return s_hMultimediaLib != NULL && s_timeBeginPeriodFn != NULL && s_timeEndPeriodFn != NULL; +#else + return FALSE; +#endif //FEATURE_PAL +} + +void SampleProfiler::UnloadDependencies() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + +#ifndef FEATURE_PAL + if (s_hMultimediaLib != NULL) + { + FreeLibrary(s_hMultimediaLib); + s_hMultimediaLib = NULL; + s_timeBeginPeriodFn = NULL; + s_timeEndPeriodFn = NULL; + } +#endif //FEATURE_PAL +} + #endif // FEATURE_PERFTRACING diff --git a/src/vm/sampleprofiler.h b/src/vm/sampleprofiler.h index 51290b4d9c..1bee70e8f6 100644 --- a/src/vm/sampleprofiler.h +++ b/src/vm/sampleprofiler.h @@ -33,7 +33,7 @@ class SampleProfiler static void Disable(); // Set the sampling rate. - static void SetSamplingRate(long nanoseconds); + static void SetSamplingRate(unsigned long nanoseconds); private: @@ -43,6 +43,31 @@ class SampleProfiler // Profiling thread proc. Invoked on a new thread when profiling is enabled. static DWORD WINAPI ThreadProc(void *args); + // Calls either PAL_nanosleep or ClrSleepEx depending on platform + // Note: Although we specify the time in ns, that is no indication + // of the actually accuracy with which we will return from sleep + // In reality Unix will have a minimum granularity of ~10ms + // and Windows has a default granularity of ~16ms, but can be + // adjusted to as low as ~1ms + // Even this however is not gaurenteed. If the system is under load + // the sampling thread may be delayed up to hundreds of ms due to + // scheduling priority. There is no way to prevent this from user threads + // Additionally we may get lucky and there will be an open CPU to run + // and under light load the timings will achieve great accuracy! + static void PlatformSleep(unsigned long nanoseconds); + + static bool LoadDependencies(); + static void UnloadDependencies(); + +#ifndef FEATURE_PAL + static HINSTANCE s_hMultimediaLib; + static PVOID s_timeBeginPeriodFn; + static PVOID s_timeEndPeriodFn; +#endif //FEATURE_PAL + + static void SetTimeGranularity(); + static void ResetTimeGranularity(); + // True when profiling is enabled. static Volatile<BOOL> s_profilingEnabled; @@ -65,7 +90,10 @@ class SampleProfiler static CLREventStatic s_threadShutdownEvent; // The sampling rate. - static long s_samplingRateInNs; + static unsigned long s_samplingRateInNs; + + // Whether or not timeBeginPeriod has been used to set the scheduler period + static bool s_timePeriodIsSet; }; #endif // FEATURE_PERFTRACING |