Log Events to EventPipe on Linux (#11433)
authorBrian Robbins <brianrob@microsoft.com>
Sat, 6 May 2017 19:36:08 +0000 (12:36 -0700)
committerVance Morrison <vancem@microsoft.com>
Sat, 6 May 2017 19:36:08 +0000 (12:36 -0700)
* 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.

27 files changed:
build.sh
clrdefinitions.cmake
src/dlls/mscoree/coreclr/CMakeLists.txt
src/scripts/genEventPipe.py [new file with mode: 0644]
src/scripts/genXplatEventing.py
src/scripts/genXplatLttng.py
src/vm/CMakeLists.txt
src/vm/ceemain.cpp
src/vm/eventpipe.cpp
src/vm/eventpipe.h
src/vm/eventpipeconfiguration.cpp [new file with mode: 0644]
src/vm/eventpipeconfiguration.h [new file with mode: 0644]
src/vm/eventpipeevent.cpp [new file with mode: 0644]
src/vm/eventpipeevent.h [new file with mode: 0644]
src/vm/eventpipeeventinstance.cpp [new file with mode: 0644]
src/vm/eventpipeeventinstance.h [new file with mode: 0644]
src/vm/eventpipefile.cpp [new file with mode: 0644]
src/vm/eventpipefile.h [new file with mode: 0644]
src/vm/eventpipejsonfile.cpp
src/vm/eventpipejsonfile.h
src/vm/eventpipeprovider.cpp [new file with mode: 0644]
src/vm/eventpipeprovider.h [new file with mode: 0644]
src/vm/fastserializableobject.h [new file with mode: 0644]
src/vm/fastserializer.cpp [new file with mode: 0644]
src/vm/fastserializer.h [new file with mode: 0644]
src/vm/sampleprofiler.cpp
src/vm/sampleprofiler.h

index 12b7b72..39e96dd 100755 (executable)
--- 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()
index f15749e..4dacae9 100644 (file)
@@ -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)
index afa253f..7a4617f 100644 (file)
@@ -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 (file)
index 0000000..3a818ce
--- /dev/null
@@ -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)
index 6c6498d..6968d29 100644 (file)
@@ -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):
index 6dd60b0..fae0e12 100644 (file)
@@ -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;
index ec4ff42..556eb0e 100644 (file)
@@ -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)
index 9cce46d..c82f7d4 100644 (file)
@@ -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
 
index 98d382e..8ea3f08 100644 (file)
@@ -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
index 2978412..c6d7f61 100644 (file)
@@ -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 (file)
index 0000000..cfb96fc
--- /dev/null
@@ -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 (file)
index 0000000..a237775
--- /dev/null
@@ -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 (file)
index 0000000..3b9f36b
--- /dev/null
@@ -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 (file)
index 0000000..9e42615
--- /dev/null
@@ -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 (file)
index 0000000..2bf500b
--- /dev/null
@@ -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 (file)
index 0000000..84ad566
--- /dev/null
@@ -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 (file)
index 0000000..895f732
--- /dev/null
@@ -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 (file)
index 0000000..1fbb4c0
--- /dev/null
@@ -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__
index 6353c91..ea2dd29 100644 (file)
@@ -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
index b6e42de..2b836d2 100644 (file)
@@ -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 (file)
index 0000000..da18533
--- /dev/null
@@ -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 (file)
index 0000000..610d76d
--- /dev/null
@@ -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 (file)
index 0000000..cbfcfc9
--- /dev/null
@@ -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 (file)
index 0000000..7f9b4e2
--- /dev/null
@@ -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 (file)
index 0000000..5fd2cfd
--- /dev/null
@@ -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__
index 004b3c6..6a6a23a 100644 (file)
@@ -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
index a5c5fc3..5ad388d 100644 (file)
@@ -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__