summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Robbins <brianrob@microsoft.com>2017-05-06 12:36:08 -0700
committerVance Morrison <vancem@microsoft.com>2017-05-06 12:36:08 -0700
commit72ac46450bec8ea88ed023d9c1faf5a04556c834 (patch)
tree59085824f4268257d041ed9870f038216cd8ed45
parent3ababc21ab334a2e37c6ba4115c946ea26a6f2fb (diff)
downloadcoreclr-72ac46450bec8ea88ed023d9c1faf5a04556c834.tar.gz
coreclr-72ac46450bec8ea88ed023d9c1faf5a04556c834.tar.bz2
coreclr-72ac46450bec8ea88ed023d9c1faf5a04556c834.zip
Log Events to EventPipe on Linux (#11433)
* Implement the EventPipe object model for providers and events. * Plumb Runtime Events into EventPipe (#11145) Plumb runtime ETW events into the EventPipe. * Fix bug where all events except for SampleProfiler events were never enabled. * Plumb EventPipeEventInstance through the EventPipe. * Implement EventPipeFile and FastSerializer. * Write event contents to the EventPipeFile. * Only build EventPipe on Linux. * Conditionally add a sentinel value marking event end. * Send SampleProfiler events to the EventPipeFile. * Fix provider ID printing to JSON file. * Write the start date/time, timestamp, and clock frequency into the trace file. * Support unloading of EventPipeProviders. * Handle failure cases when we can't walk the stack or are shutting down. * Fix a bug where we pass a null src pointer to memcpy.
-rwxr-xr-xbuild.sh32
-rw-r--r--clrdefinitions.cmake4
-rw-r--r--src/dlls/mscoree/coreclr/CMakeLists.txt6
-rw-r--r--src/scripts/genEventPipe.py500
-rw-r--r--src/scripts/genXplatEventing.py89
-rw-r--r--src/scripts/genXplatLttng.py6
-rw-r--r--src/vm/CMakeLists.txt10
-rw-r--r--src/vm/ceemain.cpp8
-rw-r--r--src/vm/eventpipe.cpp155
-rw-r--r--src/vm/eventpipe.h65
-rw-r--r--src/vm/eventpipeconfiguration.cpp271
-rw-r--r--src/vm/eventpipeconfiguration.h64
-rw-r--r--src/vm/eventpipeevent.cpp86
-rw-r--r--src/vm/eventpipeevent.h74
-rw-r--r--src/vm/eventpipeeventinstance.cpp157
-rw-r--r--src/vm/eventpipeeventinstance.h64
-rw-r--r--src/vm/eventpipefile.cpp146
-rw-r--r--src/vm/eventpipefile.h75
-rw-r--r--src/vm/eventpipejsonfile.cpp20
-rw-r--r--src/vm/eventpipejsonfile.h10
-rw-r--r--src/vm/eventpipeprovider.cpp253
-rw-r--r--src/vm/eventpipeprovider.h106
-rw-r--r--src/vm/fastserializableobject.h32
-rw-r--r--src/vm/fastserializer.cpp337
-rw-r--r--src/vm/fastserializer.h74
-rw-r--r--src/vm/sampleprofiler.cpp31
-rw-r--r--src/vm/sampleprofiler.h13
27 files changed, 2603 insertions, 85 deletions
diff --git a/build.sh b/build.sh
index 12b7b726c4..39e96dd6f9 100755
--- a/build.sh
+++ b/build.sh
@@ -154,15 +154,27 @@ generate_event_logging_sources()
# Event Logging Infrastructure
__GeneratedIntermediate="$__IntermediatesDir/Generated"
__GeneratedIntermediateEventProvider="$__GeneratedIntermediate/eventprovider_new"
+ __GeneratedIntermediateEventPipe="$__GeneratedIntermediate/eventpipe_new"
+
if [[ -d "$__GeneratedIntermediateEventProvider" ]]; then
rm -rf "$__GeneratedIntermediateEventProvider"
fi
+ if [[ -d "$__GeneratedIntermediateEventPipe" ]]; then
+ rm -rf "$__GeneratedIntermediateEventPipe"
+ fi
+
if [[ ! -d "$__GeneratedIntermediate/eventprovider" ]]; then
mkdir -p "$__GeneratedIntermediate/eventprovider"
fi
+ if [[ ! -d "$__GeneratedIntermediate/eventpipe" ]]; then
+ mkdir -p "$__GeneratedIntermediate/eventpipe"
+ fi
+
mkdir -p "$__GeneratedIntermediateEventProvider"
+ mkdir -p "$__GeneratedIntermediateEventPipe"
+
if [[ $__SkipCoreCLR == 0 || $__ConfigureOnly == 1 ]]; then
echo "Laying out dynamically generated files consumed by the build system "
echo "Laying out dynamically generated Event Logging Test files"
@@ -172,6 +184,18 @@ generate_event_logging_sources()
exit
fi
+ case $__BuildOS in
+ Linux)
+ echo "Laying out dynamically generated EventPipe Implementation"
+ $PYTHON -B -Wall -Werror "$__ProjectRoot/src/scripts/genEventPipe.py" --man "$__ProjectRoot/src/vm/ClrEtwAll.man" --intermediate "$__GeneratedIntermediateEventPipe" --exc "$__ProjectRoot/src/vm/ClrEtwAllMeta.lst"
+ if [[ $? != 0 ]]; then
+ exit
+ fi
+ ;;
+ *)
+ ;;
+ esac
+
#determine the logging system
case $__BuildOS in
Linux)
@@ -193,6 +217,14 @@ generate_event_logging_sources()
fi
rm -rf "$__GeneratedIntermediateEventProvider"
+
+ echo "Cleaning the temp folder of dynamically generated EventPipe files"
+ $PYTHON -B -Wall -Werror -c "import sys;sys.path.insert(0,\"$__ProjectRoot/src/scripts\"); from Utilities import *;UpdateDirectory(\"$__GeneratedIntermediate/eventpipe\",\"$__GeneratedIntermediateEventPipe\")"
+ if [[ $? != 0 ]]; then
+ exit
+ fi
+
+ rm -rf "$__GeneratedIntermediateEventPipe"
}
build_native()
diff --git a/clrdefinitions.cmake b/clrdefinitions.cmake
index f15749e5d5..4dacae9428 100644
--- a/clrdefinitions.cmake
+++ b/clrdefinitions.cmake
@@ -111,7 +111,9 @@ endif(FEATURE_DBGIPC)
if(FEATURE_EVENT_TRACE)
add_definitions(-DFEATURE_EVENT_TRACE=1)
endif(FEATURE_EVENT_TRACE)
-add_definitions(-DFEATURE_PERFTRACING)
+if(CLR_CMAKE_PLATFORM_LINUX)
+ add_definitions(-DFEATURE_PERFTRACING)
+endif(CLR_CMAKE_PLATFORM_LINUX)
if(CLR_CMAKE_PLATFORM_UNIX)
add_definitions(-DFEATURE_EVENTSOURCE_XPLAT=1)
endif(CLR_CMAKE_PLATFORM_UNIX)
diff --git a/src/dlls/mscoree/coreclr/CMakeLists.txt b/src/dlls/mscoree/coreclr/CMakeLists.txt
index afa253f08f..7a4617fc52 100644
--- a/src/dlls/mscoree/coreclr/CMakeLists.txt
+++ b/src/dlls/mscoree/coreclr/CMakeLists.txt
@@ -141,6 +141,12 @@ if(CLR_CMAKE_PLATFORM_UNIX AND FEATURE_EVENT_TRACE)
)
endif(CLR_CMAKE_PLATFORM_UNIX AND FEATURE_EVENT_TRACE)
+if(CLR_CMAKE_PLATFORM_LINUX)
+ list(APPEND CORECLR_LIBRARIES
+ eventpipe
+ )
+endif(CLR_CMAKE_PLATFORM_LINUX)
+
target_link_libraries(coreclr ${CORECLR_LIBRARIES})
if(WIN32)
diff --git a/src/scripts/genEventPipe.py b/src/scripts/genEventPipe.py
new file mode 100644
index 0000000000..3a818ce1e0
--- /dev/null
+++ b/src/scripts/genEventPipe.py
@@ -0,0 +1,500 @@
+from __future__ import print_function
+from genXplatEventing import *
+from genXplatLttng import *
+import os
+import xml.dom.minidom as DOM
+
+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/genEventPipe.py
+
+******************************************************************/
+"""
+
+stdprolog_cmake = """#
+#
+#******************************************************************
+
+#DO NOT MODIFY. AUTOGENERATED FILE.
+#This file is generated using the logic from <root>/src/scripts/genEventPipe.py
+
+#******************************************************************
+"""
+
+
+def generateClrEventPipeWriteEventsImpl(
+ providerName, eventNodes, allTemplates, exclusionListFile):
+ providerPrettyName = providerName.replace("Windows-", '')
+ providerPrettyName = providerPrettyName.replace("Microsoft-", '')
+ providerPrettyName = providerPrettyName.replace('-', '_')
+ WriteEventImpl = []
+
+ # EventPipeEvent declaration
+ for eventNode in eventNodes:
+ eventName = eventNode.getAttribute('symbol')
+ WriteEventImpl.append(
+ "EventPipeEvent *EventPipeEvent" +
+ eventName +
+ " = nullptr;\n")
+
+ for eventNode in eventNodes:
+ eventName = eventNode.getAttribute('symbol')
+ templateName = eventNode.getAttribute('template')
+
+ # generate EventPipeEventEnabled function
+ eventEnabledImpl = """bool EventPipeEventEnabled%s()
+{
+ return EventPipeEvent%s->IsEnabled();
+}
+
+""" % (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")
+ checking = """ if (!EventPipeEventEnabled%s())
+ return ERROR_SUCCESS;
+""" % (eventName)
+
+ fnptype.append(checking)
+
+ WriteEventImpl.extend(fnptype)
+
+ if template:
+ body = generateWriteEventBody(template, providerName, eventName)
+ WriteEventImpl.append(body)
+ else:
+ WriteEventImpl.append(
+ " EventPipe::WriteEvent(*EventPipeEvent" +
+ eventName +
+ ", nullptr, 0);\n")
+
+ WriteEventImpl.append("\n return ERROR_SUCCESS;\n}\n\n")
+
+ # EventPipeProvider and EventPipeEvent initialization
+ WriteEventImpl.append(
+ "extern \"C\" void Init" +
+ providerPrettyName +
+ "()\n{\n")
+ WriteEventImpl.append(
+ " EventPipeProvider" +
+ providerPrettyName +
+ " = new EventPipeProvider(" +
+ providerPrettyName +
+ "GUID);\n")
+ for eventNode in eventNodes:
+ eventName = eventNode.getAttribute('symbol')
+ templateName = eventNode.getAttribute('template')
+ eventKeywords = eventNode.getAttribute('keywords')
+ eventKeywordsMask = generateEventKeywords(eventKeywords)
+ eventValue = eventNode.getAttribute('value')
+ eventVersion = eventNode.getAttribute('version')
+ eventLevel = eventNode.getAttribute('level')
+ eventLevel = eventLevel.replace("win:", "EventPipeEventLevel::")
+ exclusionInfo = parseExclusionList(exclusionListFile)
+ taskName = eventNode.getAttribute('task')
+ noStack = getStackWalkBit(
+ providerName,
+ taskName,
+ eventName,
+ exclusionInfo.nostack)
+
+ initEvent = """ EventPipeEvent%s = EventPipeProvider%s->AddEvent(%s,%s,%s,%s,%d);
+""" % (eventName, providerPrettyName, eventKeywordsMask, eventValue, eventVersion, eventLevel, int(noStack))
+
+ WriteEventImpl.append(initEvent)
+ WriteEventImpl.append("}")
+
+ return ''.join(WriteEventImpl)
+
+
+def generateWriteEventBody(template, providerName, eventName):
+ header = """
+ char stackBuffer[%s];
+ char *buffer = stackBuffer;
+ unsigned int offset = 0;
+ unsigned int size = %s;
+ bool fixedBuffer = true;
+
+ bool success = true;
+""" % (template.estimated_size, template.estimated_size)
+
+ fnSig = template.signature
+ pack_list = []
+ for paramName in fnSig.paramlist:
+ parameter = fnSig.getParam(paramName)
+
+ if paramName in template.structs:
+ size = "(int)%s_ElementSize * (int)%s" % (
+ paramName, parameter.prop)
+ if template.name in specialCaseSizes and paramName in specialCaseSizes[template.name]:
+ size = "(int)(%s)" % specialCaseSizes[template.name][paramName]
+ pack_list.append(
+ " success &= WriteToBuffer((const BYTE *)%s, %s, buffer, offset, size, fixedBuffer);" %
+ (paramName, size))
+ elif paramName in template.arrays:
+ size = "sizeof(%s) * (int)%s" % (
+ lttngDataTypeMapping[parameter.winType],
+ parameter.prop)
+ if template.name in specialCaseSizes and paramName in specialCaseSizes[template.name]:
+ size = "(int)(%s)" % specialCaseSizes[template.name][paramName]
+ pack_list.append(
+ " success &= WriteToBuffer((const BYTE *)%s, %s, buffer, offset, size, fixedBuffer);" %
+ (paramName, size))
+ elif parameter.winType == "win:GUID":
+ pack_list.append(
+ " success &= WriteToBuffer(*%s, buffer, offset, size, fixedBuffer);" %
+ (parameter.name,))
+ else:
+ pack_list.append(
+ " success &= WriteToBuffer(%s, buffer, offset, size, fixedBuffer);" %
+ (parameter.name,))
+
+ code = "\n".join(pack_list) + "\n\n"
+
+ checking = """ if (!success)
+ {
+ if (!fixedBuffer)
+ delete[] buffer;
+ return ERROR_WRITE_FAULT;
+ }\n\n"""
+
+ body = " EventPipe::WriteEvent(*EventPipeEvent" + \
+ eventName + ", (BYTE *)buffer, size);\n"
+
+ footer = """
+ if (!fixedBuffer)
+ delete[] buffer;
+"""
+
+ return header + code + checking + body + footer
+
+providerGUIDMap = {}
+providerGUIDMap[
+ "{e13c0d23-ccbc-4e12-931b-d9cc2eee27e4}"] = "{0xe13c0d23,0xccbc,0x4e12,{0x93,0x1b,0xd9,0xcc,0x2e,0xee,0x27,0xe4}}"
+providerGUIDMap[
+ "{A669021C-C450-4609-A035-5AF59AF4DF18}"] = "{0xA669021C,0xC450,0x4609,{0xA0,0x35,0x5A,0xF5,0x9A,0xF4,0xDF,0x18}}"
+providerGUIDMap[
+ "{CC2BCBBA-16B6-4cf3-8990-D74C2E8AF500}"] = "{0xCC2BCBBA,0x16B6,0x4cf3,{0x89,0x90,0xD7,0x4C,0x2E,0x8A,0xF5,0x00}}"
+providerGUIDMap[
+ "{763FD754-7086-4dfe-95EB-C01A46FAF4CA}"] = "{0x763FD754,0x7086,0x4dfe,{0x95,0xEB,0xC0,0x1A,0x46,0xFA,0xF4,0xCA}}"
+
+
+def generateGUID(tmpGUID):
+ return providerGUIDMap[tmpGUID]
+
+keywordMap = {}
+
+
+def generateEventKeywords(eventKeywords):
+ mask = 0
+ # split keywords if there are multiple
+ allKeywords = eventKeywords.split()
+
+ for singleKeyword in allKeywords:
+ mask = mask | keywordMap[singleKeyword]
+
+ return mask
+
+
+def generateEventPipeCmakeFile(etwmanifest, eventpipe_directory):
+ 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)
+
+ project(eventpipe)
+
+ set(CMAKE_INCLUDE_CURRENT_DIR ON)
+ include_directories(${CLR_DIR}/src/vm)
+
+ add_library(eventpipe
+ STATIC\n""")
+
+ for providerNode in tree.getElementsByTagName('provider'):
+ providerName = providerNode.getAttribute('name')
+ providerName = providerName.replace("Windows-", '')
+ providerName = providerName.replace("Microsoft-", '')
+
+ providerName_File = providerName.replace('-', '')
+ providerName_File = providerName_File.lower()
+
+ topCmake.write(' "%s.cpp"\n' % (providerName_File))
+ topCmake.write(' "eventpipehelpers.cpp"\n')
+ topCmake.write(""" )
+
+ # Install the static eventpipe library
+ install(TARGETS eventpipe DESTINATION lib)
+ """)
+
+ topCmake.close()
+
+
+def generateEventPipeHelperFile(etwmanifest, eventpipe_directory):
+ with open(eventpipe_directory + "eventpipehelpers.cpp", 'w') as helper:
+ helper.write(stdprolog)
+ helper.write("""
+#include "stdlib.h"
+
+bool ResizeBuffer(char *&buffer, unsigned int& size, unsigned int currLen, unsigned int newSize, bool &fixedBuffer)
+{
+ newSize *= 1.5;
+ _ASSERTE(newSize > size); // check for overflow
+
+ if (newSize < 32)
+ newSize = 32;
+
+ char *newBuffer = new char[newSize];
+
+ memcpy(newBuffer, buffer, currLen);
+
+ if (!fixedBuffer)
+ delete[] buffer;
+
+ buffer = newBuffer;
+ size = newSize;
+ fixedBuffer = false;
+
+ return true;
+}
+
+bool WriteToBuffer(const BYTE *src, unsigned int len, char *&buffer, unsigned int& offset, unsigned int& 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;
+}
+
+bool WriteToBuffer(PCWSTR str, char *&buffer, unsigned int& offset, unsigned int& size, bool &fixedBuffer)
+{
+ if(!str) return true;
+ unsigned 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;
+}
+
+bool WriteToBuffer(const char *str, char *&buffer, unsigned int& offset, unsigned int& size, bool &fixedBuffer)
+{
+ if(!str) return true;
+ unsigned 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;
+}
+
+""")
+
+ tree = DOM.parse(etwmanifest)
+
+ for providerNode in tree.getElementsByTagName('provider'):
+ providerName = providerNode.getAttribute('name')
+ providerPrettyName = providerName.replace("Windows-", '')
+ providerPrettyName = providerPrettyName.replace("Microsoft-", '')
+ providerPrettyName = providerPrettyName.replace('-', '_')
+ helper.write(
+ "extern \"C\" void Init" +
+ providerPrettyName +
+ "();\n\n")
+
+ helper.write("extern \"C\" void InitProvidersAndEvents()\n{\n")
+ for providerNode in tree.getElementsByTagName('provider'):
+ providerName = providerNode.getAttribute('name')
+ providerPrettyName = providerName.replace("Windows-", '')
+ providerPrettyName = providerPrettyName.replace("Microsoft-", '')
+ providerPrettyName = providerPrettyName.replace('-', '_')
+ helper.write(" Init" + providerPrettyName + "();\n")
+ helper.write("}")
+
+ helper.close()
+
+
+def generateEventPipeImplFiles(
+ etwmanifest, eventpipe_directory, exclusionListFile):
+ tree = DOM.parse(etwmanifest)
+ coreclrRoot = os.getcwd()
+ for providerNode in tree.getElementsByTagName('provider'):
+ providerGUID = providerNode.getAttribute('guid')
+ providerGUID = generateGUID(providerGUID)
+ providerName = providerNode.getAttribute('name')
+
+ providerPrettyName = providerName.replace("Windows-", '')
+ providerPrettyName = providerPrettyName.replace("Microsoft-", '')
+ 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)
+
+ 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\"
+
+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);
+
+template <typename T>
+bool WriteToBuffer(const T &value, char *&buffer, unsigned int& offset, unsigned int& 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;
+}
+
+""" % (coreclrRoot, coreclrRoot, coreclrRoot, coreclrRoot)
+
+ eventpipeImpl.write(header)
+ eventpipeImpl.write(
+ "GUID const " +
+ providerPrettyName +
+ "GUID = " +
+ providerGUID +
+ ";\n")
+ eventpipeImpl.write(
+ "EventPipeProvider *EventPipeProvider" +
+ providerPrettyName +
+ " = nullptr;\n")
+ templateNodes = providerNode.getElementsByTagName('template')
+ allTemplates = parseTemplateNodes(templateNodes)
+ eventNodes = providerNode.getElementsByTagName('event')
+ eventpipeImpl.write(
+ generateClrEventPipeWriteEventsImpl(
+ providerName,
+ eventNodes,
+ allTemplates,
+ exclusionListFile) + "\n")
+ eventpipeImpl.close()
+
+
+def generateEventPipeFiles(
+ etwmanifest, eventpipe_directory, exclusionListFile):
+ eventpipe_directory = eventpipe_directory + "/"
+ tree = DOM.parse(etwmanifest)
+
+ if not os.path.exists(eventpipe_directory):
+ os.makedirs(eventpipe_directory)
+
+ # generate Cmake file
+ generateEventPipeCmakeFile(etwmanifest, eventpipe_directory)
+
+ # generate helper file
+ generateEventPipeHelperFile(etwmanifest, eventpipe_directory)
+
+ # generate all keywords
+ for keywordNode in tree.getElementsByTagName('keyword'):
+ keywordName = keywordNode.getAttribute('name')
+ keywordMask = keywordNode.getAttribute('mask')
+ keywordMap[keywordName] = int(keywordMask, 0)
+
+ # generate .cpp file for each provider
+ generateEventPipeImplFiles(
+ etwmanifest,
+ eventpipe_directory,
+ exclusionListFile)
+
+import argparse
+import sys
+
+
+def main(argv):
+
+ # parse the command line
+ parser = argparse.ArgumentParser(
+ description="Generates the Code required to instrument eventpipe 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('--exc', type=str, required=True,
+ help='full path to exclusion list')
+ args, unknown = parser.parse_known_args(argv)
+ if unknown:
+ print('Unknown argument(s): ', ', '.join(unknown))
+ return const.UnknownArguments
+
+ sClrEtwAllMan = args.man
+ intermediate = args.intermediate
+ exclusionListFile = args.exc
+
+ generateEventPipeFiles(sClrEtwAllMan, intermediate, exclusionListFile)
+
+if __name__ == '__main__':
+ return_code = main(sys.argv[1:])
+ sys.exit(return_code)
diff --git a/src/scripts/genXplatEventing.py b/src/scripts/genXplatEventing.py
index 6c6498d3de..6968d293e9 100644
--- a/src/scripts/genXplatEventing.py
+++ b/src/scripts/genXplatEventing.py
@@ -39,7 +39,7 @@ stdprolog_cmake="""
#******************************************************************
"""
-lindent = " ";
+lindent = " ";
palDataTypeMapping ={
#constructed types
"win:null" :" ",
@@ -282,18 +282,17 @@ def generateClrallEvents(eventNodes,allTemplates):
#generate EventEnabled
clrallEvents.append("inline BOOL EventEnabled")
clrallEvents.append(eventName)
- clrallEvents.append("() {return XplatEventLogger::IsEventLoggingEnabled() && EventXplatEnabled")
- clrallEvents.append(eventName+"();}\n\n")
+ clrallEvents.append("() {return ")
+ clrallEvents.append("EventPipeEventEnabled" + eventName + "() || ")
+ clrallEvents.append("(XplatEventLogger::IsEventLoggingEnabled() && EventXplatEnabled")
+ clrallEvents.append(eventName+"());}\n\n")
#generate FireEtw functions
fnptype = []
fnbody = []
fnptype.append("inline ULONG FireEtw")
fnptype.append(eventName)
fnptype.append("(\n")
- fnbody.append(lindent)
- fnbody.append("if (!EventEnabled")
- fnbody.append(eventName)
- fnbody.append("()) {return ERROR_SUCCESS;}\n")
+
line = []
fnptypeline = []
@@ -339,11 +338,22 @@ def generateClrallEvents(eventNodes,allTemplates):
fnptype.extend(fnptypeline)
fnptype.append("\n)\n{\n")
fnbody.append(lindent)
- fnbody.append("return FireEtXplat")
+ fnbody.append("ULONG status = EventPipeWriteEvent" + eventName + "(" + ''.join(line) + ");\n")
+ fnbody.append(lindent)
+ fnbody.append("if(XplatEventLogger::IsEventLoggingEnabled())\n")
+ fnbody.append(lindent)
+ fnbody.append("{\n")
+ fnbody.append(lindent)
+ fnbody.append(lindent)
+ fnbody.append("status &= FireEtXplat")
fnbody.append(eventName)
fnbody.append("(")
fnbody.extend(line)
fnbody.append(");\n")
+ fnbody.append(lindent)
+ fnbody.append("}\n")
+ fnbody.append(lindent)
+ fnbody.append("return status;\n")
fnbody.append("}\n\n")
clrallEvents.extend(fnptype)
@@ -400,6 +410,57 @@ def generateClrXplatEvents(eventNodes, allTemplates):
return ''.join(clrallEvents)
+def generateClrEventPipeWriteEvents(eventNodes, allTemplates):
+ clrallEvents = []
+ for eventNode in eventNodes:
+ eventName = eventNode.getAttribute('symbol')
+ templateName = eventNode.getAttribute('template')
+
+ #generate EventPipeEventEnabled and EventPipeWriteEvent functions
+ eventenabled = []
+ writeevent = []
+ fnptypeline = []
+
+ eventenabled.append("extern \"C\" bool EventPipeEventEnabled")
+ eventenabled.append(eventName)
+ eventenabled.append("();\n")
+
+ writeevent.append("extern \"C\" ULONG EventPipeWriteEvent")
+ writeevent.append(eventName)
+ writeevent.append("(\n")
+
+ if templateName:
+ template = allTemplates[templateName]
+ fnSig = template.signature
+
+ for params in fnSig.paramlist:
+ fnparam = fnSig.getParam(params)
+ wintypeName = fnparam.winType
+ typewName = palDataTypeMapping[wintypeName]
+ winCount = fnparam.count
+ countw = palDataTypeMapping[winCount]
+
+ if params in template.structs:
+ fnptypeline.append("%sint %s_ElementSize,\n" % (lindent, params))
+
+ fnptypeline.append(lindent)
+ fnptypeline.append(typewName)
+ fnptypeline.append(countw)
+ fnptypeline.append(" ")
+ fnptypeline.append(fnparam.name)
+ fnptypeline.append(",\n")
+
+ #remove trailing commas
+ if len(fnptypeline) > 0:
+ del fnptypeline[-1]
+
+ writeevent.extend(fnptypeline)
+ writeevent.append("\n);\n")
+ clrallEvents.extend(eventenabled)
+ clrallEvents.extend(writeevent)
+
+ return ''.join(clrallEvents)
+
#generates the dummy header file which is used by the VM as entry point to the logging Functions
def generateclrEtwDummy(eventNodes,allTemplates):
clretmEvents = []
@@ -670,15 +731,19 @@ def generatePlformIndependentFiles(sClrEtwAllMan,incDir,etmDummyFile):
clrallevents = incDir + "/clretwallmain.h"
clrxplatevents = incDir + "/clrxplatevents.h"
+ clreventpipewriteevents = incDir + "/clreventpipewriteevents.h"
Clrallevents = open(clrallevents,'w')
Clrxplatevents = open(clrxplatevents,'w')
+ Clreventpipewriteevents = open(clreventpipewriteevents,'w')
Clrallevents.write(stdprolog + "\n")
Clrxplatevents.write(stdprolog + "\n")
+ Clreventpipewriteevents.write(stdprolog + "\n")
- Clrallevents.write("\n#include \"clrxplatevents.h\"\n\n")
-
+ 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)
@@ -689,8 +754,12 @@ def generatePlformIndependentFiles(sClrEtwAllMan,incDir,etmDummyFile):
#pal: create clrallevents.h
Clrxplatevents.write(generateClrXplatEvents(eventNodes, allTemplates) + "\n")
+ #eventpipe: create clreventpipewriteevents.h
+ Clreventpipewriteevents.write(generateClrEventPipeWriteEvents(eventNodes, allTemplates) + "\n")
+
Clrxplatevents.close()
Clrallevents.close()
+ Clreventpipewriteevents.close()
class EventExclusions:
def __init__(self):
diff --git a/src/scripts/genXplatLttng.py b/src/scripts/genXplatLttng.py
index 6dd60b0a0b..fae0e120da 100644
--- a/src/scripts/genXplatLttng.py
+++ b/src/scripts/genXplatLttng.py
@@ -594,7 +594,7 @@ bool ResizeBuffer(char *&buffer, int& size, int currLen, int newSize, bool &fixe
bool WriteToBuffer(const BYTE *src, int len, char *&buffer, int& offset, int& size, bool &fixedBuffer)
{
if (!src) return true;
- if (offset + len)
+ if (offset + len > size)
{
if (!ResizeBuffer(buffer, size, offset, size + len, fixedBuffer))
return false;
@@ -610,7 +610,7 @@ bool WriteToBuffer(PCWSTR str, char *&buffer, int& offset, int& size, bool &fixe
if (!str) return true;
int byteCount = (PAL_wcslen(str) + 1) * sizeof(*str);
- if (offset + byteCount)
+ if (offset + byteCount > size)
{
if (!ResizeBuffer(buffer, size, offset, size + byteCount, fixedBuffer))
return false;
@@ -625,7 +625,7 @@ bool WriteToBuffer(const char *str, char *&buffer, int& offset, int& size, bool
{
if (!str) return true;
int len = strlen(str) + 1;
- if (offset + len)
+ if (offset + len > size)
{
if (!ResizeBuffer(buffer, size, offset, size + len, fixedBuffer))
return false;
diff --git a/src/vm/CMakeLists.txt b/src/vm/CMakeLists.txt
index ec4ff425d6..556eb0e93a 100644
--- a/src/vm/CMakeLists.txt
+++ b/src/vm/CMakeLists.txt
@@ -164,8 +164,14 @@ set(VM_SOURCES_WKS
eepolicy.cpp
eetoprofinterfaceimpl.cpp
eventpipe.cpp
+ eventpipeconfiguration.cpp
+ eventpipeevent.cpp
+ eventpipeeventinstance.cpp
+ eventpipefile.cpp
eventpipejsonfile.cpp
+ eventpipeprovider.cpp
eventstore.cpp
+ fastserializer.cpp
fcall.cpp
fieldmarshaler.cpp
finalizerthread.cpp
@@ -481,3 +487,7 @@ 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)
diff --git a/src/vm/ceemain.cpp b/src/vm/ceemain.cpp
index 9cce46d2f0..c82f7d4ee5 100644
--- a/src/vm/ceemain.cpp
+++ b/src/vm/ceemain.cpp
@@ -693,6 +693,11 @@ void EEStartupHelper(COINITIEE fFlags)
InitThreadManager();
STRESS_LOG0(LF_STARTUP, LL_ALWAYS, "Returned successfully from InitThreadManager");
+#ifdef FEATURE_PERFTRACING
+ // Initialize the event pipe.
+ EventPipe::Initialize();
+#endif // FEATURE_PERFTRACING
+
#ifdef FEATURE_EVENT_TRACE
// Initialize event tracing early so we can trace CLR startup time events.
InitializeEventTracing();
@@ -1036,8 +1041,7 @@ void EEStartupHelper(COINITIEE fFlags)
#endif
#ifdef FEATURE_PERFTRACING
- // Initialize the event pipe and start it if requested.
- EventPipe::Initialize();
+ // Start the event pipe if requested.
EventPipe::EnableOnStartup();
#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipe.cpp b/src/vm/eventpipe.cpp
index 98d382ea17..8ea3f0867e 100644
--- a/src/vm/eventpipe.cpp
+++ b/src/vm/eventpipe.cpp
@@ -4,21 +4,46 @@
#include "common.h"
#include "eventpipe.h"
+#include "eventpipeconfiguration.h"
+#include "eventpipeevent.h"
+#include "eventpipefile.h"
+#include "eventpipeprovider.h"
#include "eventpipejsonfile.h"
#include "sampleprofiler.h"
-CrstStatic EventPipe::s_initCrst;
+#ifdef FEATURE_PAL
+#include "pal.h"
+#endif // FEATURE_PAL
+
+#ifdef FEATURE_PERFTRACING
+
+CrstStatic EventPipe::s_configCrst;
bool EventPipe::s_tracingInitialized = false;
-bool EventPipe::s_tracingEnabled = false;
+EventPipeConfiguration* EventPipe::s_pConfig = NULL;
+EventPipeFile* EventPipe::s_pFile = NULL;
EventPipeJsonFile* EventPipe::s_pJsonFile = NULL;
+#ifdef FEATURE_PAL
+// This function is auto-generated from /src/scripts/genEventPipe.py
+extern "C" void InitProvidersAndEvents();
+#endif
+
void EventPipe::Initialize()
{
STANDARD_VM_CONTRACT;
- s_tracingInitialized = s_initCrst.InitNoThrow(
+ s_tracingInitialized = s_configCrst.InitNoThrow(
CrstEventPipe,
- (CrstFlags)(CRST_TAKEN_DURING_SHUTDOWN));
+ (CrstFlags)(CRST_REENTRANCY | CRST_TAKEN_DURING_SHUTDOWN));
+
+ s_pConfig = new EventPipeConfiguration();
+ s_pConfig->Initialize();
+
+#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()
@@ -66,9 +91,14 @@ void EventPipe::Enable()
return;
}
- // Take the lock and enable tracing.
- CrstHolder _crst(&s_initCrst);
- s_tracingEnabled = true;
+ // Take the lock before enabling tracing.
+ CrstHolder _crst(GetLock());
+
+ // Create the event pipe file.
+ SString eventPipeFileOutputPath;
+ eventPipeFileOutputPath.Printf("Process-%d.netperf", GetCurrentProcessId());
+ s_pFile = new EventPipeFile(eventPipeFileOutputPath);
+
if(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_PerformanceTracing) == 2)
{
// File placed in current working directory.
@@ -77,7 +107,14 @@ void EventPipe::Enable()
s_pJsonFile = new EventPipeJsonFile(outputFilePath);
}
+ // Enable tracing.
+ s_pConfig->Enable();
+
+ // Enable the sample profiler
SampleProfiler::Enable();
+
+ // TODO: Iterate through the set of providers, enable them as appropriate.
+ // This in-turn will iterate through all of the events and set their isEnabled bits.
}
void EventPipe::Disable()
@@ -90,32 +127,38 @@ void EventPipe::Disable()
}
CONTRACTL_END;
- CrstHolder _crst(&s_initCrst);
- s_tracingEnabled = false;
+ // Don't block GC during clean-up.
+ GCX_PREEMP();
+
+ // Take the lock before disabling tracing.
+ CrstHolder _crst(GetLock());
+
+ // Disable the profiler.
SampleProfiler::Disable();
+ // Disable tracing.
+ s_pConfig->Disable();
+
if(s_pJsonFile != NULL)
{
delete(s_pJsonFile);
s_pJsonFile = NULL;
}
-}
-bool EventPipe::EventEnabled(GUID& providerID, INT64 keyword)
-{
- CONTRACTL
+ if(s_pFile != NULL)
{
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
+ delete(s_pFile);
+ s_pFile = NULL;
}
- CONTRACTL_END;
- // TODO: Implement filtering.
- return false;
+ if(s_pConfig != NULL)
+ {
+ delete(s_pConfig);
+ s_pConfig = NULL;
+ }
}
-void EventPipe::WriteEvent(GUID& providerID, INT64 eventID, BYTE *pData, size_t length, bool sampleStack)
+void EventPipe::WriteEvent(EventPipeEvent &event, BYTE *pData, unsigned int length)
{
CONTRACTL
{
@@ -125,41 +168,53 @@ void EventPipe::WriteEvent(GUID& providerID, INT64 eventID, BYTE *pData, size_t
}
CONTRACTL_END;
- StackContents stackContents;
- bool stackWalkSucceeded;
-
- if(sampleStack)
+ // Exit early if the event is not enabled.
+ if(!event.IsEnabled())
{
- stackWalkSucceeded = WalkManagedStackForCurrentThread(stackContents);
+ return;
}
- // TODO: Write the event.
+ DWORD threadID = GetCurrentThreadId();
+
+ // Create an instance of the event.
+ EventPipeEventInstance instance(
+ event,
+ threadID,
+ pData,
+ length);
+
+ // Write to the EventPipeFile.
+ _ASSERTE(s_pFile != NULL);
+ s_pFile->WriteEvent(instance);
+
+ // Write to the EventPipeJsonFile if it exists.
+ if(s_pJsonFile != NULL)
+ {
+ s_pJsonFile->WriteEvent(instance);
+ }
}
-void EventPipe::WriteSampleProfileEvent(Thread *pThread, StackContents &stackContents)
+void EventPipe::WriteSampleProfileEvent(SampleProfilerEventInstance &instance)
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_PREEMPTIVE;
- PRECONDITION(pThread != NULL);
}
CONTRACTL_END;
- EX_TRY
+ // Write to the EventPipeFile.
+ if(s_pFile != NULL)
{
- if(s_pJsonFile != NULL)
- {
- CommonEventFields eventFields;
- QueryPerformanceCounter(&eventFields.TimeStamp);
- eventFields.ThreadID = pThread->GetOSThreadId();
+ s_pFile->WriteEvent(instance);
+ }
- static SString message(W("THREAD_TIME"));
- s_pJsonFile->WriteEvent(eventFields, message, stackContents);
- }
+ // Write to the EventPipeJsonFile if it exists.
+ if(s_pJsonFile != NULL)
+ {
+ s_pJsonFile->WriteEvent(instance);
}
- EX_CATCH{} EX_END_CATCH(SwallowAllExceptions);
}
bool EventPipe::WalkManagedStackForCurrentThread(StackContents &stackContents)
@@ -173,8 +228,12 @@ bool EventPipe::WalkManagedStackForCurrentThread(StackContents &stackContents)
CONTRACTL_END;
Thread *pThread = GetThread();
- _ASSERTE(pThread != NULL);
- return WalkManagedStackForThread(pThread, stackContents);
+ if(pThread != NULL)
+ {
+ return WalkManagedStackForThread(pThread, stackContents);
+ }
+
+ return false;
}
bool EventPipe::WalkManagedStackForThread(Thread *pThread, StackContents &stackContents)
@@ -232,3 +291,19 @@ StackWalkAction EventPipe::StackWalkCallback(CrawlFrame *pCf, StackContents *pDa
// Continue the stack walk.
return SWA_CONTINUE;
}
+
+EventPipeConfiguration* EventPipe::GetConfiguration()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return s_pConfig;
+}
+
+CrstStatic* EventPipe::GetLock()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return &s_configCrst;
+}
+
+#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipe.h b/src/vm/eventpipe.h
index 2978412325..c6d7f61152 100644
--- a/src/vm/eventpipe.h
+++ b/src/vm/eventpipe.h
@@ -5,19 +5,17 @@
#ifndef __EVENTPIPE_H__
#define __EVENTPIPE_H__
-#include "common.h"
+#ifdef FEATURE_PERFTRACING
-class EventPipeJsonFile;
-
-// The data fields common to every event.
-struct CommonEventFields
-{
- // Timestamp generated by QueryPerformanceCounter.
- LARGE_INTEGER TimeStamp;
+#include "crst.h"
+#include "stackwalk.h"
- // Thread ID.
- DWORD ThreadID;
-};
+class EventPipeConfiguration;
+class EventPipeEvent;
+class EventPipeFile;
+class EventPipeJsonFile;
+class MethodDesc;
+class SampleProfilerEventInstance;
class StackContents
{
@@ -103,10 +101,30 @@ public:
m_nextAvailableFrame++;
}
}
+
+ BYTE* GetPointer() const
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return (BYTE*)m_stackFrames;
+ }
+
+ unsigned int GetSize() const
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return (m_nextAvailableFrame * sizeof(UINT_PTR));
+ }
};
class EventPipe
{
+ // Declare friends.
+ friend class EventPipeConfiguration;
+ friend class EventPipeFile;
+ friend class EventPipeProvider;
+ friend class SampleProfiler;
+
public:
// Initialize the event pipe.
@@ -124,15 +142,12 @@ class EventPipe
// Disable tracing via the event pipe.
static void Disable();
- // Determine whether or not the specified provider/keyword combination is enabled.
- static bool EventEnabled(GUID& providerID, INT64 keyword);
-
- // Write out an event. The event is identified by the providerID/eventID pair.
+ // Write out an event.
// Data is written as a serialized blob matching the ETW serialization conventions.
- static void WriteEvent(GUID& providerID, INT64 eventID, BYTE *pData, size_t length, bool sampleStack);
+ static void WriteEvent(EventPipeEvent &event, BYTE *pData, unsigned int length);
- // Write out a sample profile event with the specified stack.
- static void WriteSampleProfileEvent(Thread *pThread, StackContents &stackContents);
+ // Write out a sample profile event.
+ static void WriteSampleProfileEvent(SampleProfilerEventInstance &instance);
// Get the managed call stack for the current thread.
static bool WalkManagedStackForCurrentThread(StackContents &stackContents);
@@ -145,10 +160,20 @@ class EventPipe
// Callback function for the stack walker. For each frame walked, this callback is invoked.
static StackWalkAction StackWalkCallback(CrawlFrame *pCf, StackContents *pData);
- static CrstStatic s_initCrst;
+ // Get the configuration object.
+ // This is called directly by the EventPipeProvider constructor to register the new provider.
+ static EventPipeConfiguration* GetConfiguration();
+
+ // Get the event pipe configuration lock.
+ static CrstStatic* GetLock();
+
+ static CrstStatic s_configCrst;
static bool s_tracingInitialized;
- static bool s_tracingEnabled;
+ static EventPipeConfiguration *s_pConfig;
+ static EventPipeFile *s_pFile;
static EventPipeJsonFile *s_pJsonFile;
};
+#endif // FEATURE_PERFTRACING
+
#endif // __EVENTPIPE_H__
diff --git a/src/vm/eventpipeconfiguration.cpp b/src/vm/eventpipeconfiguration.cpp
new file mode 100644
index 0000000000..cfb96fc8d5
--- /dev/null
+++ b/src/vm/eventpipeconfiguration.cpp
@@ -0,0 +1,271 @@
+// 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.
+
+#include "common.h"
+#include "eventpipe.h"
+#include "eventpipeconfiguration.h"
+#include "eventpipeeventinstance.h"
+#include "eventpipeprovider.h"
+
+#ifdef FEATURE_PERFTRACING
+
+// {5291C09C-2660-4D6A-83A3-C383FD020DEC}
+const GUID EventPipeConfiguration::s_configurationProviderID =
+ { 0x5291c09c, 0x2660, 0x4d6a, { 0x83, 0xa3, 0xc3, 0x83, 0xfd, 0x2, 0xd, 0xec } };
+
+EventPipeConfiguration::EventPipeConfiguration()
+{
+ STANDARD_VM_CONTRACT;
+
+ m_pProviderList = new SList<SListElem<EventPipeProvider*>>();
+}
+
+EventPipeConfiguration::~EventPipeConfiguration()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if(m_pProviderList != NULL)
+ {
+ delete(m_pProviderList);
+ m_pProviderList = NULL;
+ }
+}
+
+void EventPipeConfiguration::Initialize()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Create the configuration provider.
+ m_pConfigProvider = new EventPipeProvider(s_configurationProviderID);
+
+ // Create the metadata event.
+ m_pMetadataEvent = m_pConfigProvider->AddEvent(
+ 0, /* keywords */
+ 0, /* eventID */
+ 0, /* eventVersion */
+ EventPipeEventLevel::Critical,
+ false); /* needStack */
+}
+
+bool EventPipeConfiguration::RegisterProvider(EventPipeProvider &provider)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Take the lock before manipulating the provider list.
+ CrstHolder _crst(EventPipe::GetLock());
+
+ // See if we've already registered this provider.
+ EventPipeProvider *pExistingProvider = GetProviderNoLock(provider.GetProviderID());
+ if(pExistingProvider != NULL)
+ {
+ return false;
+ }
+
+ // The provider has not been registered, so register it.
+ m_pProviderList->InsertTail(new SListElem<EventPipeProvider*>(&provider));
+
+ // TODO: Set the provider configuration and enable it if we know
+ // anything about the provider before it is registered.
+
+ return true;
+}
+
+bool EventPipeConfiguration::UnregisterProvider(EventPipeProvider &provider)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Take the lock before manipulating the provider list.
+ CrstHolder _crst(EventPipe::GetLock());
+
+ // Find the provider.
+ SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead();
+ while(pElem != NULL)
+ {
+ if(pElem->GetValue() == &provider)
+ {
+ break;
+ }
+
+ pElem = m_pProviderList->GetNext(pElem);
+ }
+
+ // If we found the provider, remove it.
+ if(pElem != NULL)
+ {
+ if(m_pProviderList->FindAndRemove(pElem) != NULL)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+EventPipeProvider* EventPipeConfiguration::GetProvider(const GUID &providerID)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Take the lock before touching the provider list to ensure no one tries to
+ // modify the list.
+ CrstHolder _crst(EventPipe::GetLock());
+
+ return GetProviderNoLock(providerID);
+}
+
+EventPipeProvider* EventPipeConfiguration::GetProviderNoLock(const GUID &providerID)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
+ }
+ CONTRACTL_END;
+
+ SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead();
+ while(pElem != NULL)
+ {
+ EventPipeProvider *pProvider = pElem->GetValue();
+ if(pProvider->GetProviderID() == providerID)
+ {
+ return pProvider;
+ }
+
+ pElem = m_pProviderList->GetNext(pElem);
+ }
+
+ return NULL;
+}
+
+void EventPipeConfiguration::Enable()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ // Lock must be held by EventPipe::Enable.
+ PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
+ }
+ CONTRACTL_END;
+
+ SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead();
+ while(pElem != NULL)
+ {
+ // TODO: Only enable the providers that have been explicitly enabled with specified keywords/level.
+ EventPipeProvider *pProvider = pElem->GetValue();
+ pProvider->SetConfiguration(true /* providerEnabled */, 0xFFFFFFFFFFFFFFFF /* keywords */, EventPipeEventLevel::Verbose /* level */);
+
+ pElem = m_pProviderList->GetNext(pElem);
+ }
+
+}
+
+void EventPipeConfiguration::Disable()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ // Lock must be held by EventPipe::Disable.
+ PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
+ }
+ CONTRACTL_END;
+
+ SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead();
+ while(pElem != NULL)
+ {
+ EventPipeProvider *pProvider = pElem->GetValue();
+ pProvider->SetConfiguration(false /* providerEnabled */, 0 /* keywords */, EventPipeEventLevel::Critical /* level */);
+
+ pElem = m_pProviderList->GetNext(pElem);
+ }
+}
+
+EventPipeEventInstance* EventPipeConfiguration::BuildEventMetadataEvent(EventPipeEvent &sourceEvent, BYTE *pPayloadData, unsigned int payloadLength)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // The payload of the event should contain:
+ // - GUID ProviderID.
+ // - unsigned int EventID.
+ // - unsigned int EventVersion.
+ // - Optional event description payload.
+
+ // Calculate the size of the event.
+ const GUID &providerID = sourceEvent.GetProvider()->GetProviderID();
+ unsigned int eventID = sourceEvent.GetEventID();
+ unsigned int eventVersion = sourceEvent.GetEventVersion();
+ unsigned int instancePayloadSize = sizeof(providerID) + sizeof(eventID) + sizeof(eventVersion) + payloadLength;
+
+ // Allocate the payload.
+ BYTE *pInstancePayload = new BYTE[instancePayloadSize];
+
+ // Fill the buffer with the payload.
+ BYTE *currentPtr = pInstancePayload;
+
+ // Write the provider ID.
+ memcpy(currentPtr, (BYTE*)&providerID, sizeof(providerID));
+ currentPtr += sizeof(providerID);
+
+ // Write the event ID.
+ memcpy(currentPtr, &eventID, sizeof(eventID));
+ currentPtr += sizeof(eventID);
+
+ // Write the event version.
+ memcpy(currentPtr, &eventVersion, sizeof(eventVersion));
+ currentPtr += sizeof(eventVersion);
+
+ // Write the incoming payload data.
+ memcpy(currentPtr, pPayloadData, payloadLength);
+
+ // Construct the event instance.
+ EventPipeEventInstance *pInstance = new EventPipeEventInstance(
+ *m_pMetadataEvent,
+ GetCurrentThreadId(),
+ pInstancePayload,
+ instancePayloadSize);
+
+ return pInstance;
+}
+
+#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipeconfiguration.h b/src/vm/eventpipeconfiguration.h
new file mode 100644
index 0000000000..a2377757d4
--- /dev/null
+++ b/src/vm/eventpipeconfiguration.h
@@ -0,0 +1,64 @@
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __EVENTPIPE_CONFIGURATION_H__
+#define __EVENTPIPE_CONFIGURATION_H__
+
+#ifdef FEATURE_PERFTRACING
+
+#include "slist.h"
+
+class EventPipeEvent;
+class EventPipeEventInstance;
+class EventPipeProvider;
+
+class EventPipeConfiguration
+{
+public:
+
+ EventPipeConfiguration();
+ ~EventPipeConfiguration();
+
+ // Perform initialization that cannot be performed in the constructor.
+ void Initialize();
+
+ // Register a provider.
+ bool RegisterProvider(EventPipeProvider &provider);
+
+ // Unregister a provider.
+ bool UnregisterProvider(EventPipeProvider &provider);
+
+ // Get the provider with the specified provider ID if it exists.
+ EventPipeProvider* GetProvider(const GUID &providerID);
+
+ // Enable the event pipe.
+ void Enable();
+
+ // Disable the event pipe.
+ void Disable();
+
+ // Get the event used to write metadata to the event stream.
+ EventPipeEventInstance* BuildEventMetadataEvent(EventPipeEvent &sourceEvent, BYTE *pPayloadData = NULL, unsigned int payloadLength = 0);
+
+private:
+
+ // Get the provider without taking the lock.
+ EventPipeProvider* GetProviderNoLock(const GUID &providerID);
+
+ // The list of event pipe providers.
+ SList<SListElem<EventPipeProvider*>> *m_pProviderList;
+
+ // The provider used to write configuration events to the event stream.
+ EventPipeProvider *m_pConfigProvider;
+
+ // The event used to write event information to the event stream.
+ EventPipeEvent *m_pMetadataEvent;
+
+ // The provider ID for the configuration event pipe provider.
+ // This provider is used to emit configuration events.
+ static const GUID s_configurationProviderID;
+};
+
+#endif // FEATURE_PERFTRACING
+
+#endif // __EVENTPIPE_CONFIGURATION_H__
diff --git a/src/vm/eventpipeevent.cpp b/src/vm/eventpipeevent.cpp
new file mode 100644
index 0000000000..3b9f36bc4b
--- /dev/null
+++ b/src/vm/eventpipeevent.cpp
@@ -0,0 +1,86 @@
+// 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.
+
+#include "common.h"
+#include "eventpipeevent.h"
+#include "eventpipeprovider.h"
+
+#ifdef FEATURE_PERFTRACING
+
+EventPipeEvent::EventPipeEvent(EventPipeProvider &provider, INT64 keywords, unsigned int eventID, unsigned int eventVersion, EventPipeEventLevel level, bool needStack)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ m_pProvider = &provider;
+ m_keywords = keywords;
+ m_eventID = eventID;
+ m_eventVersion = eventVersion;
+ m_level = level;
+ m_needStack = needStack;
+ m_enabled = false;
+}
+
+EventPipeProvider* EventPipeEvent::GetProvider() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_pProvider;
+}
+
+INT64 EventPipeEvent::GetKeywords() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_keywords;
+}
+
+unsigned int EventPipeEvent::GetEventID() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_eventID;
+}
+
+unsigned int EventPipeEvent::GetEventVersion() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_eventVersion;
+}
+
+EventPipeEventLevel EventPipeEvent::GetLevel() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_level;
+}
+
+bool EventPipeEvent::NeedStack() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_needStack;
+}
+
+bool EventPipeEvent::IsEnabled() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_enabled;
+}
+
+void EventPipeEvent::RefreshState()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ m_enabled = m_pProvider->EventEnabled(m_keywords, m_level);
+}
+
+#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipeevent.h b/src/vm/eventpipeevent.h
new file mode 100644
index 0000000000..9e42615ed9
--- /dev/null
+++ b/src/vm/eventpipeevent.h
@@ -0,0 +1,74 @@
+// 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.
+
+#ifndef __EVENTPIPE_EVENT_H__
+#define __EVENTPIPE_EVENT_H__
+
+#ifdef FEATURE_PERFTRACING
+
+#include "eventpipeprovider.h"
+
+class EventPipeEvent
+{
+ // Declare friends.
+ friend class EventPipeProvider;
+
+private:
+
+ // The provider that contains the event.
+ EventPipeProvider *m_pProvider;
+
+ // Bit vector containing the keywords that enable the event.
+ INT64 m_keywords;
+
+ // The ID (within the provider) of the event.
+ unsigned int m_eventID;
+
+ // The version of the event.
+ unsigned int m_eventVersion;
+
+ // The verbosity of the event.
+ EventPipeEventLevel m_level;
+
+ // True if a call stack should be captured when writing the event.
+ bool m_needStack;
+
+ // True if the event is current enabled.
+ bool m_enabled;
+
+ // Refreshes the runtime state for this event.
+ // Called by EventPipeProvider when the provider configuration changes.
+ void RefreshState();
+
+ // Only EventPipeProvider can create events.
+ // The provider is responsible for allocating and freeing events.
+ EventPipeEvent(EventPipeProvider &provider, INT64 keywords, unsigned int eventID, unsigned int eventVersion, EventPipeEventLevel level, bool needStack);
+
+public:
+
+ // Get the provider associated with this event.
+ EventPipeProvider* GetProvider() const;
+
+ // Get the keywords that enable the event.
+ INT64 GetKeywords() const;
+
+ // Get the ID (within the provider) of the event.
+ unsigned int GetEventID() const;
+
+ // Get the version of the event.
+ unsigned int GetEventVersion() const;
+
+ // Get the verbosity of the event.
+ EventPipeEventLevel GetLevel() const;
+
+ // True if a call stack should be captured when writing the event.
+ bool NeedStack() const;
+
+ // True if the event is currently enabled.
+ bool IsEnabled() const;
+};
+
+#endif // FEATURE_PERFTRACING
+
+#endif // __EVENTPIPE_EVENT_H__
diff --git a/src/vm/eventpipeeventinstance.cpp b/src/vm/eventpipeeventinstance.cpp
new file mode 100644
index 0000000000..2bf500be70
--- /dev/null
+++ b/src/vm/eventpipeeventinstance.cpp
@@ -0,0 +1,157 @@
+// 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.
+
+#include "common.h"
+#include "eventpipeeventinstance.h"
+#include "eventpipejsonfile.h"
+#include "fastserializer.h"
+#include "sampleprofiler.h"
+
+#ifdef FEATURE_PERFTRACING
+
+EventPipeEventInstance::EventPipeEventInstance(
+ EventPipeEvent &event,
+ DWORD threadID,
+ BYTE *pData,
+ unsigned int length)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ m_pEvent = &event;
+ m_threadID = threadID;
+ m_pData = pData;
+ m_dataLength = length;
+ QueryPerformanceCounter(&m_timeStamp);
+
+ if(event.NeedStack())
+ {
+ EventPipe::WalkManagedStackForCurrentThread(m_stackContents);
+ }
+}
+
+StackContents* EventPipeEventInstance::GetStack()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return &m_stackContents;
+}
+
+EventPipeEvent* EventPipeEventInstance::GetEvent() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_pEvent;
+}
+
+BYTE* EventPipeEventInstance::GetData() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_pData;
+}
+
+unsigned int EventPipeEventInstance::GetLength() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_dataLength;
+}
+
+void EventPipeEventInstance::FastSerialize(FastSerializer *pSerializer, StreamLabel metadataLabel)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+#ifdef _DEBUG
+ // Useful for diagnosing serialization bugs.
+ const unsigned int value = 0xDEADBEEF;
+ pSerializer->WriteBuffer((BYTE*)&value, sizeof(value));
+#endif
+
+ // Calculate the size of the total payload so that it can be written to the file.
+ unsigned int payloadLength =
+ sizeof(metadataLabel) +
+ sizeof(m_threadID) + // Thread ID
+ sizeof(m_timeStamp) + // TimeStamp
+ m_dataLength + // Event payload data length
+ m_stackContents.GetSize(); // Stack payload size
+
+ // Write the size of the event to the file.
+ pSerializer->WriteBuffer((BYTE*)&payloadLength, sizeof(payloadLength));
+
+ // Write the metadata label.
+ pSerializer->WriteBuffer((BYTE*)&metadataLabel, sizeof(metadataLabel));
+
+ // Write the thread ID.
+ pSerializer->WriteBuffer((BYTE*)&m_threadID, sizeof(m_threadID));
+
+ // Write the timestamp.
+ pSerializer->WriteBuffer((BYTE*)&m_timeStamp, sizeof(m_timeStamp));
+
+ // Write the event data payload.
+ if(m_dataLength > 0)
+ {
+ pSerializer->WriteBuffer(m_pData, m_dataLength);
+ }
+
+ // Write the stack if present.
+ if(m_stackContents.GetSize() > 0)
+ {
+ pSerializer->WriteBuffer(m_stackContents.GetPointer(), m_stackContents.GetSize());
+ }
+}
+
+void EventPipeEventInstance::SerializeToJsonFile(EventPipeJsonFile *pFile)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if(pFile == NULL)
+ {
+ return;
+ }
+
+ EX_TRY
+ {
+ const unsigned int guidSize = 39;
+ WCHAR wszProviderID[guidSize];
+ if(!StringFromGUID2(m_pEvent->GetProvider()->GetProviderID(), wszProviderID, guidSize))
+ {
+ wszProviderID[0] = '\0';
+ }
+
+ // Strip off the {}.
+ StackScratchBuffer scratch;
+ SString guidStr(&wszProviderID[1], guidSize-3);
+
+ SString message;
+ message.Printf("Provider=%s/EventID=%d/Version=%d", guidStr.GetANSI(scratch), m_pEvent->GetEventID(), m_pEvent->GetEventVersion());
+ pFile->WriteEvent(m_timeStamp, m_threadID, message, m_stackContents);
+ }
+ EX_CATCH{} EX_END_CATCH(SwallowAllExceptions);
+}
+
+SampleProfilerEventInstance::SampleProfilerEventInstance(Thread *pThread)
+ :EventPipeEventInstance(*SampleProfiler::s_pThreadTimeEvent, pThread->GetOSThreadId(), NULL, 0)
+{
+ LIMITED_METHOD_CONTRACT;
+}
+
+#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipeeventinstance.h b/src/vm/eventpipeeventinstance.h
new file mode 100644
index 0000000000..84ad566489
--- /dev/null
+++ b/src/vm/eventpipeeventinstance.h
@@ -0,0 +1,64 @@
+// 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.
+
+#ifndef __EVENTPIPE_EVENTINSTANCE_H__
+#define __EVENTPIPE_EVENTINSTANCE_H__
+
+#ifdef FEATURE_PERFTRACING
+
+#include "eventpipe.h"
+#include "eventpipeevent.h"
+#include "fastserializableobject.h"
+#include "fastserializer.h"
+
+class EventPipeEventInstance
+{
+
+public:
+
+ EventPipeEventInstance(EventPipeEvent &event, DWORD threadID, BYTE *pData, unsigned int length);
+
+ // Get the event associated with this instance.
+ EventPipeEvent* GetEvent() const;
+
+ // Get the stack contents object to either read or write to it.
+ StackContents* GetStack();
+
+ // Get a pointer to the data payload.
+ BYTE* GetData() const;
+
+ // Get the length of the data.
+ unsigned int GetLength() const;
+
+ // Serialize this object using FastSerialization.
+ void FastSerialize(FastSerializer *pSerializer, StreamLabel metadataLabel);
+
+ // Serialize this event to the JSON file.
+ void SerializeToJsonFile(EventPipeJsonFile *pFile);
+
+protected:
+
+ EventPipeEvent *m_pEvent;
+ DWORD m_threadID;
+ LARGE_INTEGER m_timeStamp;
+
+ BYTE *m_pData;
+ unsigned int m_dataLength;
+ StackContents m_stackContents;
+};
+
+// A specific type of event instance for use by the SampleProfiler.
+// This is needed because the SampleProfiler knows how to walk stacks belonging
+// to threads other than the current thread.
+class SampleProfilerEventInstance : public EventPipeEventInstance
+{
+
+public:
+
+ SampleProfilerEventInstance(Thread *pThread);
+};
+
+#endif // FEATURE_PERFTRACING
+
+#endif // __EVENTPIPE_EVENTINSTANCE_H__
diff --git a/src/vm/eventpipefile.cpp b/src/vm/eventpipefile.cpp
new file mode 100644
index 0000000000..895f732f51
--- /dev/null
+++ b/src/vm/eventpipefile.cpp
@@ -0,0 +1,146 @@
+// 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.
+
+#include "common.h"
+#include "eventpipeconfiguration.h"
+#include "eventpipefile.h"
+
+#ifdef FEATURE_PERFTRACING
+
+EventPipeFile::EventPipeFile(SString &outputFilePath)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ m_pSerializer = new FastSerializer(outputFilePath, *this);
+ m_serializationLock.Init(LOCK_TYPE_DEFAULT);
+ m_pMetadataLabels = new MapSHashWithRemove<EventPipeEvent*, StreamLabel>();
+
+ // File start time information.
+ GetSystemTime(&m_fileOpenSystemTime);
+ QueryPerformanceCounter(&m_fileOpenTimeStamp);
+ QueryPerformanceFrequency(&m_timeStampFrequency);
+
+ // Write a forward reference to the beginning of the event stream.
+ // This also allows readers to know where the event stream ends and skip it if needed.
+ m_beginEventsForwardReferenceIndex = m_pSerializer->AllocateForwardReference();
+ m_pSerializer->WriteForwardReference(m_beginEventsForwardReferenceIndex);
+
+ // Write the header information into the file.
+
+ // Write the current date and time.
+ m_pSerializer->WriteBuffer((BYTE*)&m_fileOpenSystemTime, sizeof(m_fileOpenSystemTime));
+
+ // Write FileOpenTimeStamp
+ m_pSerializer->WriteBuffer((BYTE*)&m_fileOpenTimeStamp, sizeof(m_fileOpenTimeStamp));
+
+ // Write ClockFrequency
+ m_pSerializer->WriteBuffer((BYTE*)&m_timeStampFrequency, sizeof(m_timeStampFrequency));
+}
+
+EventPipeFile::~EventPipeFile()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Mark the end of the event stream.
+ StreamLabel currentLabel = m_pSerializer->GetStreamLabel();
+
+ // Define the event start forward reference.
+ m_pSerializer->DefineForwardReference(m_beginEventsForwardReferenceIndex, currentLabel);
+
+ // Close the serializer.
+ if(m_pSerializer != NULL)
+ {
+ delete(m_pSerializer);
+ m_pSerializer = NULL;
+ }
+}
+
+void EventPipeFile::WriteEvent(EventPipeEventInstance &instance)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Take the serialization lock.
+ SpinLockHolder _slh(&m_serializationLock);
+
+ // Check to see if we've seen this event type before.
+ // If not, then write the event metadata to the event stream first.
+ StreamLabel metadataLabel = GetMetadataLabel(*instance.GetEvent());
+ if(metadataLabel == 0)
+ {
+ EventPipeEventInstance* pMetadataInstance = EventPipe::GetConfiguration()->BuildEventMetadataEvent(*instance.GetEvent());
+
+ metadataLabel = m_pSerializer->GetStreamLabel();
+ pMetadataInstance->FastSerialize(m_pSerializer, (StreamLabel)0); // 0 breaks recursion and represents the metadata event.
+
+ SaveMetadataLabel(*instance.GetEvent(), metadataLabel);
+
+ delete (pMetadataInstance->GetData());
+ delete (pMetadataInstance);
+ }
+
+ // Write the event to the stream.
+ instance.FastSerialize(m_pSerializer, metadataLabel);
+}
+
+StreamLabel EventPipeFile::GetMetadataLabel(EventPipeEvent &event)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ StreamLabel outLabel;
+ if(m_pMetadataLabels->Lookup(&event, &outLabel))
+ {
+ _ASSERTE(outLabel != 0);
+ return outLabel;
+ }
+
+ return 0;
+}
+
+void EventPipeFile::SaveMetadataLabel(EventPipeEvent &event, StreamLabel label)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(label > 0);
+ }
+ CONTRACTL_END;
+
+ // If a pre-existing metadata label exists, remove it.
+ StreamLabel outLabel;
+ if(m_pMetadataLabels->Lookup(&event, &outLabel))
+ {
+ m_pMetadataLabels->Remove(&event);
+ }
+
+ // Add the metadata label.
+ m_pMetadataLabels->Add(&event, label);
+}
+
+#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipefile.h b/src/vm/eventpipefile.h
new file mode 100644
index 0000000000..1fbb4c0b79
--- /dev/null
+++ b/src/vm/eventpipefile.h
@@ -0,0 +1,75 @@
+// 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.
+
+
+#ifndef __EVENTPIPE_FILE_H__
+#define __EVENTPIPE_FILE_H__
+
+#ifdef FEATURE_PERFTRACING
+
+#include "eventpipe.h"
+#include "eventpipeeventinstance.h"
+#include "fastserializableobject.h"
+#include "fastserializer.h"
+
+class EventPipeFile : public FastSerializableObject
+{
+ public:
+ EventPipeFile(SString &outputFilePath);
+ ~EventPipeFile();
+
+ // Write an event to the file.
+ void WriteEvent(EventPipeEventInstance &instance);
+
+ // Serialize this object.
+ // Not supported - this is the entry object for the trace,
+ // which means that the contents hasn't yet been created.
+ void FastSerialize(FastSerializer *pSerializer)
+ {
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(!"This function should never be called!");
+ }
+
+ // Get the type name of this object.
+ const char* GetTypeName()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return "Microsoft.DotNet.Runtime.EventPipeFile";
+ }
+
+ private:
+
+ // Get the metadata address in the file for an event.
+ // The return value can be written into the file as a back-pointer to the event metadata.
+ StreamLabel GetMetadataLabel(EventPipeEvent &event);
+
+ // Save the metadata address in the file for an event.
+ void SaveMetadataLabel(EventPipeEvent &event, StreamLabel label);
+
+ // The object responsible for serialization.
+ FastSerializer *m_pSerializer;
+
+ // The system time when the file was opened.
+ SYSTEMTIME m_fileOpenSystemTime;
+
+ // The timestamp when the file was opened. Used for calculating file-relative timestamps.
+ LARGE_INTEGER m_fileOpenTimeStamp;
+
+ // The frequency of the timestamps used for this file.
+ LARGE_INTEGER m_timeStampFrequency;
+
+ // The forward reference index that marks the beginning of the event stream.
+ unsigned int m_beginEventsForwardReferenceIndex;
+
+ // The serialization which is responsible for making sure only a single event
+ // or block of events gets written to the file at once.
+ SpinLock m_serializationLock;
+
+ // Hashtable of metadata labels.
+ MapSHashWithRemove<EventPipeEvent*, StreamLabel> *m_pMetadataLabels;
+};
+
+#endif // FEATURE_PERFTRACING
+
+#endif // __EVENTPIPE_FILE_H__
diff --git a/src/vm/eventpipejsonfile.cpp b/src/vm/eventpipejsonfile.cpp
index 6353c917e7..ea2dd29fe0 100644
--- a/src/vm/eventpipejsonfile.cpp
+++ b/src/vm/eventpipejsonfile.cpp
@@ -5,6 +5,8 @@
#include "common.h"
#include "eventpipejsonfile.h"
+#ifdef FEATURE_PERFTRACING
+
EventPipeJsonFile::EventPipeJsonFile(SString &outFilePath)
{
CONTRACTL
@@ -15,6 +17,7 @@ EventPipeJsonFile::EventPipeJsonFile(SString &outFilePath)
}
CONTRACTL_END;
+ m_writeErrorEncountered = false;
m_pFileStream = new CFileStream();
if(FAILED(m_pFileStream->OpenForWrite(outFilePath)))
{
@@ -52,7 +55,14 @@ EventPipeJsonFile::~EventPipeJsonFile()
}
}
-void EventPipeJsonFile::WriteEvent(CommonEventFields &commonFields, SString &message, StackContents &stackContents)
+void EventPipeJsonFile::WriteEvent(EventPipeEventInstance &instance)
+{
+ STANDARD_VM_CONTRACT;
+
+ instance.SerializeToJsonFile(this);
+}
+
+void EventPipeJsonFile::WriteEvent(LARGE_INTEGER timeStamp, DWORD threadID, SString &message, StackContents &stackContents)
{
STANDARD_VM_CONTRACT;
@@ -67,16 +77,16 @@ void EventPipeJsonFile::WriteEvent(CommonEventFields &commonFields, SString &mes
// Convert the timestamp from a QPC value to a trace-relative timestamp.
double millisecondsSinceTraceStart = 0.0;
- if(commonFields.TimeStamp.QuadPart != m_fileOpenTimeStamp.QuadPart)
+ if(timeStamp.QuadPart != m_fileOpenTimeStamp.QuadPart)
{
LARGE_INTEGER elapsedNanoseconds;
- elapsedNanoseconds.QuadPart = commonFields.TimeStamp.QuadPart - m_fileOpenTimeStamp.QuadPart;
+ elapsedNanoseconds.QuadPart = timeStamp.QuadPart - m_fileOpenTimeStamp.QuadPart;
millisecondsSinceTraceStart = elapsedNanoseconds.QuadPart / 1000000.0;
}
StackScratchBuffer scratch;
SString threadFrame;
- threadFrame.Printf("Thread (%d)", commonFields.ThreadID);
+ threadFrame.Printf("Thread (%d)", threadID);
SString event;
event.Printf("{\"Time\" : \"%f\", \"Metric\" : \"1\",\n\"Stack\": [\n\"%s\",\n%s\"%s\"]},", millisecondsSinceTraceStart, message.GetANSI(scratch), strCallStack.GetANSI(scratch), threadFrame.GetANSI(scratch));
Write(event);
@@ -129,3 +139,5 @@ void EventPipeJsonFile::FormatCallStack(StackContents &stackContents, SString &r
resultStr.Append(frameStr);
}
}
+
+#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipejsonfile.h b/src/vm/eventpipejsonfile.h
index b6e42def68..2b836d2c67 100644
--- a/src/vm/eventpipejsonfile.h
+++ b/src/vm/eventpipejsonfile.h
@@ -6,8 +6,11 @@
#ifndef __EVENTPIPE_JSONFILE_H__
#define __EVENTPIPE_JSONFILE_H__
+#ifdef FEATURE_PERFTRACING
+
#include "common.h"
#include "eventpipe.h"
+#include "eventpipeeventinstance.h"
#include "fstream.h"
class EventPipeJsonFile
@@ -16,8 +19,11 @@ class EventPipeJsonFile
EventPipeJsonFile(SString &outFilePath);
~EventPipeJsonFile();
+ // Write an event instance.
+ void WriteEvent(EventPipeEventInstance &instance);
+
// Write an event with the specified message and stack.
- void WriteEvent(CommonEventFields &commonFields, SString &message, StackContents &stackContents);
+ void WriteEvent(LARGE_INTEGER timeStamp, DWORD threadID, SString &message, StackContents &stackContents);
private:
@@ -37,4 +43,6 @@ class EventPipeJsonFile
LARGE_INTEGER m_fileOpenTimeStamp;
};
+#endif // FEATURE_PERFTRACING
+
#endif // __EVENTPIPE_JSONFILE_H__
diff --git a/src/vm/eventpipeprovider.cpp b/src/vm/eventpipeprovider.cpp
new file mode 100644
index 0000000000..da185334a9
--- /dev/null
+++ b/src/vm/eventpipeprovider.cpp
@@ -0,0 +1,253 @@
+// 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.
+
+#include "common.h"
+#include "eventpipe.h"
+#include "eventpipeconfiguration.h"
+#include "eventpipeevent.h"
+#include "eventpipeprovider.h"
+
+#ifdef FEATURE_PERFTRACING
+
+EventPipeProvider::EventPipeProvider(const GUID &providerID)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ m_providerID = providerID;
+ m_enabled = false;
+ m_keywords = 0;
+ m_providerLevel = EventPipeEventLevel::Critical;
+ m_pEventList = new SList<SListElem<EventPipeEvent*>>();
+ m_pCallbackFunction = NULL;
+ m_pCallbackData = NULL;
+
+ // Register the provider.
+ EventPipeConfiguration* pConfig = EventPipe::GetConfiguration();
+ _ASSERTE(pConfig != NULL);
+ pConfig->RegisterProvider(*this);
+}
+
+EventPipeProvider::~EventPipeProvider()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Unregister the provider.
+ // This call is re-entrant.
+ EventPipeConfiguration* pConfig = EventPipe::GetConfiguration();
+ _ASSERTE(pConfig != NULL);
+ pConfig->UnregisterProvider(*this);
+
+ // 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)
+ {
+ EventPipeEvent *pEvent = pElem->GetValue();
+ delete pEvent;
+
+ pElem = m_pEventList->GetNext(pElem);
+ }
+
+ delete m_pEventList;
+ m_pEventList = NULL;
+ }
+}
+
+const GUID& EventPipeProvider::GetProviderID() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_providerID;
+}
+
+bool EventPipeProvider::Enabled() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_enabled;
+}
+
+bool EventPipeProvider::EventEnabled(INT64 keywords) const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // The event is enabled if:
+ // - The provider is enabled.
+ // - The event keywords are unspecified in the manifest (== 0) or when masked with the enabled config are != 0.
+ return (Enabled() && ((keywords == 0) || ((m_keywords & keywords) != 0)));
+}
+
+bool EventPipeProvider::EventEnabled(INT64 keywords, EventPipeEventLevel eventLevel) const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // The event is enabled if:
+ // - The provider is enabled.
+ // - The event keywords are unspecified in the manifest (== 0) or when masked with the enabled config are != 0.
+ // - The event level is LogAlways or the provider's verbosity level is set to greater than the event's verbosity level in the manifest.
+ return (EventEnabled(keywords) &&
+ ((eventLevel == EventPipeEventLevel::LogAlways) || (m_providerLevel >= eventLevel)));
+}
+
+void EventPipeProvider::SetConfiguration(bool providerEnabled, INT64 keywords, EventPipeEventLevel providerLevel)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
+ }
+ CONTRACTL_END;
+
+ m_enabled = providerEnabled;
+ m_keywords = keywords;
+ m_providerLevel = providerLevel;
+
+ RefreshAllEvents();
+ InvokeCallback();
+}
+
+EventPipeEvent* EventPipeProvider::AddEvent(INT64 keywords, unsigned int eventID, unsigned int eventVersion, EventPipeEventLevel level, bool needStack)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Create the event.
+ EventPipeEvent *pEvent = new EventPipeEvent(
+ *this,
+ keywords,
+ eventID,
+ eventVersion,
+ level,
+ needStack);
+
+ // Add it to the list of events.
+ AddEvent(*pEvent);
+ return pEvent;
+}
+
+void EventPipeProvider::AddEvent(EventPipeEvent &event)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Take the config lock before inserting a new event.
+ CrstHolder _crst(EventPipe::GetLock());
+
+ m_pEventList->InsertTail(new SListElem<EventPipeEvent*>(&event));
+}
+
+void EventPipeProvider::RegisterCallback(EventPipeCallback pCallbackFunction, void *pData)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Take the config lock before setting the callback.
+ CrstHolder _crst(EventPipe::GetLock());
+
+ if(m_pCallbackFunction == NULL)
+ {
+ m_pCallbackFunction = pCallbackFunction;
+ m_pCallbackData = pData;
+ }
+}
+
+void EventPipeProvider::UnregisterCallback(EventPipeCallback pCallbackFunction)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Take the config lock before setting the callback.
+ CrstHolder _crst(EventPipe::GetLock());
+
+ if(m_pCallbackFunction == pCallbackFunction)
+ {
+ m_pCallbackFunction = NULL;
+ m_pCallbackData = NULL;
+ }
+}
+
+void EventPipeProvider::InvokeCallback()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
+ }
+ CONTRACTL_END;
+
+ if(m_pCallbackFunction != NULL)
+ {
+ (*m_pCallbackFunction)(
+ &m_providerID,
+ m_enabled,
+ (UCHAR) m_providerLevel,
+ m_keywords,
+ 0 /* matchAllKeywords */,
+ NULL /* FilterData */,
+ m_pCallbackData /* CallbackContext */);
+ }
+}
+
+void EventPipeProvider::RefreshAllEvents()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
+ }
+ CONTRACTL_END;
+
+ SListElem<EventPipeEvent*> *pElem = m_pEventList->GetHead();
+ while(pElem != NULL)
+ {
+ EventPipeEvent *pEvent = pElem->GetValue();
+ pEvent->RefreshState();
+
+ pElem = m_pEventList->GetNext(pElem);
+ }
+}
+
+#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipeprovider.h b/src/vm/eventpipeprovider.h
new file mode 100644
index 0000000000..610d76d8bf
--- /dev/null
+++ b/src/vm/eventpipeprovider.h
@@ -0,0 +1,106 @@
+// 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.
+
+#ifndef __EVENTPIPE_PROVIDER_H__
+#define __EVENTPIPE_PROVIDER_H__
+
+#ifdef FEATURE_PERFTRACING
+
+#include "slist.h"
+
+class EventPipeEvent;
+
+// Define the event pipe callback to match the ETW callback signature.
+typedef void (*EventPipeCallback)(
+ LPCGUID SourceID,
+ ULONG IsEnabled,
+ UCHAR Level,
+ ULONGLONG MatchAnyKeywords,
+ ULONGLONG MatchAllKeywords,
+ void *FilterData,
+ void *CallbackContext);
+
+enum class EventPipeEventLevel
+{
+ LogAlways,
+ Critical,
+ Error,
+ Warning,
+ Informational,
+ Verbose
+};
+
+class EventPipeProvider
+{
+ // Declare friends.
+ friend class EventPipeConfiguration;
+
+private:
+ // The GUID of the provider.
+ GUID m_providerID;
+
+ // True if the provider is enabled.
+ bool m_enabled;
+
+ // Bit vector containing the currently enabled keywords.
+ INT64 m_keywords;
+
+ // The current verbosity of the provider.
+ EventPipeEventLevel m_providerLevel;
+
+ // List of every event currently associated with the provider.
+ // New events can be added on-the-fly.
+ SList<SListElem<EventPipeEvent*>> *m_pEventList;
+
+ // The optional provider callback.
+ EventPipeCallback m_pCallbackFunction;
+
+ // The optional provider callback data pointer.
+ void *m_pCallbackData;
+
+public:
+
+ EventPipeProvider(const GUID &providerID);
+ ~EventPipeProvider();
+
+ // Get the provider ID.
+ const GUID& GetProviderID() const;
+
+ // Determine if the provider is enabled.
+ bool Enabled() const;
+
+ // Determine if the specified keywords are enabled.
+ bool EventEnabled(INT64 keywords) const;
+
+ // Determine if the specified keywords and level match the configuration.
+ bool EventEnabled(INT64 keywords, EventPipeEventLevel eventLevel) const;
+
+ // Create a new event.
+ EventPipeEvent* AddEvent(INT64 keywords, unsigned int eventID, unsigned int eventVersion, EventPipeEventLevel level, bool needStack);
+
+ // Register a callback with the provider to be called on state change.
+ void RegisterCallback(EventPipeCallback pCallbackFunction, void *pData);
+
+ // Unregister a callback.
+ void UnregisterCallback(EventPipeCallback pCallbackFunction);
+
+private:
+
+ // Add an event to the provider.
+ void AddEvent(EventPipeEvent &event);
+
+ // Set the provider configuration (enable and disable sets of events).
+ // This is called by EventPipeConfiguration.
+ void SetConfiguration(bool providerEnabled, INT64 keywords, EventPipeEventLevel providerLevel);
+
+ // Refresh the runtime state of all events.
+ void RefreshAllEvents();
+
+ // Invoke the provider callback.
+ void InvokeCallback();
+};
+
+#endif // FEATURE_PERFTRACING
+
+#endif // __EVENTPIPE_PROVIDER_H__
diff --git a/src/vm/fastserializableobject.h b/src/vm/fastserializableobject.h
new file mode 100644
index 0000000000..cbfcfc9f0e
--- /dev/null
+++ b/src/vm/fastserializableobject.h
@@ -0,0 +1,32 @@
+// 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.
+
+#ifndef __FASTSERIALIZABLE_OBJECT_H__
+#define __FASTSERIALIZABLE_OBJECT_H__
+
+#ifdef FEATURE_PERFTRACING
+
+class FastSerializer;
+
+class FastSerializableObject
+{
+
+public:
+
+ // Virtual destructor to ensure that derived class destructors get called.
+ virtual ~FastSerializableObject()
+ {
+ LIMITED_METHOD_CONTRACT;
+ }
+
+ // Serialize the object using the specified serializer.
+ virtual void FastSerialize(FastSerializer *pSerializer) = 0;
+
+ // Get the type name for the current object.
+ virtual const char* GetTypeName() = 0;
+};
+
+#endif // FEATURE_PERFTRACING
+
+#endif // _FASTSERIALIZABLE_OBJECT_H__
diff --git a/src/vm/fastserializer.cpp b/src/vm/fastserializer.cpp
new file mode 100644
index 0000000000..7f9b4e20a6
--- /dev/null
+++ b/src/vm/fastserializer.cpp
@@ -0,0 +1,337 @@
+// 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.
+
+#include "common.h"
+#include "fastserializer.h"
+
+#ifdef FEATURE_PERFTRACING
+
+FastSerializer::FastSerializer(SString &outputFilePath, FastSerializableObject &object)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ m_writeErrorEncountered = false;
+ m_pEntryObject = &object;
+ m_currentPos = 0;
+ m_nextForwardReference = 0;
+ m_pFileStream = new CFileStream();
+ if(FAILED(m_pFileStream->OpenForWrite(outputFilePath)))
+ {
+ delete(m_pFileStream);
+ m_pFileStream = NULL;
+ return;
+ }
+
+ // Write the file header.
+ WriteFileHeader();
+
+ // Write the entry object.
+ WriteEntryObject();
+}
+
+FastSerializer::~FastSerializer()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Write the end of the entry object.
+ WriteTag(FastSerializerTags::EndObject);
+
+ // Write forward reference table.
+ StreamLabel forwardReferenceLabel = WriteForwardReferenceTable();
+
+ // Write trailer.
+ WriteTrailer(forwardReferenceLabel);
+
+ if(m_pFileStream != NULL)
+ {
+ delete(m_pFileStream);
+ m_pFileStream = NULL;
+ }
+}
+
+StreamLabel FastSerializer::GetStreamLabel() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return (StreamLabel)m_currentPos;
+}
+
+void FastSerializer::WriteObject(FastSerializableObject *pObject)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(pObject != NULL);
+ }
+ CONTRACTL_END;
+
+ // Write a BeginObject tag.
+ WriteTag(FastSerializerTags::BeginObject);
+
+ // Write object begin tag.
+ WriteSerializationType(pObject);
+
+ // Ask the object to serialize itself using the current serializer.
+ pObject->FastSerialize(this);
+
+ // Write object end tag.
+ WriteTag(FastSerializerTags::EndObject);
+}
+
+void FastSerializer::WriteBuffer(BYTE *pBuffer, unsigned int length)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ PRECONDITION(pBuffer != NULL);
+ PRECONDITION(length > 0);
+ }
+ CONTRACTL_END;
+
+ if(m_writeErrorEncountered || m_pFileStream == NULL)
+ {
+ return;
+ }
+
+ EX_TRY
+ {
+ ULONG outCount;
+ m_pFileStream->Write(pBuffer, length, &outCount);
+
+#ifdef _DEBUG
+ size_t prevPos = m_currentPos;
+#endif
+ m_currentPos += outCount;
+#ifdef _DEBUG
+ _ASSERTE(prevPos < m_currentPos);
+#endif
+
+ if (length != outCount)
+ {
+ // This will cause us to stop writing to the file.
+ // The file will still remain open until shutdown so that we don't have to take a lock at this level when we touch the file stream.
+ m_writeErrorEncountered = true;
+ }
+ }
+ EX_CATCH
+ {
+ m_writeErrorEncountered = true;
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+}
+
+void FastSerializer::WriteEntryObject()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ }
+ CONTRACTL_END;
+
+ // Write begin entry object tag.
+ WriteTag(FastSerializerTags::BeginObject);
+
+ // Write the type information for the entry object.
+ WriteSerializationType(m_pEntryObject);
+
+ // The object is now initialized. Fields or other objects can now be written.
+}
+
+unsigned int FastSerializer::AllocateForwardReference()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ PRECONDITION(m_nextForwardReference < MaxForwardReferences);
+ }
+ CONTRACTL_END;
+
+ // TODO: Handle failure.
+
+ // Save the index.
+ int index = m_nextForwardReference;
+
+ // Allocate the forward reference and zero-fill it so that the reader
+ // will know if it was not properly defined.
+ m_forwardReferences[m_nextForwardReference++] = 0;
+
+ return index;
+}
+
+void FastSerializer::DefineForwardReference(unsigned int index, StreamLabel value)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ PRECONDITION(index < MaxForwardReferences-1);
+ }
+ CONTRACTL_END;
+
+ m_forwardReferences[index] = value;
+}
+
+void FastSerializer::WriteForwardReference(unsigned int index)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ PRECONDITION(index < MaxForwardReferences-1);
+ }
+ CONTRACTL_END;
+
+ WriteBuffer((BYTE*)&index, sizeof(index));
+}
+
+void FastSerializer::WriteSerializationType(FastSerializableObject *pObject)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ PRECONDITION(pObject != NULL);
+ }
+ CONTRACTL_END;
+
+ // Write the BeginObject tag.
+ WriteTag(FastSerializerTags::BeginObject);
+
+ // Write a NullReferenceTag, which implies that the following fields belong to SerializationType.
+ WriteTag(FastSerializerTags::NullReference);
+
+ // Write the SerializationType version fields.
+ int serializationType[2];
+ serializationType[0] = 1; // Object Version.
+ serializationType[1] = 0; // Minimum Reader Version.
+ WriteBuffer((BYTE*) &serializationType, sizeof(serializationType));
+
+ // Write the SerializationType TypeName field.
+ const char *strTypeName = pObject->GetTypeName();
+ unsigned int length = (unsigned int)strlen(strTypeName);
+ WriteString(strTypeName, length);
+
+ // Write the EndObject tag.
+ WriteTag(FastSerializerTags::EndObject);
+}
+
+
+void FastSerializer::WriteTag(FastSerializerTags tag, BYTE *payload, unsigned int payloadLength)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ }
+ CONTRACTL_END;
+
+ WriteBuffer((BYTE *)&tag, sizeof(tag));
+ if(payload != NULL)
+ {
+ _ASSERTE(payloadLength > 0);
+ WriteBuffer(payload, payloadLength);
+ }
+}
+
+
+void FastSerializer::WriteFileHeader()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ const char *strSignature = "!FastSerialization.1";
+ unsigned int length = (unsigned int)strlen(strSignature);
+ WriteString(strSignature, length);
+}
+
+void FastSerializer::WriteString(const char *strContents, unsigned int length)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ }
+ CONTRACTL_END;
+
+ // Write the string length .
+ WriteBuffer((BYTE*) &length, sizeof(length));
+
+ // Write the string contents.
+ WriteBuffer((BYTE*) strContents, length);
+}
+
+StreamLabel FastSerializer::WriteForwardReferenceTable()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ }
+ CONTRACTL_END;
+
+ // Save the position of the start of the forward references table.
+ StreamLabel current = GetStreamLabel();
+
+ // Write the count of allocated references.
+ WriteBuffer((BYTE*) &m_nextForwardReference, sizeof(m_nextForwardReference));
+
+ // Write each of the allocated references.
+ WriteBuffer((BYTE*) m_forwardReferences, sizeof(StreamLabel) * m_nextForwardReference);
+
+ return current;
+}
+
+void FastSerializer::WriteTrailer(StreamLabel forwardReferencesTableStart)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ }
+ CONTRACTL_END;
+
+ // Get the current location to mark the beginning of the trailer.
+ StreamLabel current = GetStreamLabel();
+
+ // Write the trailer, which contains the start of the forward references table.
+ WriteBuffer((BYTE*) &forwardReferencesTableStart, sizeof(forwardReferencesTableStart));
+
+ // Write the location of the trailer. This is the final piece of data written to the file,
+ // so that it can be easily found by a reader that can seek to the end of the file.
+ WriteBuffer((BYTE*) &current, sizeof(current));
+}
+
+#endif // FEATURE_PERFTRACING
diff --git a/src/vm/fastserializer.h b/src/vm/fastserializer.h
new file mode 100644
index 0000000000..5fd2cfd4a5
--- /dev/null
+++ b/src/vm/fastserializer.h
@@ -0,0 +1,74 @@
+// 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.
+
+#ifndef __FASTSERIALIZER_H__
+#define __FASTSERIALIZER_H__
+
+#ifdef FEATURE_PERFTRACING
+
+#include "fastserializableobject.h"
+#include "fstream.h"
+
+class FastSerializer;
+
+typedef unsigned int StreamLabel;
+
+enum class FastSerializerTags : BYTE
+{
+ Error, // To improve debugabilty, 0 is an illegal tag.
+ NullReference, // Tag for a null object forwardReference.
+ ObjectReference, // Followed by StreamLabel
+ ForwardReference, // Followed by an index (32-bit integer) into the Forward forwardReference array and a Type object
+ BeginObject, // Followed by Type object, object data, tagged EndObject
+ BeginPrivateObject, // Like beginObject, but not placed in interning table on deserialiation
+ EndObject, // Placed after an object to mark its end.
+ ForwardDefinition, // Followed by a forward forwardReference index and an object definition (BeginObject)
+ Byte,
+ Int16,
+ Int32,
+ Int64,
+ SkipRegion,
+ String,
+ Limit, // Just past the last valid tag, used for asserts.
+};
+
+class FastSerializer
+{
+public:
+
+ FastSerializer(SString &outputFilePath, FastSerializableObject &object);
+ ~FastSerializer();
+
+ StreamLabel GetStreamLabel() const;
+
+ void WriteObject(FastSerializableObject *pObject);
+ void WriteBuffer(BYTE *pBuffer, unsigned int length);
+ void WriteTag(FastSerializerTags tag, BYTE *payload = NULL, unsigned int payloadLength = 0);
+ void WriteString(const char *strContents, unsigned int length);
+
+ unsigned int AllocateForwardReference();
+ void DefineForwardReference(unsigned int index, StreamLabel value);
+ void WriteForwardReference(unsigned int index);
+
+private:
+
+ void WriteEntryObject();
+ void WriteSerializationType(FastSerializableObject *pObject);
+ void WriteFileHeader();
+ StreamLabel WriteForwardReferenceTable();
+ void WriteTrailer(StreamLabel forwardReferencesTableStart);
+
+ CFileStream *m_pFileStream;
+ bool m_writeErrorEncountered;
+ FastSerializableObject *m_pEntryObject;
+ size_t m_currentPos;
+
+ static const unsigned int MaxForwardReferences = 100;
+ StreamLabel m_forwardReferences[MaxForwardReferences];
+ unsigned int m_nextForwardReference;
+};
+
+#endif // FEATURE_PERFTRACING
+
+#endif // __FASTSERIALIZER_H__
diff --git a/src/vm/sampleprofiler.cpp b/src/vm/sampleprofiler.cpp
index 004b3c68b0..6a6a23a03b 100644
--- a/src/vm/sampleprofiler.cpp
+++ b/src/vm/sampleprofiler.cpp
@@ -3,18 +3,23 @@
// See the LICENSE file in the project root for more information.
#include "common.h"
+#include "eventpipeeventinstance.h"
#include "sampleprofiler.h"
#include "hosting.h"
#include "threadsuspend.h"
+#ifdef FEATURE_PERFTRACING
+
Volatile<BOOL> SampleProfiler::s_profilingEnabled = false;
Thread* SampleProfiler::s_pSamplingThread = NULL;
+const GUID SampleProfiler::s_providerID = {0x3c530d44,0x97ae,0x513a,{0x1e,0x6d,0x78,0x3e,0x8f,0x8e,0x03,0xa9}}; // {3c530d44-97ae-513a-1e6d-783e8f8e03a9}
+EventPipeProvider* SampleProfiler::s_pEventPipeProvider = NULL;
+EventPipeEvent* SampleProfiler::s_pThreadTimeEvent = NULL;
CLREventStatic SampleProfiler::s_threadShutdownEvent;
#ifdef FEATURE_PAL
long SampleProfiler::s_samplingRateInNs = 1000000; // 1ms
#endif
-// Synchronization of multiple callers occurs in EventPipe::Enable.
void SampleProfiler::Enable()
{
CONTRACTL
@@ -23,9 +28,22 @@ void SampleProfiler::Enable()
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(s_pSamplingThread == NULL);
+ // Synchronization of multiple callers occurs in EventPipe::Enable.
+ PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
}
CONTRACTL_END;
+ if(s_pEventPipeProvider == NULL)
+ {
+ s_pEventPipeProvider = new EventPipeProvider(s_providerID);
+ s_pThreadTimeEvent = s_pEventPipeProvider->AddEvent(
+ 0, /* keywords */
+ 0, /* eventID */
+ 0, /* eventVersion */
+ EventPipeEventLevel::Informational,
+ false /* NeedStack */);
+ }
+
s_profilingEnabled = true;
s_pSamplingThread = SetupUnstartedThread();
if(s_pSamplingThread->CreateNewThread(0, ThreadProc, NULL))
@@ -40,7 +58,6 @@ void SampleProfiler::Enable()
}
}
-// Synchronization of multiple callers occurs in EventPipe::Disable.
void SampleProfiler::Disable()
{
CONTRACTL
@@ -48,6 +65,8 @@ void SampleProfiler::Disable()
THROWS;
GC_TRIGGERS;
MODE_ANY;
+ // Synchronization of multiple callers occurs in EventPipe::Disable.
+ PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
}
CONTRACTL_END;
@@ -140,16 +159,20 @@ void SampleProfiler::WalkManagedThreads()
CONTRACTL_END;
Thread *pThread = NULL;
- StackContents stackContents;
// Iterate over all managed threads.
// Assumes that the ThreadStoreLock is held because we've suspended all threads.
while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL)
{
+ SampleProfilerEventInstance instance(pThread);
+ StackContents &stackContents = *(instance.GetStack());
+
// Walk the stack and write it out as an event.
if(EventPipe::WalkManagedStackForThread(pThread, stackContents) && !stackContents.IsEmpty())
{
- EventPipe::WriteSampleProfileEvent(pThread, stackContents);
+ EventPipe::WriteSampleProfileEvent(instance);
}
}
}
+
+#endif // FEATURE_PERFTRACING
diff --git a/src/vm/sampleprofiler.h b/src/vm/sampleprofiler.h
index a5c5fc3ffd..5ad388d8ff 100644
--- a/src/vm/sampleprofiler.h
+++ b/src/vm/sampleprofiler.h
@@ -5,11 +5,17 @@
#ifndef __SAMPLEPROFILER_H__
#define __SAMPLEPROFILER_H__
+#ifdef FEATURE_PERFTRACING
+
#include "common.h"
#include "eventpipe.h"
class SampleProfiler
{
+
+ // Declare friends.
+ friend class SampleProfilerEventInstance;
+
public:
// Enable profiling.
@@ -32,6 +38,11 @@ class SampleProfiler
// The sampling thread.
static Thread *s_pSamplingThread;
+ // The provider and event emitted by the profiler.
+ static const GUID s_providerID;
+ static EventPipeProvider *s_pEventPipeProvider;
+ static EventPipeEvent *s_pThreadTimeEvent;
+
// Thread shutdown event for synchronization between Disable() and the sampling thread.
static CLREventStatic s_threadShutdownEvent;
@@ -41,4 +52,6 @@ class SampleProfiler
#endif
};
+#endif // FEATURE_PERFTRACING
+
#endif // __SAMPLEPROFILER_H__