1
0
Fork 0
mirror of https://github.com/VSadov/Satori.git synced 2025-06-09 09:34:49 +09:00

Add user_events support for the native runtime events (#102523)

This commit is contained in:
David Mason 2024-06-19 09:41:10 -07:00 committed by GitHub
parent 796a79fc4c
commit 7934e64492
Signed by: github
GPG key ID: B5690EEEBB952194
128 changed files with 30478 additions and 26 deletions

View file

@ -115,6 +115,33 @@ Altered source versions must be plainly marked as such, and must not be misrepre
This notice may not be removed or altered from any source distribution.
License notice for LinuxTracepoints
-----------------------------------
https://github.com/microsoft/LinuxTracepoints/blob/main/LICENSE
Copyright (c) Microsoft Corporation.
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
License notice for Mono
-------------------------------

View file

@ -0,0 +1,20 @@
# user_events support in the runtime
Historically only kernelspace code was allowed to emit events in the Linux kernel, meaning that programs like Perf could only collect system events. Over the years various libraries such as LTTng have been created to allow userspace applications to write to the same trace as the kernel code. The runtime has supported LTTng since very early on, but LTTng has some limitations that are problematic. LTTng requires that all providers and events are known at compile time rather than runtime, and has also recently broken their ABI in a way that is difficult to recover from.
Starting with kernel version 6.4 the user_events feature is available. user_events allows userspace applications to write events to the same traces as kernel events but does not have the limitations of LTTng. It allows dynamic event creation at runtime and has a stable ABI. For this reason we are adding support for user_events to the runtime in .net 9.
# Limitations
Currently the support for user_events is experimental and does not support managed EventSources, it only supports native runtime events such as JIT, GC, class loads, etc.
# How to enable
The support for user_events is off by default and can be enabled in one of two ways.
1. Setting the `DOTNET_EnableUserEvents` environment variable to the value `1`.
2. Setting the `System.Diagnostics.Tracing.UserEvents` configuration value to `true` in either your project file or in your `runtimeconfig.json` file.
# Format
The events are written with the EventHeader format specified at https://github.com/microsoft/LinuxTracepoints/blob/main/libeventheader-tracepoint/include/eventheader/eventheader.h

View file

@ -174,6 +174,14 @@ if(FEATURE_EVENT_TRACE)
endif(CLR_CMAKE_HOST_UNIX)
endif(FEATURE_EVENT_TRACE)
if(FEATURE_PERFTRACING)
if(CLR_CMAKE_TARGET_LINUX)
list(APPEND CORECLR_LIBRARIES
usereventsprovider
)
endif(CLR_CMAKE_TARGET_LINUX)
endif(FEATURE_PERFTRACING)
if(FEATURE_MERGE_JIT_AND_ENGINE)
set(CLRJIT_STATIC clrjit_static)
endif(FEATURE_MERGE_JIT_AND_ENGINE)

View file

@ -690,6 +690,11 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_EventPipeProcNumbers, W("EventPipeProcNumbers"
RETAIL_CONFIG_DWORD_INFO(INTERNAL_EventPipeOutputStreaming, W("EventPipeOutputStreaming"), 1, "Enable/disable streaming for trace file set in DOTNET_EventPipeOutputPath. Non-zero values enable streaming.")
RETAIL_CONFIG_DWORD_INFO(INTERNAL_EventPipeEnableStackwalk, W("EventPipeEnableStackwalk"), 1, "Set to 0 to disable collecting stacks for EventPipe events.")
//
// UserEvents
//
RETAIL_CONFIG_DWORD_INFO(INTERNAL_EnableUserEvents, W("EnableUserEvents"), 0, "Enable/disable writing events to user_events. Non-zero values enable tracing.")
#ifdef FEATURE_AUTO_TRACE
RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_AutoTrace_N_Tracers, W("AutoTrace_N_Tracers"), 0, "", CLRConfig::LookupOptions::ParseIntegerAsBase10)
RETAIL_CONFIG_STRING_INFO(INTERNAL_AutoTrace_Command, W("AutoTrace_Command"), "")

View file

@ -121,12 +121,15 @@ enum EtwGCSettingFlags
#define ETW_TRACING_INITIALIZED(RegHandle) (TRUE)
#define ETW_EVENT_ENABLED(Context, EventDescriptor) (EventPipeHelper::IsEnabled(Context, EventDescriptor.Level, EventDescriptor.Keyword) || \
(XplatEventLogger::IsKeywordEnabled(Context, EventDescriptor.Level, EventDescriptor.Keyword)))
(XplatEventLogger::IsKeywordEnabled(Context, EventDescriptor.Level, EventDescriptor.Keyword)) || \
(UserEventsHelper::IsEnabled(Context, EventDescriptor.Level, EventDescriptor.Keyword)))
#define ETW_CATEGORY_ENABLED(Context, Level, Keyword) (EventPipeHelper::IsEnabled(Context, Level, Keyword) || \
(XplatEventLogger::IsKeywordEnabled(Context, Level, Keyword)))
(XplatEventLogger::IsKeywordEnabled(Context, Level, Keyword)) || \
(UserEventsHelper::IsEnabled(Context, Level, Keyword)))
#define ETW_TRACING_ENABLED(Context, EventDescriptor) (EventEnabled##EventDescriptor())
#define ETW_TRACING_CATEGORY_ENABLED(Context, Level, Keyword) (EventPipeHelper::IsEnabled(Context, Level, Keyword) || \
(XplatEventLogger::IsKeywordEnabled(Context, Level, Keyword)))
(XplatEventLogger::IsKeywordEnabled(Context, Level, Keyword)) || \
(UserEventsHelper::IsEnabled(Context, Level, Keyword)))
#define ETW_PROVIDER_ENABLED(ProviderSymbol) (TRUE)
#else //defined(FEATURE_PERFTRACING)
#define ETW_INLINE
@ -651,6 +654,13 @@ public:
static bool Enabled();
static bool IsEnabled(DOTNET_TRACE_CONTEXT Context, UCHAR Level, ULONGLONG Keyword);
};
class UserEventsHelper
{
public:
static bool Enabled();
static bool IsEnabled(DOTNET_TRACE_CONTEXT Context, UCHAR Level, ULONGLONG Keyword);
};
#endif // defined(FEATURE_PERFTRACING)
#endif // FEATURE_EVENT_TRACE

View file

@ -1,3 +1,9 @@
#
## Licensed to the .NET Foundation under one or more agreements.
## The .NET Foundation licenses this file to you under the MIT license.
#
#
from __future__ import print_function
from genEventing import *
from genLttngProvider import *
@ -1189,16 +1195,6 @@ def generateEventPipeImplFiles(
etwmanifest, eventpipe_directory, extern, target_cpp, runtimeFlavor, inclusionList, exclusionList, dryRun):
tree = DOM.parse(etwmanifest)
# Find the src directory starting with the assumption that
# A) It is named 'src'
# B) This script lives in it
src_dirname = os.path.dirname(__file__)
while os.path.basename(src_dirname) != "src":
src_dirname = os.path.dirname(src_dirname)
if os.path.basename(src_dirname) == "":
raise IOError("Could not find the Core CLR 'src' directory")
for providerNode in tree.getElementsByTagName('provider'):
providerName = providerNode.getAttribute('name')
if not includeProvider(providerName, runtimeFlavor):

View file

@ -12,6 +12,7 @@
from __future__ import print_function
import os
import sys
import xml.dom.minidom as DOM
from utilities import open_for_update, parseInclusionList
@ -80,6 +81,38 @@ coreCLREventPipeDataTypeMapping={
"BYTE" : "BYTE",
}
coreCLRUserEventDataTypeMapping={
"win:UInt8" : "TraceLoggingUInt8",
"win:UInt16" : "TraceLoggingUInt16",
"win:Int32" : "TraceLoggingInt32",
"win:UInt32" : "TraceLoggingUInt32",
"win:HexInt32" : "TraceLoggingHexInt32",
"win:UInt64" : "TraceLoggingUInt64",
"win:Int64" : "TraceLoggingInt64",
"win:HexInt64" : "TraceLoggingHexInt64",
"win:Pointer" : "TraceLoggingPointer",
"win:Boolean" : "TraceLoggingBoolean",
"win:UnicodeString" : "TraceLoggingString16",
"win:GUID" : "TraceLoggingGuid",
"win:Binary" : "TraceLoggingBinary",
"win:Double" : "TraceLoggingFloat64",
"win:AnsiString" : "TraceLoggingString",
}
coreCLRUserEventLogLevelMapping={
"win:LogAlways" : "0",
"win:Critical" : "1",
"win:Error" : "2",
"win:Warning" : "3",
"win:Informational" : "4",
"win:Verbose" : "5",
}
coreCLRUserEventArrayTypeMapping={
"win:UInt32" : "TraceLoggingUInt32Array",
"win:Binary" : "TraceLoggingUInt8Array",
"win:UInt64" : "TraceLoggingUInt64Array",
}
monoPalDataTypeMapping={
#constructed types
"win:null" :" ",
@ -143,6 +176,29 @@ aotEventPipeDataTypeMapping={
"WCHAR" : "WCHAR",
"BYTE" : "BYTE",
}
def getUserEventDataTypeMapping(runtimeFlavor):
if runtimeFlavor.coreclr:
return coreCLRUserEventDataTypeMapping
# elif runtimeFlavor.mono:
# return monoUserEventDataTypeMapping
# elif runtimeFlavor.nativeaot:
# return aotUserEventDataTypeMapping
def getUserEventLogLevelMapping(runtimeFlavor):
if runtimeFlavor.coreclr:
return coreCLRUserEventLogLevelMapping
# elif runtimeFlavor.mono:
# return monoUserEventLogLevelMapping
# elif runtimeFlavor.nativeaot:
# return aotUserEventLogLevelMapping
def getArrayDataTypeMapping(runtimeFlavor):
if runtimeFlavor.coreclr:
return coreCLRUserEventArrayTypeMapping
# elif runtimeFlavor.mono:
# return monoUserEventArrayTypeMapping
# elif runtimeFlavor.nativeaot:
# return aotUserEventArrayTypeMapping
def getEventPipeDataTypeMapping(runtimeFlavor):
if runtimeFlavor.coreclr:
@ -160,6 +216,7 @@ def getPalDataTypeMapping(runtimeFlavor):
elif runtimeFlavor.nativeaot:
return aotPalDataTypeMapping
def includeProvider(providerName, runtimeFlavor):
if (runtimeFlavor.coreclr or runtimeFlavor.nativeaot) and providerName == "Microsoft-DotNETRuntimeMonoProfiler":
return False
@ -410,7 +467,7 @@ def parseTemplateNodes(templateNodes):
return allTemplates
def generateClrallEvents(eventNodes, allTemplates, target_cpp, runtimeFlavor, write_xplatheader, providerName, inclusionList, generatedFileType):
def generateClrallEvents(eventNodes, allTemplates, target_cpp, runtimeFlavor, write_xplatheader, providerName, inclusionList, generatedFileType, user_events):
clrallEvents = []
for eventNode in eventNodes:
eventName = eventNode.getAttribute('symbol')
@ -439,6 +496,8 @@ def generateClrallEvents(eventNodes, allTemplates, target_cpp, runtimeFlavor, wr
if runtimeFlavor.coreclr or write_xplatheader or runtimeFlavor.nativeaot:
if os.name == 'posix':
if user_events and runtimeFlavor.coreclr:
clrallEvents.append(" || UserEventsEventEnabled" + eventName + "()")
# native AOT does not support non-windows eventing other than via event pipe
if not runtimeFlavor.nativeaot:
clrallEvents.append(" || (XplatEventLogger" +
@ -533,6 +592,13 @@ def generateClrallEvents(eventNodes, allTemplates, target_cpp, runtimeFlavor, wr
fnbody.append("ActivityId,RelatedActivityId);\n")
if user_events and runtimeFlavor.coreclr:
fnbody.append(lindent)
fnbody.append("status &= UserEventsWriteEvent" + eventName + "(" + ''.join(line))
if len(line) > 0:
fnbody.append(",")
fnbody.append("ActivityId,RelatedActivityId);\n")
if runtimeFlavor.coreclr or write_xplatheader:
fnbody.append(lindent)
fnbody.append("status &= FireEtXplat" + eventName + "(" + ''.join(line) + ");\n")
@ -667,6 +733,63 @@ def generateClrEventPipeWriteEvents(eventNodes, allTemplates, extern, target_cpp
return ''.join(clrallEvents)
def generateClrUserEventsWriteEvents(eventNodes, allTemplates, extern, target_cpp, runtimeFlavor, providerName, inclusion_list):
clrallEvents = []
for eventNode in eventNodes:
eventName = eventNode.getAttribute('symbol')
if not includeEvent(inclusion_list, providerName, eventName):
continue
templateName = eventNode.getAttribute('template')
#generate UserEventsEventEnabled and UserEventsWriteEvent functions
eventenabled = []
writeevent = []
fnptypeline = []
if extern:eventenabled.append('extern "C" ')
eventenabled.append("%s UserEventsEventEnabled" % (getEventPipeDataTypeMapping(runtimeFlavor)["BOOL"]))
eventenabled.append(eventName)
eventenabled.append("(void);\n")
if extern: writeevent.append('extern "C" ')
writeevent.append("%s UserEventsWriteEvent" % (getEventPipeDataTypeMapping(runtimeFlavor)["ULONG"]))
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 = getPalDataTypeMapping(runtimeFlavor)[wintypeName]
winCount = fnparam.count
countw = getPalDataTypeMapping(runtimeFlavor)[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")
fnptypeline.append(lindent)
fnptypeline.append("%s ActivityId%s\n" % (getEventPipeDataTypeMapping(runtimeFlavor)["LPCGUID"], " = nullptr," if target_cpp else ","))
fnptypeline.append(lindent)
fnptypeline.append("%s RelatedActivityId%s" % (getEventPipeDataTypeMapping(runtimeFlavor)["LPCGUID"], " = nullptr" if target_cpp else ""))
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 = []
@ -749,7 +872,7 @@ def getKeywordsMaskCombined(keywords, keywordsToMask):
return mask
def updateclreventsfile(write_xplatheader, target_cpp, runtimeFlavor, eventpipe_trace_context_typedef, dotnet_trace_context_typedef_windows, tree, clrallevents, inclusion_list, generatedFileType):
def updateclreventsfile(write_xplatheader, target_cpp, runtimeFlavor, eventpipe_trace_context_typedef, dotnet_trace_context_typedef_windows, user_events_trace_context_typedef, tree, clrallevents, inclusion_list, generatedFileType, user_events):
is_windows = os.name == 'nt'
with open_for_update(clrallevents) as Clrallevents:
Clrallevents.write(stdprolog)
@ -759,6 +882,8 @@ def updateclreventsfile(write_xplatheader, target_cpp, runtimeFlavor, eventpipe_
if runtimeFlavor.coreclr or write_xplatheader:
Clrallevents.write('#include "clrxplatevents.h"\n')
Clrallevents.write('#include "clreventpipewriteevents.h"\n')
if user_events and runtimeFlavor.coreclr:
Clrallevents.write('#include "clrusereventswriteevents.h"\n')
elif generatedFileType == "header":
Clrallevents.write('#ifndef CLR_ETW_ALL_MAIN_H\n')
Clrallevents.write('#define CLR_ETW_ALL_MAIN_H\n\n')
@ -767,6 +892,8 @@ def updateclreventsfile(write_xplatheader, target_cpp, runtimeFlavor, eventpipe_
Clrallevents.write('#include <PalRedhawk.h>\n')
Clrallevents.write('#include "clretwallmain.h"\n')
Clrallevents.write('#include "clreventpipewriteevents.h"\n')
if user_events and runtimeFlavor.coreclr:
Clrallevents.write('#include "clrusereventswriteevents.h"\n')
Clrallevents.write('#ifdef FEATURE_ETW\n')
Clrallevents.write('#include "ClrEtwAll.h"\n')
Clrallevents.write('#endif\n')
@ -787,6 +914,9 @@ def updateclreventsfile(write_xplatheader, target_cpp, runtimeFlavor, eventpipe_
else:
Clrallevents.write("\n")
if not is_windows and runtimeFlavor.coreclr:
Clrallevents.write(user_events_trace_context_typedef)
if not is_windows and not write_xplatheader and not runtimeFlavor.nativeaot:
Clrallevents.write(eventpipe_trace_context_typedef) # define EVENTPIPE_TRACE_CONTEXT
Clrallevents.write("\n")
@ -799,7 +929,7 @@ def updateclreventsfile(write_xplatheader, target_cpp, runtimeFlavor, eventpipe_
eventNodes = providerNode.getElementsByTagName('event')
#vm header:
Clrallevents.write(generateClrallEvents(eventNodes, allTemplates, target_cpp, runtimeFlavor, write_xplatheader, providerName, inclusion_list, generatedFileType))
Clrallevents.write(generateClrallEvents(eventNodes, allTemplates, target_cpp, runtimeFlavor, write_xplatheader, providerName, inclusion_list, generatedFileType, user_events))
providerName = providerNode.getAttribute('name')
providerSymbol = providerNode.getAttribute('symbol')
@ -814,7 +944,7 @@ def updateclreventsfile(write_xplatheader, target_cpp, runtimeFlavor, eventpipe_
if generatedFileType == "header":
Clrallevents.write("#endif // __CLR_ETW_ALL_MAIN_H__\n")
def generatePlatformIndependentFiles(sClrEtwAllMan, incDir, etmDummyFile, extern, write_xplatheader, target_cpp, runtimeFlavor, inclusion_list):
def generatePlatformIndependentFiles(sClrEtwAllMan, incDir, etmDummyFile, extern, write_xplatheader, target_cpp, runtimeFlavor, inclusion_list, user_events):
generateEtmDummyHeader(sClrEtwAllMan,etmDummyFile)
tree = DOM.parse(sClrEtwAllMan)
@ -838,6 +968,16 @@ typedef struct _EVENTPIPE_TRACE_CONTEXT
#endif // EVENTPIPE_TRACE_CONTEXT_DEF
""" % (getEventPipeDataTypeMapping(runtimeFlavor)["WCHAR"], getEventPipeDataTypeMapping(runtimeFlavor)["UCHAR"], getEventPipeDataTypeMapping(runtimeFlavor)["ULONGLONG"])
user_events_trace_context_typedef = """
#if !defined(USER_EVENTS_TRACE_CONTEXT_DEF)
#define USER_EVENTS_TRACE_CONTEXT_DEF
typedef struct _USER_EVENTS_TRACE_CONTEXT
{
UCHAR id;
} USER_EVENTS_TRACE_CONTEXT, *PUSER_EVENTS_TRACE_CONTEXT;
#endif // USER_EVENTS_TRACE_CONTEXT_DEF
"""
lttng_trace_context_typedef = """
#if !defined(LTTNG_TRACE_CONTEXT_DEF)
#define LTTNG_TRACE_CONTEXT_DEF
@ -869,6 +1009,7 @@ typedef struct _DOTNET_TRACE_CONTEXT
{
EVENTPIPE_TRACE_CONTEXT EventPipeProvider;
PLTTNG_TRACE_CONTEXT LttngProvider;
USER_EVENTS_TRACE_CONTEXT UserEventsProvider;
} DOTNET_TRACE_CONTEXT, *PDOTNET_TRACE_CONTEXT;
#endif // DOTNET_TRACE_CONTEXT_DEF
"""
@ -878,11 +1019,11 @@ typedef struct _DOTNET_TRACE_CONTEXT
# Write the main source(s) for FireETW* functions
# nativeaot requires header and source file to be separated as well as a noop implementation
if runtimeFlavor.nativeaot:
updateclreventsfile(write_xplatheader, target_cpp, runtimeFlavor, eventpipe_trace_context_typedef, dotnet_trace_context_typedef_windows, tree, os.path.join(incDir, "clretwallmain.cpp"), inclusion_list, "source-impl")
updateclreventsfile(write_xplatheader, target_cpp, runtimeFlavor, eventpipe_trace_context_typedef, dotnet_trace_context_typedef_windows, tree, os.path.join(incDir, "clretwallmain.h"), inclusion_list, "header")
updateclreventsfile(write_xplatheader, target_cpp, runtimeFlavor, eventpipe_trace_context_typedef, dotnet_trace_context_typedef_windows, tree, os.path.join(incDir, "disabledclretwallmain.cpp"), inclusion_list, "source-impl-noop")
updateclreventsfile(write_xplatheader, target_cpp, runtimeFlavor, eventpipe_trace_context_typedef, dotnet_trace_context_typedef_windows, user_events_trace_context_typedef, tree, os.path.join(incDir, "clretwallmain.cpp"), inclusion_list, "source-impl", user_events)
updateclreventsfile(write_xplatheader, target_cpp, runtimeFlavor, eventpipe_trace_context_typedef, dotnet_trace_context_typedef_windows, user_events_trace_context_typedef, tree, os.path.join(incDir, "clretwallmain.h"), inclusion_list, "header", user_events)
updateclreventsfile(write_xplatheader, target_cpp, runtimeFlavor, eventpipe_trace_context_typedef, dotnet_trace_context_typedef_windows, user_events_trace_context_typedef, tree, os.path.join(incDir, "disabledclretwallmain.cpp"), inclusion_list, "source-impl-noop", user_events)
else:
updateclreventsfile(write_xplatheader, target_cpp, runtimeFlavor, eventpipe_trace_context_typedef, dotnet_trace_context_typedef_windows, tree, os.path.join(incDir, "clretwallmain.h"), inclusion_list, "header-impl")
updateclreventsfile(write_xplatheader, target_cpp, runtimeFlavor, eventpipe_trace_context_typedef, dotnet_trace_context_typedef_windows, user_events_trace_context_typedef, tree, os.path.join(incDir, "clretwallmain.h"), inclusion_list, "header-impl", user_events)
if write_xplatheader:
clrproviders = os.path.join(incDir, "clrproviders.h")
@ -897,6 +1038,7 @@ typedef struct _EVENT_DESCRIPTOR
if not is_windows and not runtimeFlavor.nativeaot:
Clrproviders.write(eventpipe_trace_context_typedef) # define EVENTPIPE_TRACE_CONTEXT
Clrproviders.write(lttng_trace_context_typedef) # define LTTNG_TRACE_CONTEXT
Clrproviders.write(user_events_trace_context_typedef)
Clrproviders.write(dotnet_trace_context_typedef_unix + "\n")
allProviders = []
@ -955,6 +1097,20 @@ typedef struct _EVENT_DESCRIPTOR
#eventpipe: create clreventpipewriteevents.h
Clreventpipewriteevents.write(generateClrEventPipeWriteEvents(eventNodes, allTemplates, extern, target_cpp, runtimeFlavor, providerName, inclusion_list) + "\n")
if user_events and runtimeFlavor.coreclr:
clrusereventswriteeventsPath = os.path.join(incDir, "clrusereventswriteevents.h")
with open_for_update(clrusereventswriteeventsPath) as clrusereventswriteevents:
clrusereventswriteevents.write(stdprolog + "\n")
for providerNode in tree.getElementsByTagName('provider'):
providerName = providerNode.getAttribute('name')
templateNodes = providerNode.getElementsByTagName('template')
allTemplates = parseTemplateNodes(templateNodes)
eventNodes = providerNode.getElementsByTagName('event')
#user_events: create clrusereventswriteevents.h
clrusereventswriteevents.write(generateClrUserEventsWriteEvents(eventNodes, allTemplates, extern, target_cpp, runtimeFlavor, providerName, inclusion_list) + "\n")
# Write secondary headers for FireEtXplat* and EventPipe* functions
if write_xplatheader:
clrxplatevents = os.path.join(incDir, "clrxplatevents.h")
@ -992,6 +1148,8 @@ def main(argv):
help='if specified, will not generated extern function stub headers' )
required.add_argument('--noxplatheader', action='store_true',
help='if specified, will not write a generated cross-platform header' )
required.add_argument('--userevents', action='store_true',
help='if specified, will emit support for user_events' )
args, unknown = parser.parse_known_args(argv)
if unknown:
print('Unknown argument(s): ', ', '.join(unknown))
@ -1004,6 +1162,7 @@ def main(argv):
runtimeFlavor = RuntimeFlavor(args.runtimeflavor)
extern = not args.nonextern
write_xplatheader = not args.noxplatheader
user_events = args.userevents
target_cpp = True
if runtimeFlavor.mono:
@ -1013,7 +1172,7 @@ def main(argv):
inclusion_list = parseInclusionList(inclusion_filename)
generatePlatformIndependentFiles(sClrEtwAllMan, incdir, etmDummyFile, extern, write_xplatheader, target_cpp, runtimeFlavor, inclusion_list)
generatePlatformIndependentFiles(sClrEtwAllMan, incdir, etmDummyFile, extern, write_xplatheader, target_cpp, runtimeFlavor, inclusion_list, user_events)
if __name__ == '__main__':
return_code = main(sys.argv[1:])

View file

@ -0,0 +1,501 @@
#
## Licensed to the .NET Foundation under one or more agreements.
## The .NET Foundation licenses this file to you under the MIT license.
#
# This file generates support for the .net runtime to emit events via the
# EventHeader format specified at https://github.com/microsoft/LinuxTracepoints/blob/main/libeventheader-tracepoint/include/eventheader/eventheader.h
#
# It uses the LinuxTracepoints library included in this repo at src/native/External/LinuxTracepoints.
#
# The support is accomplished with the following:
#
# In clretwall.h all methods have user_events specific code added like the following.
# inline BOOL EventEnabledGCStart(void) {return EventPipeEventEnabledGCStart() || UserEventsEventEnabledGCStart() || (XplatEventLogger::IsEventLoggingEnabled() && EventXplatEnabledGCStart());}
#
# inline ULONG FireEtwGCStart(
# const unsigned int Count,
# const unsigned int Reason,
# LPCGUID ActivityId = nullptr,
# LPCGUID RelatedActivityId = nullptr
# )
# {
# ULONG status = EventPipeWriteEventGCStart(Count,Reason,ActivityId,RelatedActivityId);
# status &= UserEventsWriteEventGCStart(Count,Reason,ActivityId,RelatedActivityId);
# status &= FireEtXplatGCStart(Count,Reason);
# return status;
# }
#
# The clretwall.h file is generate in the genEventing.py script.
#
# This script outputs one file for each provider, with each event on the provider having a method for
# checking if the event is enabled and a method for firing the event.
# BOOL UserEventsEventEnabledGCStart(void)
# {
# return IsUserEventsEnabled() && TraceLoggingProviderEnabled(DotNETRuntime, 4, 1);
# }
#
# extern "C" ULONG UserEventsWriteEventGCStart(
# const unsigned int Count,
# const unsigned int Reason,
# LPCGUID ActivityId,
# LPCGUID RelatedActivityId)
# {
# if (!UserEventsEventEnabledGCStart())
# return ERROR_SUCCESS;
# TraceLoggingWriteActivity(DotNETRuntime, "GCStart", ActivityId, RelatedActivityId, TraceLoggingLevel(4), TraceLoggingKeyword(1),
# TraceLoggingUInt32(Count),
# TraceLoggingUInt32(Reason)
# );
# return ERROR_SUCCESS;
# }
#
# We also generate a function to check if a given keyword/level is enabled for each provider:
#
# bool DotNETRuntimeEnabledByKeyword(uint8_t level, uint64_t keyword)
# {
# if (!IsUserEventsEnabled())
# {
# return false;
# }
#
# switch (level)
# {
# case (0):
# if (keyword == 0)
# {
# if (TraceLoggingProviderEnabled(DotNETRuntimeStress, 0, 0)) return true;
# }
# // Continue for every eligible keyword
# break;
# case (1):
# if (keyword == 0)
# {
# if (TraceLoggingProviderEnabled(DotNETRuntimeStress, 1, 0)) return true;
# }
# // Continue for every eligible keyword
# break;
# case (2):
# if (keyword == 0)
# {
# if (TraceLoggingProviderEnabled(DotNETRuntimeStress, 2, 0)) return true;
# }
# // Continue for every eligible keyword
# break;
# case (3):
# if (keyword == 0)
# {
# if (TraceLoggingProviderEnabled(DotNETRuntimeStress, 3, 0)) return true;
# }
# // Continue for every eligible keyword
# break;
# case (4):
# if (keyword == 0)
# {
# if (TraceLoggingProviderEnabled(DotNETRuntimeStress, 4, 0)) return true;
# }
# // Continue for every eligible keyword
# break;
# case (5):
# if (keyword == 0)
# {
# if (TraceLoggingProviderEnabled(DotNETRuntimeStress, 5, 0)) return true;
# }
# // Continue for every eligible keyword
# break;
#
# }
# return false;
# }
from __future__ import print_function
from genEventing import *
import os
import xml.dom.minidom as DOM
from utilities import open_for_update, parseExclusionList, parseInclusionList
stdprolog_cpp = """// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
/******************************************************************
DO NOT MODIFY. AUTOGENERATED FILE.
This file is generated using the logic from <root>/src/scripts/genUserEvent.py
******************************************************************/
"""
stdprolog_cmake = """#
#
#******************************************************************
#DO NOT MODIFY. AUTOGENERATED FILE.
#This file is generated using the logic from <root>/src/scripts/genUserEvent.py
#******************************************************************
"""
userevent_dirname = "userevents"
def generateMethodSignatureEnabled(eventName, runtimeFlavor, providerName, eventLevel, eventKeywords):
return "%s UserEventsEventEnabled%s(void)" % (getEventPipeDataTypeMapping(runtimeFlavor)["BOOL"], eventName,)#mayhbe add in genEventing.py
def generateMethodSignatureWrite(eventName, template, extern, runtimeFlavor):
sig_pieces = []
if extern: sig_pieces.append('extern "C" ')
sig_pieces.append("%s UserEventsWriteEvent" % (getEventPipeDataTypeMapping(runtimeFlavor)["ULONG"]))
sig_pieces.append(eventName)
sig_pieces.append("(")
if template:
sig_pieces.append("\n")
fnSig = template.signature
for paramName in fnSig.paramlist:
fnparam = fnSig.getParam(paramName)
wintypeName = fnparam.winType
typewName = getPalDataTypeMapping(runtimeFlavor)[wintypeName]
winCount = fnparam.count
countw = getPalDataTypeMapping(runtimeFlavor)[winCount]
if paramName in template.structs:
sig_pieces.append(
"%sint %s_ElementSize,\n" %
(lindent, paramName))
sig_pieces.append(lindent)
sig_pieces.append(typewName)
if countw != " ":
sig_pieces.append(countw)
sig_pieces.append(" ")
sig_pieces.append(fnparam.name)
sig_pieces.append(",\n")
sig_pieces.append(lindent)
sig_pieces.append("%s ActivityId,\n" % (getEventPipeDataTypeMapping(runtimeFlavor)["LPCGUID"]))
sig_pieces.append(lindent)
sig_pieces.append("%s RelatedActivityId" % (getEventPipeDataTypeMapping(runtimeFlavor)["LPCGUID"]))
sig_pieces.append(")")
return ''.join(sig_pieces)
def formatGuid(providerGuid):
providerGuid = providerGuid[1:-1]
guidParts = providerGuid.split('-')
lastPart = guidParts[-1]
guidParts = guidParts[:-1]
nextLastPart = guidParts[-1]
guidParts = guidParts[:-1]
nextLastPartSplit = [nextLastPart[i:i+2] for i in range(0, len(nextLastPart), 2)]
lastPartSplit = [lastPart[i:i+2] for i in range(0, len(lastPart), 2)]
guidParts.extend(nextLastPartSplit)
guidParts.extend(lastPartSplit)
guidParts[0] = "0x" + guidParts[0]
return ", 0x".join(guidParts)
def generateClrUserEventWriteEventsImpl(providerNode, providerPrettyName, providerName, eventNodes, allTemplates, extern, target_cpp, runtimeFlavor, thisProviderKeywords, inclusionList, exclusionList):
WriteEventImpl = []
# User Event Provider Declaration
providerGuid = formatGuid(providerNode.getAttribute('guid'))
WriteEventImpl.append("TRACELOGGING_DEFINE_PROVIDER(%s, \"%s\", (%s));\n\n" % (providerPrettyName, providerName, providerGuid))
WriteEventImpl.append("""void Init%s()
{
int err = TraceLoggingRegister(%s);
_ASSERTE(err == 0);
}\n\n""" % (providerPrettyName, providerPrettyName))
WriteEventImpl.append("""bool %sEnabledByKeyword(uint8_t level, uint64_t keyword)
{
if (!IsUserEventsEnabled())
{
return false;
}
switch (level)
{\n""" % (providerPrettyName))
for level in range(6):
# first case, 0 keyword
WriteEventImpl.append(" case (%s):\n" % (level))
WriteEventImpl.append(" if (keyword == 0)\n")
WriteEventImpl.append(" {\n")
WriteEventImpl.append(" if (TraceLoggingProviderEnabled(%s, %s, 0)) return true;\n" % (providerPrettyName, level))
WriteEventImpl.append(" }\n")
# rest of keywords, only generate ones we know about via keywordMap in order to not define
# bogus events. (TraceLoggingProviderEnable registers the event to check if it is set or not)
for keyword in thisProviderKeywords:
WriteEventImpl.append(" if (keyword == 0x%x)\n" % (keyword))
WriteEventImpl.append(" {\n")
WriteEventImpl.append(" if (TraceLoggingProviderEnabled(%s, %s, 0x%x)) return true;\n" % (providerPrettyName, level, keyword))
WriteEventImpl.append(" }\n")
WriteEventImpl.append(" break;\n")
WriteEventImpl.append("""
}
return false;
}\n\n""")
#&& TraceLoggingProviderEnabled(%s, level, keyword);
for eventNode in eventNodes:
eventName = eventNode.getAttribute('symbol')
templateName = eventNode.getAttribute('template')
eventLevel = eventNode.getAttribute('level')
eventKeywords = eventNode.getAttribute('keywords')
eventKeywordsMask = generateEventKeywords(eventKeywords)
if not includeEvent(inclusionList, providerName, eventName):
continue
eventIsEnabledFunc = "TraceLoggingProviderEnabled"
# generate UserEventEnabled function
eventEnabledImpl = generateMethodSignatureEnabled(eventName, runtimeFlavor, providerName, eventLevel, eventKeywords) + """
{
return IsUserEventsEnabled() && %s(%s, %s, %s);
}
""" % (eventIsEnabledFunc, providerPrettyName, getUserEventLogLevelMapping(runtimeFlavor)[eventLevel], eventKeywordsMask)
WriteEventImpl.append(eventEnabledImpl)
# generate UserEventWriteEvent function
fnptype = []
if templateName:
template = allTemplates[templateName]
else:
template = None
fnptype.append(generateMethodSignatureWrite(eventName, template, extern, runtimeFlavor))
fnptype.append("\n{\n")
checking = """ if (!UserEventsEventEnabled%s())
return ERROR_SUCCESS;
""" % (eventName)
fnptype.append(checking)
WriteEventImpl.extend(fnptype)
if template:
body = generateWriteEventBody(template, providerPrettyName, eventName, runtimeFlavor, eventLevel, eventKeywordsMask)
WriteEventImpl.append(body)
WriteEventImpl.append("}\n\n")
else:
if runtimeFlavor.coreclr:
WriteEventImpl.append(
" TraceLoggingWriteActivity(%s, \"%s\"" %(providerPrettyName, eventName) +
", ActivityId, RelatedActivityId);\n")
WriteEventImpl.append(" return ERROR_SUCCESS;\n")
WriteEventImpl.append("}\n\n")
elif (runtimeFlavor.mono or runtimeFlavor.nativeaot):
WriteEventImpl.append("\n return ERROR_SUCCESS;\n}\n\n")
return ''.join(WriteEventImpl)
def generateWriteEventBody(template, providerPrettyName, eventName, runtimeFlavor, eventLevel, eventKeywordsMask):
#each of the if/else in this function is a different type of template, or a template containing specific args
fnSig = template.signature
pack_list = []
#need the providerNode to get the first arg for TraceLoggingWrite
pack_list.append(" TraceLoggingWriteActivity(%s, \"%s\", ActivityId, RelatedActivityId, TraceLoggingLevel(%s), TraceLoggingKeyword(%s)" % (providerPrettyName, eventName, getUserEventLogLevelMapping(runtimeFlavor)[eventLevel], eventKeywordsMask))
for paramName in fnSig.paramlist:
parameter = fnSig.getParam(paramName)
if paramName in template.structs:
size = "(int)%s_ElementSize * (int)%s" % (
paramName, parameter.prop)
pack_list.append(" TraceLoggingBinary(%s, %s)" % (parameter, size))
elif paramName in template.arrays:
size = "sizeof(%s) * (int)%s" % (
getArrayDataTypeMapping(runtimeFlavor)[parameter.winType],
parameter.prop)
elif parameter.winType == "win:GUID":
pack_list.append(" %s((uint8_t *)%s)" % (getUserEventDataTypeMapping(runtimeFlavor)[parameter.winType], parameter.name))
else:
pack_list.append(" %s(%s)" % (getUserEventDataTypeMapping(runtimeFlavor)[parameter.winType], parameter.name))
code = ",\n".join(pack_list) + "\n );\n return ERROR_SUCCESS;\n" #+ "\n\n"
return code
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 getCoreCLRUserEventImplFilePrefix():
return """#include "common.h"
#include <stdint.h>
#include <eventheader/TraceLoggingProvider.h>
#include <user_events.h>
"""
def getCoreCLRUserEventImplFileSuffix():
return ""
def getMonoUserEventImplFilePrefix():
return ""
def getMonoUserEventImplFileSuffix():
return ""
def getAotUserEventImplFilePrefix():
return ""
def getAotUserEventImplFileSuffix():
return ""
def generateUserEventImplFiles(
etwmanifest, userevent_directory, extern, target_cpp, runtimeFlavor, inclusionList, exclusionList, dryRun):
tree = DOM.parse(etwmanifest)
for providerNode in tree.getElementsByTagName('provider'):
providerName = providerNode.getAttribute('name')
if not includeProvider(providerName, runtimeFlavor):
continue
providerPrettyName = providerName.replace("Windows-", '')
providerPrettyName = providerPrettyName.replace("Microsoft-", '')
providerName_File = providerPrettyName.replace('-', '')
if target_cpp:
providerName_File = providerName_File + ".cpp"
else:
providerName_File = providerName_File + ".c"
providerName_File = providerName_File.lower()
usereventfile = os.path.join(userevent_directory, providerName_File)
providerPrettyName = providerPrettyName.replace('-', '_')
thisProviderKeywords = []
for keywordNode in providerNode.getElementsByTagName('keyword'):
mask = int(keywordNode.getAttribute('mask'), 0)
thisProviderKeywords.append(mask)
if dryRun:
print(usereventfile)
else:
with open_for_update(usereventfile) as usereventImpl:
usereventImpl.write(stdprolog_cpp)
header = ""
if runtimeFlavor.coreclr:
header = getCoreCLRUserEventImplFilePrefix()
elif runtimeFlavor.mono:
header = getMonoUserEventImplFilePrefix()
elif runtimeFlavor.nativeaot:
header = getAotUserEventImplFilePrefix()
usereventImpl.write(header + "\n")
templateNodes = providerNode.getElementsByTagName('template')
allTemplates = parseTemplateNodes(templateNodes)
eventNodes = providerNode.getElementsByTagName('event')
usereventImpl.write(
generateClrUserEventWriteEventsImpl(
providerNode,
providerPrettyName,
providerName,
eventNodes,
allTemplates,
extern,
target_cpp,
runtimeFlavor,
thisProviderKeywords,
inclusionList,
exclusionList) + "\n")
if runtimeFlavor.coreclr:
usereventImpl.write(getCoreCLRUserEventImplFileSuffix())
elif runtimeFlavor.mono:
usereventImpl.write(getMonoUserEventImplFileSuffix())
elif runtimeFlavor.nativeaot and providerName=="Microsoft-Windows-DotNETRuntime":
usereventImpl.write(getAotUserEventImplFileSuffix())
def generateUserEventFiles(
etwmanifest, intermediate, extern, target_cpp, runtimeFlavor, inclusionList, exclusionList, dryRun):
if runtimeFlavor.nativeaot or runtimeFlavor.mono:
raise Exception("genUserEvents.py only supports coreclr currently.")
userevent_directory = os.path.join(intermediate, userevent_dirname)
tree = DOM.parse(etwmanifest)
if not os.path.exists(userevent_directory):
os.makedirs(userevent_directory)
# generate all keywords
for keywordNode in tree.getElementsByTagName('keyword'):
keywordName = keywordNode.getAttribute('name')
keywordMask = keywordNode.getAttribute('mask')
keywordMap[keywordName] = int(keywordMask, 0)
# generate file for each provider
generateUserEventImplFiles(
etwmanifest,
userevent_directory,
extern,
target_cpp,
runtimeFlavor,
inclusionList,
exclusionList,
dryRun
)
import argparse
import sys
def main(argv):
# parse the command line
parser = argparse.ArgumentParser(
description="Generates the Code required to instrument userevent logging mechanism")
required = parser.add_argument_group('required arguments')
required.add_argument('--man', type=str, required=True,
help='full path to manifest containing the description of events')
required.add_argument('--exc', type=str, required=True,
help='full path to exclusion list')
required.add_argument('--inc', type=str,default="",
help='full path to inclusion list')
required.add_argument('--intermediate', type=str, required=True,
help='full path to eventprovider intermediate directory')
required.add_argument('--runtimeflavor', type=str,default="CoreCLR",
help='runtime flavor')
required.add_argument('--nonextern', action='store_true',
help='if specified, will generate files to be compiled into the CLR rather than extern' )
required.add_argument('--dry-run', action='store_true',
help='if specified, will output the names of the generated files instead of generating the files' )
args, unknown = parser.parse_known_args(argv)
if unknown:
print('Unknown argument(s): ', ', '.join(unknown))
return 1
sClrEtwAllMan = args.man
exclusion_filename = args.exc
inclusion_filename = args.inc
intermediate = args.intermediate
runtimeFlavor = RuntimeFlavor(args.runtimeflavor)
extern = not args.nonextern
dryRun = args.dry_run
target_cpp = True
if runtimeFlavor.mono:
extern = False
target_cpp = False
inclusion_list = parseInclusionList(inclusion_filename)
exclusion_list = parseExclusionList(exclusion_filename)
generateUserEventFiles(sClrEtwAllMan, intermediate, extern, target_cpp, runtimeFlavor, inclusion_list, exclusion_list, dryRun)
if __name__ == '__main__':
return_code = main(sys.argv[1:])
sys.exit(return_code)

View file

@ -37,6 +37,8 @@ endif(FEATURE_JIT_PITCHING)
if(FEATURE_PERFTRACING)
set(CORECLR_EVENTPIPE_SHIM_DIR ${CMAKE_CURRENT_SOURCE_DIR}/eventing/eventpipe)
include_directories(${CORECLR_EVENTPIPE_SHIM_DIR})
set(CORECLR_USEREVENTS_SHIM_DIR ${CMAKE_CURRENT_SOURCE_DIR}/eventing/userevents)
include_directories(${CORECLR_USEREVENTS_SHIM_DIR})
endif(FEATURE_PERFTRACING)
set(VM_SOURCES_DAC_AND_WKS_COMMON

View file

@ -197,6 +197,10 @@
#include "diagnosticserveradapter.h"
#include "eventpipeadapter.h"
#if defined(FEATURE_PERFTRACING) && defined(TARGET_LINUX)
#include "user_events.h"
#endif // defined(FEATURE_PERFTRACING) && defined(TARGET_LINUX)
#ifndef TARGET_UNIX
// Included for referencing __security_cookie
#include "process.h"
@ -661,6 +665,9 @@ void EEStartupHelper()
#ifdef FEATURE_PERFTRACING
// Initialize the event pipe.
EventPipeAdapter::Initialize();
#if defined(TARGET_LINUX)
InitUserEvents();
#endif // TARGET_LINUX
#endif // FEATURE_PERFTRACING
#ifdef TARGET_UNIX

View file

@ -8,6 +8,10 @@ else()
set(NEED_XPLAT_HEADER ON)
endif()
if (CLR_CMAKE_TARGET_LINUX)
set(USEREVENTS_ARG "--userevents")
endif(CLR_CMAKE_TARGET_LINUX)
include(FindPython)
set (EventingHeaders
@ -26,7 +30,7 @@ set(GENEVENTING_SCRIPT ${CLR_DIR}/scripts/genEventing.py)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/eventing_headers.timestamp
COMMAND ${Python_EXECUTABLE} ${GENEVENTING_SCRIPT} --man ${EVENT_MANIFEST} --incdir ${GENERATED_INCLUDE_DIR} --dummy ${GENERATED_INCLUDE_DIR}/etmdummy.h ${NONEXTERN_ARG} ${NOXPLATHEADER_ARG}
COMMAND ${Python_EXECUTABLE} ${GENEVENTING_SCRIPT} --man ${EVENT_MANIFEST} --incdir ${GENERATED_INCLUDE_DIR} --dummy ${GENERATED_INCLUDE_DIR}/etmdummy.h ${NONEXTERN_ARG} ${NOXPLATHEADER_ARG} ${USEREVENTS_ARG}
COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/eventing_headers.timestamp
DEPENDS ${EVENT_MANIFEST} ${GENEVENTING_SCRIPT}
VERBATIM
@ -39,6 +43,10 @@ add_dependencies(eventing_headers eventprovider)
add_subdirectory(eventpipe)
if(CLR_CMAKE_TARGET_LINUX)
add_subdirectory(userevents)
endif(CLR_CMAKE_TARGET_LINUX)
if(CLR_CMAKE_HOST_WIN32)
add_subdirectory(EtwProvider)
endif()

View file

@ -0,0 +1,55 @@
include(FindPython)
set (GENERATE_SCRIPT ${CLR_DIR}/scripts/genUserEvents.py)
set(GENERATE_COMMAND ${Python_EXECUTABLE} ${GENERATE_SCRIPT} --man ${EVENT_MANIFEST} --exc ${EVENT_EXCLUSIONS} --intermediate ${CMAKE_CURRENT_BINARY_DIR} ${NONEXTERN_ARG})
execute_process(
COMMAND ${GENERATE_COMMAND} --dry-run
RESULT_VARIABLE GEN_USEREVENTS_RESULT
OUTPUT_VARIABLE USEREVENTS_PROVIDER_OUTPUT
ERROR_VARIABLE GEN_USEREVENTS_ERRORS
)
if (NOT GEN_USEREVENTS_RESULT EQUAL 0)
message(FATAL_ERROR "Failed to generate user_events provider: ${GEN_USEREVENTS_ERRORS}")
endif()
include(${CLR_SRC_NATIVE_DIR}/external/LinuxTracepoints.cmake)
include_directories(${COREPAL_SOURCE_DIR}/inc/rt)
set (USEREVENTS_HELPER_SOURCES
user_events.cpp)
# TODO: keep in sync with providers in ClrEtwAll.man
set (USEREVENTS_PROVIDER_SOURCES
userevents/dotnetruntime.cpp
userevents/dotnetruntimeprivate.cpp
userevents/dotnetruntimerundown.cpp
userevents/dotnetruntimestress.cpp)
set (CORECLR_USEREVENTS_SHIM_SOURCE_PATH "${CORECLR_USEREVENTS_SHIM_DIR}")
addprefix(USEREVENTS_PROVIDER_SOURCES ${CMAKE_CURRENT_BINARY_DIR} "${USEREVENTS_PROVIDER_SOURCES}")
add_custom_command(OUTPUT ${USEREVENTS_PROVIDER_SOURCES}
COMMAND ${GENERATE_COMMAND}
DEPENDS ${EVENT_MANIFEST} ${GENERATE_SCRIPT})
set_source_files_properties(
${USEREVENTS_PROVIDER_SOURCES}
PROPERTIES GENERATED TRUE)
add_custom_target(
generated_userevents_headers
DEPENDS ${USEREVENTS_HEADERS})
add_library_clr(usereventsprovider
OBJECT
${USEREVENTS_HELPER_SOURCES}
${USEREVENTS_PROVIDER_SOURCES}
${LinuxTracepoints_sources}
)
set_target_properties(usereventsprovider PROPERTIES LINKER_LANGUAGE CXX)
add_dependencies(usereventsprovider eventing_headers)

View file

@ -0,0 +1,80 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#include <common.h>
#include <configuration.h>
#include "user_events.h"
namespace
{
bool s_userEventsEnabled = false;
}
void InitDotNETRuntime();
bool DotNETRuntimeEnabledByKeyword(uint8_t level, uint64_t keyword);
void InitDotNETRuntimePrivate();
bool DotNETRuntimePrivateEnabledByKeyword(uint8_t level, uint64_t keyword);
void InitDotNETRuntimeRundown();
bool DotNETRuntimeRundownEnabledByKeyword(uint8_t level, uint64_t keyword);
void InitDotNETRuntimeStress();
bool DotNETRuntimeStressEnabledByKeyword(uint8_t level, uint64_t keyword);
void InitUserEvents()
{
bool isEnabled = Configuration::GetKnobBooleanValue(W("System.Diagnostics.Tracing.UserEvents"), false);
if (!isEnabled)
{
isEnabled = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EnableUserEvents) != 0;
}
s_userEventsEnabled = isEnabled;
if (s_userEventsEnabled)
{
InitDotNETRuntime();
MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context.UserEventsProvider.id = 0;
InitDotNETRuntimePrivate();
MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_DOTNET_Context.UserEventsProvider.id = 1;
InitDotNETRuntimeRundown();
MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context.UserEventsProvider.id = 2;
InitDotNETRuntimeStress();
MICROSOFT_WINDOWS_DOTNETRUNTIME_STRESS_PROVIDER_DOTNET_Context.UserEventsProvider.id = 3;
}
}
bool IsUserEventsEnabled()
{
return s_userEventsEnabled;
}
bool IsUserEventsEnabledByKeyword(UCHAR providerId, uint8_t level, uint64_t keyword)
{
switch (providerId)
{
case 0:
{
return DotNETRuntimeEnabledByKeyword(level, keyword);
}
case 1:
{
return DotNETRuntimePrivateEnabledByKeyword(level, keyword);
}
case 2:
{
return DotNETRuntimeRundownEnabledByKeyword(level, keyword);
}
case 3:
{
return DotNETRuntimeStressEnabledByKeyword(level, keyword);
}
default:
{
_ASSERTE(!"Unknown provider id");
}
}
return false;
}

View file

@ -0,0 +1,12 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#ifndef __USEREVENTS_H__
#define __USEREVENTS_H__
void InitUserEvents();
bool IsUserEventsEnabled();
bool IsUserEventsEnabledByKeyword(UCHAR providerId, uint8_t level, uint64_t keyword);
#endif // __USEREVENTS_H__

View file

@ -5804,6 +5804,30 @@ bool EventPipeHelper::IsEnabled(DOTNET_TRACE_CONTEXT Context, UCHAR Level, ULONG
return false;
}
#ifdef TARGET_LINUX
#include "user_events.h"
bool UserEventsHelper::Enabled()
{
return IsUserEventsEnabled();
}
bool UserEventsHelper::IsEnabled(DOTNET_TRACE_CONTEXT Context, UCHAR Level, ULONGLONG Keyword)
{
return IsUserEventsEnabledByKeyword(Context.UserEventsProvider.id, Level, Keyword);
}
#else // TARGET_LINUX
bool UserEventsHelper::Enabled()
{
return false;
}
bool UserEventsHelper::IsEnabled(DOTNET_TRACE_CONTEXT Context, UCHAR Level, ULONGLONG Keyword)
{
return false;
}
#endif // TARGET_LINUX
#endif // FEATURE_PERFTRACING
#if defined(HOST_UNIX) && defined(FEATURE_PERFTRACING)

View file

@ -0,0 +1,2 @@
1.3.3
https://github.com/microsoft/LinuxTracepoints/tree/a817b91dfb08b2929ec6d48a211644e3394bf1c7

View file

@ -0,0 +1,11 @@
# IMPORTANT: do not use add_compile_options(), add_definitions() or similar functions here since it will leak to the including projects
include_directories(${CMAKE_CURRENT_LIST_DIR}/LinuxTracepoints/libtracepoint/include)
include_directories(${CMAKE_CURRENT_LIST_DIR}/LinuxTracepoints/libeventheader-tracepoint/include)
set(LinuxTracepoints_sources_base
libtracepoint/src/tracepoint.c
libeventheader-tracepoint/src/eventheader-tracepoint.c)
addprefix(LinuxTracepoints_sources "${CMAKE_CURRENT_LIST_DIR}/LinuxTracepoints" "${LinuxTracepoints_sources_base}")

View file

@ -0,0 +1,12 @@
* text=auto
*.c text
*.cpp text
*.h text
*.cs text
*.md text
*.txt text
*.json text
*.yml text
*.config text
*.cmake.in text
*.sln text eol=crlf

View file

@ -0,0 +1,10 @@
*.suo
*.user
/bin*/
.vs/
obj/
/out/
.vscode
/rust/target/
/rust/Cargo.lock
/build/

View file

@ -0,0 +1,94 @@
# LinuxTracepoints Change Log
## v1.3.4 (TBD)
- libtracepoint-control: New `tracepoint-collect` tool that records tracepoint
events into a perf.data file.
- libtracepoint-control: TracepointSession SavePerfDataFile adds a
`PERF_RECORD_FINISHED_INIT` record to the generated perf.data file.
- libeventheader: tool `eventheader-register` deleted. Instead, use
`tracepoint-register` from libtracepoint.
## v1.3.3 (2024-04-15)
- BUG FIX: EADDRINUSE returned during TraceLoggingRegister on newer kernels.
The "name already in use" detection splits on whitespace, while all other
processing splits on semicolon. Fix by adding space after each semicolon
in `EVENTHEADER_COMMAND_TYPES`.
- libtracepoint-decode: In pipe mode, load event names at FinishedInit instead
of HeaderLastFeature since not all traces emit HeaderLastFeature.
- libtracepoint-decode: Recognize files from LP32 systems as 32-bit.
- libtracepoint: new tool `tracepoint-register` for pre-registering
tracepoints.
- libeventheader: existing tool `eventheader-register` is deprecated in
favor of `tracepoint-register`.
- libeventheader-decode-dotnet: Moved to separate repository
[LinuxTracepoints-Net](https://github.com/microsoft/LinuxTracepoints-Net).
## v1.3.2 (2024-02-27)
- Bug fix: Open `user_events_data` for `O_WRONLY` instead of `O_RDWR`.
## v1.3.1 (2024-01-11)
- `TracepointSession` supports per-CPU buffer sizes (including 0) to allow
memory usage optimization when trace producers are known to be bound to
specific CPUs.
- `TracepointSession` uses `PERF_ATTR_SIZE_VER3` for the size of
`perf_event_attr` to minimize the chance of incompatibilities.
## v1.3.0 (2023-11-27)
- **Breaking changes** to `PerfDataFile`:
- `dataFile.AttrCount()` method replaced by `EventDescCount()` method.
- `dataFile.Attr(index)` method replaced by `EventDesc(index)` method.
The returned `PerfEventDesc` object contains an `attr` pointer.
- `dataFile.EventDescById(id)` method replaced by `FindEventDescById(id)`.
- **Breaking changes** to `PerfSampleEventInfo`:
- `eventInfo.session` field renamed to `session_info`.
- `eventInfo.attr` field replaced by `Attr()` method.
- `eventInfo.name` field replaced by `Name()` method.
- `eventInfo.sample_type` field replaced by `SampleType()` method.
- `eventInfo.raw_meta` field replaced by `Metadata()` method.
- **Breaking changes** to `TracepointSession`:
- `session.EnableTracePoint(...)` method renamed to `EnableTracepoint(...)`.
- `session.DisableTracePoint(...)` method renamed to `DisableTracepoint(...)`.
- `EventFormatter` formats timestamps as date-time if clock information is
available in the event metadata. If clock information is not present, it
continues to format timestamps as seconds.
- `TracepointSession` provides `SavePerfDataFile(filename)` method to save
the current contents of the session buffers into a `perf.data` file.
- `TracepointSession` now includes ID in default sample type.
- `TracepointSession` records clock information from the session.
- `TracepointSession` provides access to information about the tracepoints
that have been added to the session (metadata, status, statistics).
- `PerfDataFile` decodes clock information from perf.data files if present.
- `PerfDataFile` provides access to more metadata via `PerfEventDesc` struct.
- `PerfDataFile` provides `EventDataSize` for determining the size of an event.
- New `PerfDataFileWriter` class for generating `perf.data` files.
- Changed procedure for locating the `user_events_data` file.
- Old: parse `/proc/mounts` to determine the `tracefs` or `debugfs` mount
point, then use that as the root for the `user_events_data` path.
- New: try `/sys/kernel/tracing/user_events_data`; if that doesn't exist,
parse `/proc/mounts` to find the `tracefs` or `debugfs` mount point.
- Rationale: Probe an absolute path so that containers don't have to
create a fake `/proc/mounts` and for efficiency in the common case.
## v1.2.1 (2023-07-24)
- Prefer `user_events_data` from `tracefs` over `user_events_data` from
`debugfs`.
## v1.2 (2023-06-27)
- Added "Preregister" methods to the `TracepointCache` class so that a
controller can pre-register events that it wants to collect.
- If no consumers have enabled a tracepoint, the kernel now returns `EBADF`.
The provider APIs have been updated to be consistent with the new behavior.
## v1.1 (2023-06-20)
- Add namespaces to the C++ APIs.
- Move non-eventheader logic from eventheader-decode to new tracepoint-decode
library.
- Add new libtracepoint-control library.

View file

@ -0,0 +1,17 @@
cmake_minimum_required(VERSION 3.10)
include(version.cmake)
project(LinuxTracepoints
VERSION ${LINUXTRACEPOINTS_VERSION})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
add_subdirectory(libtracepoint)
add_subdirectory(libtracepoint-decode-cpp)
add_subdirectory(libeventheader-tracepoint)
add_subdirectory(libeventheader-decode-cpp)
if(NOT WIN32)
add_subdirectory(libtracepoint-control-cpp)
endif()

View file

@ -0,0 +1,117 @@
{
"version": 3,
"configurePresets": [
{
"name": "Release",
"description": "Compile project with release build settings",
"generator": "Unix Makefiles",
"binaryDir": "${sourceDir}/build/out/${presetName}",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/build/install/${presetName}"
},
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Linux"
},
"vendor": {
"microsoft.com/VisualStudioSettings/CMake/1.0": {
"hostOS": ["Linux"]
},
"microsoft.com/VisualStudioRemoteSettings/CMake/1.0": {
"copySources": true,
"rsyncCommandArgs": ["-t", "--delete", "--delete-excluded"],
"copySourcesOptions": {
"method": "rsync"
}
}
}
},
{
"name": "Debug",
"description": "Compile project with debug build settings",
"inherits": "Release",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "ReleaseClang",
"displayName": "Release clang",
"inherits": "Release",
"cacheVariables": {
"CMAKE_C_COMPILER": "clang",
"CMAKE_CXX_COMPILER": "clang++"
}
},
{
"name": "DebugClang",
"inherits": "Debug",
"cacheVariables": {
"CMAKE_C_COMPILER": "clang",
"CMAKE_CXX_COMPILER": "clang++"
}
},
{
"name": "DebugGCC",
"inherits": "Debug",
"cacheVariables": {
"CMAKE_C_COMPILER": "gcc",
"CMAKE_CXX_COMPILER": "g++"
}
},
{
"name": "ReleaseGCC",
"inherits": "Release",
"cacheVariables": {
"CMAKE_C_COMPILER": "gcc",
"CMAKE_CXX_COMPILER": "g++"
}
},
{
"name": "Windows",
"displayName": "Debug VS",
"description": "Compile project with VS",
"generator": "Visual Studio 17 2022",
"architecture": "x64",
"binaryDir": "${sourceDir}/out/build/${presetName}",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}"
},
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
},
"vendor": {
"microsoft.com/VisualStudioSettings/CMake/1.0": {
"hostOS": ["Windows"]
}
}
}
],
"buildPresets": [
{
"name": "Release",
"description": "",
"displayName": "",
"configurePreset": "Release"
},
{
"name": "Debug",
"description": "",
"displayName": "",
"configurePreset": "Debug"
},
{
"name": "Windows",
"description": "",
"displayName": "",
"configurePreset": "Windows",
"configuration": "Debug",
"targets": ["ALL_BUILD"]
}
]
}

View file

@ -0,0 +1,9 @@
# Microsoft Open Source Code of Conduct
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
Resources:
- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns

View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) Microsoft Corporation.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE

View file

@ -0,0 +1,182 @@
# Libraries for Linux Tracepoints and user_events
This repository contains C/C++ libraries for collecting and decoding
[Linux Tracepoint](https://www.kernel.org/doc/html/latest/trace/tracepoints.html)
events and for generating Tracepoint events from user mode using the
[user_events](https://docs.kernel.org/trace/user_events.html) facility.
Related repositories:
- [LinuxTracepoints-Net](https://github.com/microsoft/LinuxTracepoints-Net) -
.NET libraries and tools for decoding perf.data files, including `eventheader`
events.
- [LinuxTracepoints-Rust](https://github.com/microsoft/LinuxTracepoints-Rust) -
Rust libraries for generating Tracepoint events from user mode using the
[user_events](https://docs.kernel.org/trace/user_events.html) facility
## Overview
- [libtracepoint](libtracepoint) -
low-level C/C++ tracing interface. Designed to support replacement at
link-time if a different implementation is needed (e.g. for testing).
- [tracepoint-provider.h](libtracepoint/include/tracepoint/tracepoint-provider.h) -
a developer-friendly C/C++ API for writing tracepoint events to any
implementation of the `tracepoint.h` interface.
- [tracepoint.h](libtracepoint/include/tracepoint/tracepoint-provider.h) -
low-level interface for writing tracepoint events.
- [libtracepoint.a](libtracepoint/src/tracepoint.c) -
default implementation that writes directly to the Linux `user_events` facility.
- [libtracepoint-control-cpp](libtracepoint-control-cpp) -
C++ library for controlling a tracepoint event collection session.
- `TracingSession.h` implements an event collection session that can
collect tracepoint events and enumerate the events that the session has
collected. Supports real-time and circular-buffer modes.
- `TracingPath.h` has functions for finding the `/sys/kernel/tracing`
mount point and reading `format` files.
- `TracepointSpec.h` parses tracepoint event specifications for configuring
a tracepoint collection session.
- `TracingCache.h` implements a cache for tracking parsed `format` files
based on system+name or by `common_type` id.
- [libtracepoint-decode-cpp](libtracepoint-decode-cpp) -
C++ library for decoding tracepoints. Works on both Linux and Windows.
- `PerfDataFile.h` defines the `PerfDataFile` class that decodes
`perf.data` files.
- `PerfEventInfo.h` defines the `PerfSampleEventInfo` and
`PerfNonSampleEventInfo` structures for raw event information.
- `PerfEventMetadata.h` defines classes for parsing ftrace event metadata
information.
- [libeventheader-tracepoint](libeventheader-tracepoint) -
`eventheader` envelope that supports extended attributes including severity
level and optional field information (field types and field names).
- [TraceLoggingProvider.h](libeventheader-tracepoint/include/eventheader/TraceLoggingProvider.h) -
a developer-friendly C/C++ API for writing `eventheader`-encapsulated
events to any implementation of the tracepoint interface.
- [EventHeaderDynamic.h](libeventheader-tracepoint/include/eventheader/EventHeaderDynamic.h) -
C++ API for writing runtime-defined `eventheader`-encapsulated events,
intended for use as an implementation layer for a higher-level API like
OpenTelemetry.
- [libeventheader-decode-cpp](libeventheader-decode-cpp) -
C++ library for decoding events that use the `eventheader` envelope.
- `EventEnumerator` class parses an event into fields.
- `EventFormatter` class converts event data into a string.
- `decode-perf` tool that decodes `perf.data` files to JSON.
## General Usage
- Configure a Linux system with the `user_events` feature enabled.
- Supported on Linux kernel 6.4 and later.
- Kernel must be built with `user_events` support (`CONFIG_USER_EVENTS=y`).
- Must have either `tracefs` or `debugfs` mounted. For example, you might add
the following line to your `/etc/fstab` file:
`tracefs /sys/kernel/tracing tracefs defaults 0 0`
- The user that will generate events must have `x` access to the `tracing`
directory and `w` access to the `tracing/user_events_data` file. One
possible implementation is to create a `tracers` group, then:
- `chgrp tracers /sys/kernel/tracing`
- `chgrp tracers /sys/kernel/tracing/user_events_data`
- `chmod g+x /sys/kernel/tracing`
- `chmod g+w /sys/kernel/tracing/user_events_data`
- Use one of the event generation APIs to write a program that generates events.
- C/C++ programs can use
[tracepoint-provider.h](libtracepoint/include/tracepoint/tracepoint-provider.h)
to generate regular Linux Tracepoint events that are defined at compile-time.
(Link with `libtracepoint`.)
- C/C++ programs can use
[TraceLoggingProvider.h](libeventheader-tracepoint/include/eventheader/TraceLoggingProvider.h)
to generate eventheader-enabled Tracepoint events that are defined at
compile-time. (Link with `libtracepoint` and `libeventheader-tracepoint`.)
- C++ middle-layer APIs (e.g. an OpenTelemetry exporter) can use
[EventHeaderDynamic.h](libeventheader-tracepoint/include/eventheader/EventHeaderDynamic.h)
to generate eventheader-enabled Tracepoint events that are runtime-dynamic.
(Link with `libtracepoint` and `libeventheader-tracepoint`.)
- Rust programs can use
[LinuxTracepoints-Rust](https://github.com/microsoft/LinuxTracepoints-Rust)
to generate eventheader-enabled Tracepoint events.
- To collect events in a C++ program, use
[libtracepoint-control-cpp](libtracepoint-control-cpp). Note that your
program must run as a privileged user (`CAP_PERFMON` capability plus read access to
`/sys/kernel/tracing/events`) because access to the event collection system is
restricted by default.
- To collect events without writing C++ code, use the included
[tracepoint-collect](libtracepoint-control-cpp/tools/tracepoint-collect.cpp) tool
or the Linux [`perf`](https://www.man7.org/linux/man-pages/man1/perf.1.html) tool
to collect events to a `perf.data` file, e.g.
`tracepoint-collect -o File.perf user_events:MyEvent1 user_events:MyEvent2` or
`perf record -o File.perf -k monotonic -e user_events:MyEvent1,user_events:MyEvent2`.
Note that you must run the tool as a privileged user to collect events (`CAP_PERFMON`
capability plus read access to `/sys/kernel/tracing/events`).
- The `perf` tool binary is typically available as part of the `linux-perf`
package (e.g. can be installed by `apt install linux-perf`). However, this
package installs a `perf_VERSION` binary rather than a `perf` binary, so
you will need to add an appropriate VERSION suffix to your `perf` commands
or use a wrapper script.
- To capture tracepoints using `perf`, you'll also need to install
`libtraceevent`, e.g. `apt install libtraceevent1`.
- The `linux-base` package installs a `perf` wrapper script that redirects to
the version of `perf` that matches your current kernel (if present) so that
you can run the appropriate version of `perf` without the VERSION suffix.
This frequently doesn't work because the latest `perf` binary from `apt`
doesn't always match the running kernel, so you may want to make your own
wrapper script instead.
- Note that for purposes of collecting events, it is usually not important
for the version of the `perf` tool to match the kernel version, so it's
ok to use e.g. `perf_5.10` even if you are running a newer kernel.
- Note that tracepoints must be registered before you can start collecting
them. The `tracepoint-collect` tool has facilities to pre-register a user_events
tracepoint. The `perf` command will report an error if the tracepoint is not yet
registered.
- You can usually register tracepoints by starting the program that generates
them. Most programs will register all of their tracepoints when they start
running. (They will usually unregister when they stop running.)
- You can also use the
[`tracepoint-register`](libtracepoint/tools/tracepoint-register.cpp)
tool to pre-register an event so you can start collecting it before
starting the program that generates it.
- If writing your own event collection tool, you might do something similar
in your tool to pre-register the events that you need to collect. For
example, you might use the `PreregisterTracepoint` or
`PreregisterEventHeaderTracepoint` methods of the `TracepointCache` class
in [`libtracepoint=control`](libtracepoint-control-cpp).
- Use the [`decode-perf`](libeventheader-decode-cpp/tools/decode-perf.cpp)
tool to decode the `perf.data` file to JSON text, or write your own decoding
tool using [libtracepoint-decode-cpp](libtracepoint-decode-cpp) and
`libeventheader-decode-cpp`.
## Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit [https://cla.opensource.microsoft.com](https://cla.opensource.microsoft.com).
When you submit a pull request, a CLA bot will automatically determine whether you need to provide
a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
## Trademarks
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft
trademarks or logos is subject to and must follow
[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/legal/intellectualproperty/trademarks/usage/general).
Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.
Any use of third-party trademarks or logos are subject to those third-party's policies.

View file

@ -0,0 +1,41 @@
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.8 BLOCK -->
## Security
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.
## Reporting Security Issues
**Please do not report security vulnerabilities through public GitHub issues.**
Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report).
If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc).
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
* Full paths of source file(s) related to the manifestation of the issue
* The location of the affected source code (tag/branch/commit or direct URL)
* Any special configuration required to reproduce the issue
* Step-by-step instructions to reproduce the issue
* Proof-of-concept or exploit code (if possible)
* Impact of the issue, including how an attacker might exploit the issue
This information will help us triage your report more quickly.
If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.
## Preferred Languages
We prefer all communications to be in English.
## Policy
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).
<!-- END MICROSOFT SECURITY.MD BLOCK -->

View file

@ -0,0 +1,11 @@
# Support
## How to file issues and get help
This project uses GitHub Issues to track bugs and feature requests. Please search the existing
issues before filing new issues to avoid duplicates. For new issues, file your bug or
feature request as a new Issue.
## Microsoft Support Policy
Support for this **PROJECT or PRODUCT** is limited to the resources listed above.

View file

@ -0,0 +1,262 @@
"EventHeaderInterceptorLE64.dat": [
{ "n": "Long_Provider_Name_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX0123456789:LongProviderEvent", "meta": { "level": 5 } },
{ "n": "TestProviderC:EventCG", "enabled": true, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:EventCppG", "enabled": true, "meta": { "level": 5 } },
{ "n": "TestProviderC:CScalars1", "meta": { "level": 5 } },
{ "n": "TestProviderC:CScalars2", "meta": { "level": 1, "keyword": "0xF123456789ABCDEF", "opcode": 2 } },
{ "n": "TestProviderC:CScalars3", "Struct20;tag=0xBEEF": { "hi;tag=0x12": "hi", "ch10;tag=0x10": [ "H", "o", "w" ] }, "meta": { "id": 1000, "version": 11, "level": 4, "keyword": "0x85", "opcode": 3, "tag": "0x1234" } },
{ "n": "TestProviderC:Transfer00", "meta": { "level": 5 } },
{ "n": "TestProviderC:Transfer10", "meta": { "level": 5, "activity": "01020304-0506-0708-0102-030405060708" } },
{ "n": "TestProviderC:Transfer11", "meta": { "level": 5, "activity": "01020304-0506-0708-0102-030405060708", "relatedActivity": "01020304-0506-0708-0102-030405060708" } },
{ "n": "TestProviderC:i8", "i8": 100, "(-128)": -128, "meta": { "level": 5 } },
{ "n": "TestProviderC:u8", "u8": 200, "(255)": 255, "meta": { "level": 5 } },
{ "n": "TestProviderC:i16", "i16": 30000, "(-32767-1)": -32768, "meta": { "level": 5 } },
{ "n": "TestProviderC:u16", "u16": 60000, "(65535)": 65535, "meta": { "level": 5 } },
{ "n": "TestProviderC:i32", "i32": 2000000000, "(-2147483647-1)": -2147483648, "meta": { "level": 5 } },
{ "n": "TestProviderC:u32", "u32": 4000000000, "(4294967295U)": 4294967295, "meta": { "level": 5 } },
{ "n": "TestProviderC:i64", "i64": 9000000000000000000, "(-9223372036854775807L-1)": -9223372036854775808, "meta": { "level": 5 } },
{ "n": "TestProviderC:u64", "u64": 18000000000000000000, "(18446744073709551615UL)": 18446744073709551615, "meta": { "level": 5 } },
{ "n": "TestProviderC:iptr", "iptr": 1234, "(-9223372036854775807L-1)": -9223372036854775808, "meta": { "level": 5 } },
{ "n": "TestProviderC:uptr", "uptr": 4321, "(18446744073709551615UL)": 18446744073709551615, "meta": { "level": 5 } },
{ "n": "TestProviderC:iL", "iL": 2000000000, "(-0x7fffffffffffffffL - 1L)": -9223372036854775808, "meta": { "level": 5 } },
{ "n": "TestProviderC:uL", "uL": 4000000000, "(0x7fffffffffffffffL * 2UL + 1UL)": 18446744073709551615, "meta": { "level": 5 } },
{ "n": "TestProviderC:hi8", "i8": "0x64", "(-128)": "0x80", "meta": { "level": 5 } },
{ "n": "TestProviderC:hu8", "u8": "0xC8", "(255)": "0xFF", "meta": { "level": 5 } },
{ "n": "TestProviderC:hi16", "i16": "0x7530", "(-32767-1)": "0x8000", "meta": { "level": 5 } },
{ "n": "TestProviderC:hu16", "u16": "0xEA60", "(65535)": "0xFFFF", "meta": { "level": 5 } },
{ "n": "TestProviderC:hi32", "i32": "0x77359400", "(-2147483647-1)": "0x80000000", "meta": { "level": 5 } },
{ "n": "TestProviderC:hu32", "u32": "0xEE6B2800", "(4294967295U)": "0xFFFFFFFF", "meta": { "level": 5 } },
{ "n": "TestProviderC:hi64", "i64": "0x7CE66C50E2840000", "(-9223372036854775807L-1)": "0x8000000000000000", "meta": { "level": 5 } },
{ "n": "TestProviderC:hu64", "u64": "0xF9CCD8A1C5080000", "(18446744073709551615UL)": "0xFFFFFFFFFFFFFFFF", "meta": { "level": 5 } },
{ "n": "TestProviderC:hiptr", "iptr": "0x4D2", "(-9223372036854775807L-1)": "0x8000000000000000", "meta": { "level": 5 } },
{ "n": "TestProviderC:huptr", "uptr": "0x10E1", "(18446744073709551615UL)": "0xFFFFFFFFFFFFFFFF", "meta": { "level": 5 } },
{ "n": "TestProviderC:hiL", "iL": "0x77359400", "(-0x7fffffffffffffffL - 1L)": "0x8000000000000000", "meta": { "level": 5 } },
{ "n": "TestProviderC:huL", "uL": "0xEE6B2800", "(0x7fffffffffffffffL * 2UL + 1UL)": "0xFFFFFFFFFFFFFFFF", "meta": { "level": 5 } },
{ "n": "TestProviderC:f32", "f32": 3.1400001, "1/3": 0.333333343, "NaN": "-nan", "-Inf": "-inf", "meta": { "level": 5 } },
{ "n": "TestProviderC:f64", "f64": 6.2800000000000002, "1/3": 0.33333333333333331, "NaN": "-nan", "-Inf": "-inf", "meta": { "level": 5 } },
{ "n": "TestProviderC:b8", "b0": false, "b1": true, "(255)": 255, "meta": { "level": 5 } },
{ "n": "TestProviderC:b32", "b0": false, "b1": true, "(-2147483647-1)": -2147483648, "meta": { "level": 5 } },
{ "n": "TestProviderC:ch", "ch": "A", "FE": "Ăľ", "meta": { "level": 5 } },
{ "n": "TestProviderC:u16ch", "u16ch": "A", "FFFE": "ďżľ", "meta": { "level": 5 } },
{ "n": "TestProviderC:u32ch", "u32ch": "A", "FFFE": "ďżľ", "10FFFF": "ôŹżż", "FFFEFDFC": <>żżŻ·Ľ", "meta": { "level": 5 } },
{ "n": "TestProviderC:wch", "wch": "B", "FFFE": "ďżľ", "10FFFF": "ôŹżż", "0x7fffffff": "ýżżżżż", "meta": { "level": 5 } },
{ "n": "TestProviderC:ptr", "pSamplePtr": "0xFFFFFFFFFFFFCFC7", "UINTPTR_MAX": "0xFFFFFFFFFFFFFFFF", "meta": { "level": 5 } },
{ "n": "TestProviderC:pid", "i32": 2000000000, "(2147483647)": 2147483647, "meta": { "level": 5 } },
{ "n": "TestProviderC:port", "port80": 80, "(65535)": 65535, "meta": { "level": 5 } },
{ "n": "TestProviderC:errno", "(-2147483647-1)": "ERRNO(-2147483648)", "1": "EPERM(1)", "131": "ENOTRECOVERABLE(131)", "meta": { "level": 5 } },
{ "n": "TestProviderC:t32", "i32": "2033-05-18T03:33:20", "(-2147483647-1)": "1901-12-13T20:45:52", "(2147483647)": "2038-01-19T03:14:07", "0": "1970-01-01T00:00:00", "meta": { "level": 5 } },
{ "n": "TestProviderC:t64", "i64": "TIME(9000000000000000000)", "(-9223372036854775807L-1)": "TIME(-9223372036854775808)", "(9223372036854775807L)": "TIME(9223372036854775807)", "0": "1970-01-01T00:00:00", "-11644473600": "1601-01-01T00:00:00", "-11644473601": "1600-12-31T23:59:59", "910692730085": "30828-09-14T02:48:05", "910692730086": "30828-09-14T02:48:06", "meta": { "level": 5 } },
{ "n": "TestProviderC:guid", "guid": "01020304-0506-0708-0102-030405060708", "meta": { "level": 5 } },
{ "n": "TestProviderC:sz", "NULL": "", "ch10": "HowAreU8?", "meta": { "level": 5 } },
{ "n": "TestProviderC:sz8", "NULL": "", "ch10": "HowAreU8?", "meta": { "level": 5 } },
{ "n": "TestProviderC:wsz", "NULL": "", "wch10": "Goodbye!!", "meta": { "level": 5 } },
{ "n": "TestProviderC:sz16", "NULL": "", "u16ch10": "HowAreU16", "meta": { "level": 5 } },
{ "n": "TestProviderC:sz32", "NULL": "", "u32ch10": "HowAreU32", "meta": { "level": 5 } },
{ "n": "TestProviderC:csz", "NULL": "", "ch10": "HowAr", "meta": { "level": 5 } },
{ "n": "TestProviderC:csz8", "NULL": "", "ch10": "HowAr", "meta": { "level": 5 } },
{ "n": "TestProviderC:cwsz", "NULL": "", "wch10": "Goodb", "meta": { "level": 5 } },
{ "n": "TestProviderC:csz16", "NULL": "", "u16ch10": "HowAr", "meta": { "level": 5 } },
{ "n": "TestProviderC:csz32", "NULL": "", "u32ch10": "HowAr", "meta": { "level": 5 } },
{ "n": "TestProviderC:bin", "NULL": "", "ch10": "48 6f 77 41 72", "meta": { "level": 5 } },
{ "n": "TestProviderC:ipV4", "ipv4": "127.0.0.1", "(4294967295U)": "255.255.255.255", "meta": { "level": 5 } },
{ "n": "TestProviderC:ipV6", "ipv6": "102:304:506:708:90a:b0c:d0e:f10", "fefe..fe00": "fefe:fefe:fefe:fefe:fefe:fefe:fefe:fe00", "meta": { "level": 5 } },
{ "n": "TestProviderC:ai8", "a1": [ 100 ], "s": [ 100 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:au8", "a1": [ 200 ], "s": [ 200 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:ai16", "a1": [ 30000 ], "s": [ 30000 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:au16", "a1": [ 60000 ], "s": [ 60000 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:ai32", "a1": [ 2000000000 ], "s": [ 2000000000 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:au32", "a1": [ 4000000000 ], "s": [ 4000000000 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:ai64", "a1": [ 9000000000000000000 ], "s": [ 9000000000000000000 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:au64", "a1": [ 18000000000000000000 ], "s": [ 18000000000000000000 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:aiptr", "a1": [ 1234 ], "s": [ 1234 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:auptr", "a1": [ 4321 ], "s": [ 4321 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:aiL", "a1": [ 2000000000 ], "s": [ 2000000000 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:auL", "a1": [ 4000000000 ], "s": [ 4000000000 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:hai8", "a1": [ "0x64" ], "s": [ "0x64" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:hau8", "a1": [ "0xC8" ], "s": [ "0xC8" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:hai16", "a1": [ "0x7530" ], "s": [ "0x7530" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:hau16", "a1": [ "0xEA60" ], "s": [ "0xEA60" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:hai32", "a1": [ "0x77359400" ], "s": [ "0x77359400" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:hau32", "a1": [ "0xEE6B2800" ], "s": [ "0xEE6B2800" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:hai64", "a1": [ "0x7CE66C50E2840000" ], "s": [ "0x7CE66C50E2840000" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:hau64", "a1": [ "0xF9CCD8A1C5080000" ], "s": [ "0xF9CCD8A1C5080000" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:haiptr", "a1": [ "0x4D2" ], "s": [ "0x4D2" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:hauptr", "a1": [ "0x10E1" ], "s": [ "0x10E1" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:haiL", "a1": [ "0x77359400" ], "s": [ "0x77359400" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:hauL", "a1": [ "0xEE6B2800" ], "s": [ "0xEE6B2800" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:af32", "a1": [ 3.1400001 ], "s": [ 3.1400001 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:af64", "a1": [ 6.2800000000000002 ], "s": [ 6.2800000000000002 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:ab8", "a1": [ true ], "s": [ true ], "meta": { "level": 5 } },
{ "n": "TestProviderC:ab32", "a1": [ true ], "s": [ true ], "meta": { "level": 5 } },
{ "n": "TestProviderC:ach", "a4": [ "H", "o", "w", "A" ], "s5": [ "H", "o", "w", "A", "r" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:ach16", "a4": [ "H", "o", "w", "A" ], "s5": [ "H", "o", "w", "A", "r" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:ach32", "a4": [ "H", "o", "w", "A" ], "s5": [ "H", "o", "w", "A", "r" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:awch", "a4": [ "G", "o", "o", "d" ], "s5": [ "G", "o", "o", "d", "b" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:aptr", "a1": [ "0xFFFFFFFFFFFFCFC7" ], "s": [ "0xFFFFFFFFFFFFCFC7" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:apid", "a1": [ 2000000000 ], "s": [ 2000000000 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:aport", "a1": [ 24810 ], "s": [ 24810 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:aerrno", "a1": [ "ERRNO(2000000000)" ], "s": [ "ERRNO(2000000000)" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:aft", "a1": [ "2033-05-18T03:33:20" ], "s": [ "2033-05-18T03:33:20" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:auft", "a1": [ "TIME(9000000000000000000)" ], "s": [ "TIME(9000000000000000000)" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:ag", "s0": [ ], "s1": [ "01020304-0506-0708-0102-030405060708" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:ahexbytes", "s0": [ ], "s1": [ "01 02 03 04 05 06 07 08 01 02 03 04 05 06 07 08" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:Default", "V8": 200, "V16": 60000, "V32": 4000000000, "V64": 18000000000000000000, "V128": "01 02 03 04 05 06 07 08 01 02 03 04 05 06 07 08", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderC:HexBytes", "V8": "01", "V16": "01 02", "V32": "01 02 03 04", "V64": "01 02 03 04 05 06 07 08", "V128": "01 02 03 04 05 06 07 08 01 02 03 04 05 06 07 08", "NChar8": "68 6a 6b 6c", "NChar16": "68 00 6a 00 6b 00 6c 00", "NChar32": "68 00 00 00 6a 00 00 00 6b 00 00 00 6c 00 00 00", "LChar8": "68 6a 6b 6c", "LChar16": "68 00 6a 00 6b 00 6c 00", "LChar32": "68 00 00 00 6a 00 00 00 6b 00 00 00 6c 00 00 00", "meta": { "level": 5 } },
{ "n": "TestProviderC:Bool16", "false16": false, "true16": true, "u16": 60000, "i16": 30000, "meta": { "level": 5 } },
{ "n": "TestProviderC:StringUtf", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderC:StringUtfBom-NoBom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderC:StringXml-NoBom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderC:StringJson-NoBom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderC:StringUtfBom-Bom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderC:StringXml-Bom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderC:StringJson-Bom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderC:StringUtfBom-XBom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderC:StringXml-XBom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderC:StringJson-XBom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderC:Packed", "five": 5, "Struct1": { "NChar8": "hjkl" }, "StructArray": [ { "Struct2": { "K": "A", "NChar16": "68 00 6a 00 6b 00 6c 00" } }, { "Struct2": { "K": "B", "NChar16": "ff fe 68 00 6a 00 6b 00 6c 00" } }, { "Struct2": { "K": "C", "NChar16": "fe ff 00 68 00 6a 00 6b 00 6c" } } ], "five": 5, "meta": { "level": 5 } },
{ "n": "TestProviderC:Packed0", "five": 5, "Struct1": { "NChar8": "hjkl" }, "StructArray": [ ], "five": 5, "meta": { "level": 5 } },
{ "n": "TestProviderC:PackedComplexArray", "five": 5, "MyStrings3": [ "ABC", "", "XYZ" ], "five": 5, "meta": { "level": 5 } },
{ "n": "TestProviderC:EventCG", "enabled": true, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:EventCppG", "enabled": true, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:CScalars1", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:CScalars2", "meta": { "level": 1, "keyword": "0xF123456789ABCDEF", "opcode": 2 } },
{ "n": "TestProviderCpp:CScalars3", "Struct20;tag=0xBEEF": { "hi;tag=0x12": "hi", "ch10;tag=0x10": [ "H", "o", "w" ] }, "meta": { "id": 1000, "version": 11, "level": 4, "keyword": "0x85", "opcode": 3, "tag": "0x1234" } },
{ "n": "TestProviderCpp:Transfer00", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Transfer10", "meta": { "level": 5, "activity": "01020304-0506-0708-0102-030405060708" } },
{ "n": "TestProviderCpp:Transfer11", "meta": { "level": 5, "activity": "01020304-0506-0708-0102-030405060708", "relatedActivity": "01020304-0506-0708-0102-030405060708" } },
{ "n": "TestProviderCpp:i8", "i8": 100, "(-128)": -128, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:u8", "u8": 200, "(255)": 255, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:i16", "i16": 30000, "(-32767-1)": -32768, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:u16", "u16": 60000, "(65535)": 65535, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:i32", "i32": 2000000000, "(-2147483647-1)": -2147483648, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:u32", "u32": 4000000000, "(4294967295U)": 4294967295, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:i64", "i64": 9000000000000000000, "(-9223372036854775807L-1)": -9223372036854775808, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:u64", "u64": 18000000000000000000, "(18446744073709551615UL)": 18446744073709551615, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:iptr", "iptr": 1234, "(-9223372036854775807L-1)": -9223372036854775808, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:uptr", "uptr": 4321, "(18446744073709551615UL)": 18446744073709551615, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:iL", "iL": 2000000000, "(-0x7fffffffffffffffL - 1L)": -9223372036854775808, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:uL", "uL": 4000000000, "(0x7fffffffffffffffL * 2UL + 1UL)": 18446744073709551615, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hi8", "i8": "0x64", "(-128)": "0x80", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hu8", "u8": "0xC8", "(255)": "0xFF", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hi16", "i16": "0x7530", "(-32767-1)": "0x8000", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hu16", "u16": "0xEA60", "(65535)": "0xFFFF", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hi32", "i32": "0x77359400", "(-2147483647-1)": "0x80000000", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hu32", "u32": "0xEE6B2800", "(4294967295U)": "0xFFFFFFFF", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hi64", "i64": "0x7CE66C50E2840000", "(-9223372036854775807L-1)": "0x8000000000000000", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hu64", "u64": "0xF9CCD8A1C5080000", "(18446744073709551615UL)": "0xFFFFFFFFFFFFFFFF", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hiptr", "iptr": "0x4D2", "(-9223372036854775807L-1)": "0x8000000000000000", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:huptr", "uptr": "0x10E1", "(18446744073709551615UL)": "0xFFFFFFFFFFFFFFFF", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hiL", "iL": "0x77359400", "(-0x7fffffffffffffffL - 1L)": "0x8000000000000000", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:huL", "uL": "0xEE6B2800", "(0x7fffffffffffffffL * 2UL + 1UL)": "0xFFFFFFFFFFFFFFFF", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:f32", "f32": 3.1400001, "1/3": 0.333333343, "NaN": "-nan", "-Inf": "-inf", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:f64", "f64": 6.2800000000000002, "1/3": 0.33333333333333331, "NaN": "-nan", "-Inf": "-inf", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:b8", "b0": false, "b1": true, "(255)": 255, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:b32", "b0": false, "b1": true, "(-2147483647-1)": -2147483648, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ch", "ch": "A", "FE": "Ăľ", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:u16ch", "u16ch": "A", "FFFE": "ďżľ", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:u32ch", "u32ch": "A", "FFFE": "ďżľ", "10FFFF": "ôŹżż", "FFFEFDFC": <>żżŻ·Ľ", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:wch", "wch": "B", "FFFE": "ďżľ", "10FFFF": "ôŹżż", "0x7fffffff": "ýżżżżż", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ptr", "pSamplePtr": "0xFFFFFFFFFFFFCFC7", "UINTPTR_MAX": "0xFFFFFFFFFFFFFFFF", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:pid", "i32": 2000000000, "(2147483647)": 2147483647, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:port", "port80": 80, "(65535)": 65535, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:errno", "(-2147483647-1)": "ERRNO(-2147483648)", "1": "EPERM(1)", "131": "ENOTRECOVERABLE(131)", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:t32", "i32": "2033-05-18T03:33:20", "(-2147483647-1)": "1901-12-13T20:45:52", "(2147483647)": "2038-01-19T03:14:07", "0": "1970-01-01T00:00:00", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:t64", "i64": "TIME(9000000000000000000)", "(-9223372036854775807L-1)": "TIME(-9223372036854775808)", "(9223372036854775807L)": "TIME(9223372036854775807)", "0": "1970-01-01T00:00:00", "-11644473600": "1601-01-01T00:00:00", "-11644473601": "1600-12-31T23:59:59", "910692730085": "30828-09-14T02:48:05", "910692730086": "30828-09-14T02:48:06", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:guid", "guid": "01020304-0506-0708-0102-030405060708", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:sz", "NULL": "", "ch10": "HowAreU8?", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:sz8", "NULL": "", "ch10": "HowAreU8?", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:wsz", "NULL": "", "wch10": "Goodbye!!", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:sz16", "NULL": "", "u16ch10": "HowAreU16", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:sz32", "NULL": "", "u32ch10": "HowAreU32", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:csz", "NULL": "", "ch10": "HowAr", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:csz8", "NULL": "", "ch10": "HowAr", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:cwsz", "NULL": "", "wch10": "Goodb", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:csz16", "NULL": "", "u16ch10": "HowAr", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:csz32", "NULL": "", "u32ch10": "HowAr", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:bin", "NULL": "", "ch10": "48 6f 77 41 72", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ipV4", "ipv4": "127.0.0.1", "(4294967295U)": "255.255.255.255", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ipV6", "ipv6": "102:304:506:708:90a:b0c:d0e:f10", "fefe..fe00": "fefe:fefe:fefe:fefe:fefe:fefe:fefe:fe00", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ai8", "a1": [ 100 ], "s": [ 100 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:au8", "a1": [ 200 ], "s": [ 200 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ai16", "a1": [ 30000 ], "s": [ 30000 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:au16", "a1": [ 60000 ], "s": [ 60000 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ai32", "a1": [ 2000000000 ], "s": [ 2000000000 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:au32", "a1": [ 4000000000 ], "s": [ 4000000000 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ai64", "a1": [ 9000000000000000000 ], "s": [ 9000000000000000000 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:au64", "a1": [ 18000000000000000000 ], "s": [ 18000000000000000000 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:aiptr", "a1": [ 1234 ], "s": [ 1234 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:auptr", "a1": [ 4321 ], "s": [ 4321 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:aiL", "a1": [ 2000000000 ], "s": [ 2000000000 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:auL", "a1": [ 4000000000 ], "s": [ 4000000000 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hai8", "a1": [ "0x64" ], "s": [ "0x64" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hau8", "a1": [ "0xC8" ], "s": [ "0xC8" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hai16", "a1": [ "0x7530" ], "s": [ "0x7530" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hau16", "a1": [ "0xEA60" ], "s": [ "0xEA60" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hai32", "a1": [ "0x77359400" ], "s": [ "0x77359400" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hau32", "a1": [ "0xEE6B2800" ], "s": [ "0xEE6B2800" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hai64", "a1": [ "0x7CE66C50E2840000" ], "s": [ "0x7CE66C50E2840000" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hau64", "a1": [ "0xF9CCD8A1C5080000" ], "s": [ "0xF9CCD8A1C5080000" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:haiptr", "a1": [ "0x4D2" ], "s": [ "0x4D2" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hauptr", "a1": [ "0x10E1" ], "s": [ "0x10E1" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:haiL", "a1": [ "0x77359400" ], "s": [ "0x77359400" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hauL", "a1": [ "0xEE6B2800" ], "s": [ "0xEE6B2800" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:af32", "a1": [ 3.1400001 ], "s": [ 3.1400001 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:af64", "a1": [ 6.2800000000000002 ], "s": [ 6.2800000000000002 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ab8", "a1": [ true ], "s": [ true ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ab32", "a1": [ true ], "s": [ true ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ach", "a4": [ "H", "o", "w", "A" ], "s5": [ "H", "o", "w", "A", "r" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ach16", "a4": [ "H", "o", "w", "A" ], "s5": [ "H", "o", "w", "A", "r" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ach32", "a4": [ "H", "o", "w", "A" ], "s5": [ "H", "o", "w", "A", "r" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:awch", "a4": [ "G", "o", "o", "d" ], "s5": [ "G", "o", "o", "d", "b" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:aptr", "a1": [ "0xFFFFFFFFFFFFCFC7" ], "s": [ "0xFFFFFFFFFFFFCFC7" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:apid", "a1": [ 2000000000 ], "s": [ 2000000000 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:aport", "a1": [ 24810 ], "s": [ 24810 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:aerrno", "a1": [ "ERRNO(2000000000)" ], "s": [ "ERRNO(2000000000)" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:aft", "a1": [ "2033-05-18T03:33:20" ], "s": [ "2033-05-18T03:33:20" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:auft", "a1": [ "TIME(9000000000000000000)" ], "s": [ "TIME(9000000000000000000)" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ag", "s0": [ ], "s1": [ "01020304-0506-0708-0102-030405060708" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ahexbytes", "s0": [ ], "s1": [ "01 02 03 04 05 06 07 08 01 02 03 04 05 06 07 08" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Default", "V8": 200, "V16": 60000, "V32": 4000000000, "V64": 18000000000000000000, "V128": "01 02 03 04 05 06 07 08 01 02 03 04 05 06 07 08", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:HexBytes", "V8": "01", "V16": "01 02", "V32": "01 02 03 04", "V64": "01 02 03 04 05 06 07 08", "V128": "01 02 03 04 05 06 07 08 01 02 03 04 05 06 07 08", "NChar8": "68 6a 6b 6c", "NChar16": "68 00 6a 00 6b 00 6c 00", "NChar32": "68 00 00 00 6a 00 00 00 6b 00 00 00 6c 00 00 00", "LChar8": "68 6a 6b 6c", "LChar16": "68 00 6a 00 6b 00 6c 00", "LChar32": "68 00 00 00 6a 00 00 00 6b 00 00 00 6c 00 00 00", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Bool16", "false16": false, "true16": true, "u16": 60000, "i16": 30000, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:StringUtf", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:StringUtfBom-NoBom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:StringXml-NoBom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:StringJson-NoBom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:StringUtfBom-Bom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:StringXml-Bom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:StringJson-Bom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:StringUtfBom-XBom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:StringXml-XBom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:StringJson-XBom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Packed", "five": 5, "Struct1": { "NChar8": "hjkl" }, "StructArray": [ { "Struct2": { "K": "A", "NChar16": "68 00 6a 00 6b 00 6c 00" } }, { "Struct2": { "K": "B", "NChar16": "ff fe 68 00 6a 00 6b 00 6c 00" } }, { "Struct2": { "K": "C", "NChar16": "fe ff 00 68 00 6a 00 6b 00 6c" } } ], "five": 5, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Packed0", "five": 5, "Struct1": { "NChar8": "hjkl" }, "StructArray": [ ], "five": 5, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:PackedComplexArray", "five": 5, "MyStrings3": [ "ABC", "", "XYZ" ], "five": 5, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:bool", "false;tag=0x1234": false, "true": true, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:char", "0": "\u0000", "A": "A", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:char16", "0": "\u0000", "A": "A", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:char32", "0": "\u0000", "A": "A", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:wchar", "0": "\u0000", "A": "A", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:schar", "0": 0, "A": 65, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:uchar", "0": 0, "A": 65, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:sshort", "0": 0, "A": 65, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:ushort", "0": 0, "A": 65, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:sint", "0": 0, "A": 65, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:uint", "0": 0, "A": 65, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:slong", "0": 0, "A": 65, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:ulong", "0": 0, "A": 65, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:slonglong", "0": 0, "A": 65, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:ulonglong", "0": 0, "A": 65, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:float", "0": 0, "65": 65, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:double", "0": 0, "65": 65, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:void*", "0": "0x0", "p": "0xFFFFFFFFFFFFCFC7", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:cvoid*", "0": "0x0", "p": "0xFFFFFFFFFFFFCFC7", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:char*", "0": "", "hello": "hello", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:cchar*", "0": "", "hello": "hello", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:char16_t*", "0": "", "hello": "hello", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:cchar16_t*", "0": "", "hello": "hello", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:char32_t*", "0": "", "hello": "hello", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:cchar32_t*", "0": "", "hello": "hello", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:wchar_t*", "0": "", "hello": "hello", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:cwchar_t*", "0": "", "hello": "hello", "meta": { "level": 5 } } ]

View file

@ -0,0 +1,262 @@
"EventHeaderInterceptorLE64.dat": [
{ "n": "Long_Provider_Name_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX0123456789:LongProviderEvent", "meta": { "level": 5 } },
{ "n": "TestProviderC:EventCG", "enabled": true, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:EventCppG", "enabled": true, "meta": { "level": 5 } },
{ "n": "TestProviderC:CScalars1", "meta": { "level": 5 } },
{ "n": "TestProviderC:CScalars2", "meta": { "level": 1, "keyword": "0xF123456789ABCDEF", "opcode": 2 } },
{ "n": "TestProviderC:CScalars3", "Struct20;tag=0xBEEF": { "hi;tag=0x12": "hi", "ch10;tag=0x10": [ "H", "o", "w" ] }, "meta": { "id": 1000, "version": 11, "level": 4, "keyword": "0x85", "opcode": 3, "tag": "0x1234" } },
{ "n": "TestProviderC:Transfer00", "meta": { "level": 5 } },
{ "n": "TestProviderC:Transfer10", "meta": { "level": 5, "activity": "01020304-0506-0708-0102-030405060708" } },
{ "n": "TestProviderC:Transfer11", "meta": { "level": 5, "activity": "01020304-0506-0708-0102-030405060708", "relatedActivity": "01020304-0506-0708-0102-030405060708" } },
{ "n": "TestProviderC:i8", "i8": 100, "(-128)": -128, "meta": { "level": 5 } },
{ "n": "TestProviderC:u8", "u8": 200, "(255)": 255, "meta": { "level": 5 } },
{ "n": "TestProviderC:i16", "i16": 30000, "(-32767-1)": -32768, "meta": { "level": 5 } },
{ "n": "TestProviderC:u16", "u16": 60000, "(65535)": 65535, "meta": { "level": 5 } },
{ "n": "TestProviderC:i32", "i32": 2000000000, "(-2147483647-1)": -2147483648, "meta": { "level": 5 } },
{ "n": "TestProviderC:u32", "u32": 4000000000, "(4294967295U)": 4294967295, "meta": { "level": 5 } },
{ "n": "TestProviderC:i64", "i64": 9000000000000000000, "(-9223372036854775807L-1)": -9223372036854775808, "meta": { "level": 5 } },
{ "n": "TestProviderC:u64", "u64": 18000000000000000000, "(18446744073709551615UL)": 18446744073709551615, "meta": { "level": 5 } },
{ "n": "TestProviderC:iptr", "iptr": 1234, "(-9223372036854775807L-1)": -9223372036854775808, "meta": { "level": 5 } },
{ "n": "TestProviderC:uptr", "uptr": 4321, "(18446744073709551615UL)": 18446744073709551615, "meta": { "level": 5 } },
{ "n": "TestProviderC:iL", "iL": 2000000000, "(-0x7fffffffffffffffL - 1L)": -9223372036854775808, "meta": { "level": 5 } },
{ "n": "TestProviderC:uL", "uL": 4000000000, "(0x7fffffffffffffffL * 2UL + 1UL)": 18446744073709551615, "meta": { "level": 5 } },
{ "n": "TestProviderC:hi8", "i8": "0x64", "(-128)": "0x80", "meta": { "level": 5 } },
{ "n": "TestProviderC:hu8", "u8": "0xC8", "(255)": "0xFF", "meta": { "level": 5 } },
{ "n": "TestProviderC:hi16", "i16": "0x7530", "(-32767-1)": "0x8000", "meta": { "level": 5 } },
{ "n": "TestProviderC:hu16", "u16": "0xEA60", "(65535)": "0xFFFF", "meta": { "level": 5 } },
{ "n": "TestProviderC:hi32", "i32": "0x77359400", "(-2147483647-1)": "0x80000000", "meta": { "level": 5 } },
{ "n": "TestProviderC:hu32", "u32": "0xEE6B2800", "(4294967295U)": "0xFFFFFFFF", "meta": { "level": 5 } },
{ "n": "TestProviderC:hi64", "i64": "0x7CE66C50E2840000", "(-9223372036854775807L-1)": "0x8000000000000000", "meta": { "level": 5 } },
{ "n": "TestProviderC:hu64", "u64": "0xF9CCD8A1C5080000", "(18446744073709551615UL)": "0xFFFFFFFFFFFFFFFF", "meta": { "level": 5 } },
{ "n": "TestProviderC:hiptr", "iptr": "0x4D2", "(-9223372036854775807L-1)": "0x8000000000000000", "meta": { "level": 5 } },
{ "n": "TestProviderC:huptr", "uptr": "0x10E1", "(18446744073709551615UL)": "0xFFFFFFFFFFFFFFFF", "meta": { "level": 5 } },
{ "n": "TestProviderC:hiL", "iL": "0x77359400", "(-0x7fffffffffffffffL - 1L)": "0x8000000000000000", "meta": { "level": 5 } },
{ "n": "TestProviderC:huL", "uL": "0xEE6B2800", "(0x7fffffffffffffffL * 2UL + 1UL)": "0xFFFFFFFFFFFFFFFF", "meta": { "level": 5 } },
{ "n": "TestProviderC:f32", "f32": 3.1400001, "1/3": 0.333333343, "NaN": "-nan(ind)", "-Inf": "-inf", "meta": { "level": 5 } },
{ "n": "TestProviderC:f64", "f64": 6.2800000000000002, "1/3": 0.33333333333333331, "NaN": "-nan(ind)", "-Inf": "-inf", "meta": { "level": 5 } },
{ "n": "TestProviderC:b8", "b0": false, "b1": true, "(255)": 255, "meta": { "level": 5 } },
{ "n": "TestProviderC:b32", "b0": false, "b1": true, "(-2147483647-1)": -2147483648, "meta": { "level": 5 } },
{ "n": "TestProviderC:ch", "ch": "A", "FE": "Ăľ", "meta": { "level": 5 } },
{ "n": "TestProviderC:u16ch", "u16ch": "A", "FFFE": "ďżľ", "meta": { "level": 5 } },
{ "n": "TestProviderC:u32ch", "u32ch": "A", "FFFE": "ďżľ", "10FFFF": "ôŹżż", "FFFEFDFC": <>żżŻ·Ľ", "meta": { "level": 5 } },
{ "n": "TestProviderC:wch", "wch": "B", "FFFE": "ďżľ", "10FFFF": "ôŹżż", "0x7fffffff": "ýżżżżż", "meta": { "level": 5 } },
{ "n": "TestProviderC:ptr", "pSamplePtr": "0xFFFFFFFFFFFFCFC7", "UINTPTR_MAX": "0xFFFFFFFFFFFFFFFF", "meta": { "level": 5 } },
{ "n": "TestProviderC:pid", "i32": 2000000000, "(2147483647)": 2147483647, "meta": { "level": 5 } },
{ "n": "TestProviderC:port", "port80": 80, "(65535)": 65535, "meta": { "level": 5 } },
{ "n": "TestProviderC:errno", "(-2147483647-1)": "ERRNO(-2147483648)", "1": "EPERM(1)", "131": "ENOTRECOVERABLE(131)", "meta": { "level": 5 } },
{ "n": "TestProviderC:t32", "i32": "2033-05-18T03:33:20", "(-2147483647-1)": "1901-12-13T20:45:52", "(2147483647)": "2038-01-19T03:14:07", "0": "1970-01-01T00:00:00", "meta": { "level": 5 } },
{ "n": "TestProviderC:t64", "i64": "TIME(9000000000000000000)", "(-9223372036854775807L-1)": "TIME(-9223372036854775808)", "(9223372036854775807L)": "TIME(9223372036854775807)", "0": "1970-01-01T00:00:00", "-11644473600": "1601-01-01T00:00:00", "-11644473601": "TIME(-11644473601)", "910692730085": "30828-09-14T02:48:05", "910692730086": "TIME(910692730086)", "meta": { "level": 5 } },
{ "n": "TestProviderC:guid", "guid": "01020304-0506-0708-0102-030405060708", "meta": { "level": 5 } },
{ "n": "TestProviderC:sz", "NULL": "", "ch10": "HowAreU8?", "meta": { "level": 5 } },
{ "n": "TestProviderC:sz8", "NULL": "", "ch10": "HowAreU8?", "meta": { "level": 5 } },
{ "n": "TestProviderC:wsz", "NULL": "", "wch10": "Goodbye!!", "meta": { "level": 5 } },
{ "n": "TestProviderC:sz16", "NULL": "", "u16ch10": "HowAreU16", "meta": { "level": 5 } },
{ "n": "TestProviderC:sz32", "NULL": "", "u32ch10": "HowAreU32", "meta": { "level": 5 } },
{ "n": "TestProviderC:csz", "NULL": "", "ch10": "HowAr", "meta": { "level": 5 } },
{ "n": "TestProviderC:csz8", "NULL": "", "ch10": "HowAr", "meta": { "level": 5 } },
{ "n": "TestProviderC:cwsz", "NULL": "", "wch10": "Goodb", "meta": { "level": 5 } },
{ "n": "TestProviderC:csz16", "NULL": "", "u16ch10": "HowAr", "meta": { "level": 5 } },
{ "n": "TestProviderC:csz32", "NULL": "", "u32ch10": "HowAr", "meta": { "level": 5 } },
{ "n": "TestProviderC:bin", "NULL": "", "ch10": "48 6f 77 41 72", "meta": { "level": 5 } },
{ "n": "TestProviderC:ipV4", "ipv4": "127.0.0.1", "(4294967295U)": "255.255.255.255", "meta": { "level": 5 } },
{ "n": "TestProviderC:ipV6", "ipv6": "102:304:506:708:90a:b0c:d0e:f10", "fefe..fe00": "fefe:fefe:fefe:fefe:fefe:fefe:fefe:fe00", "meta": { "level": 5 } },
{ "n": "TestProviderC:ai8", "a1": [ 100 ], "s": [ 100 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:au8", "a1": [ 200 ], "s": [ 200 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:ai16", "a1": [ 30000 ], "s": [ 30000 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:au16", "a1": [ 60000 ], "s": [ 60000 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:ai32", "a1": [ 2000000000 ], "s": [ 2000000000 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:au32", "a1": [ 4000000000 ], "s": [ 4000000000 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:ai64", "a1": [ 9000000000000000000 ], "s": [ 9000000000000000000 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:au64", "a1": [ 18000000000000000000 ], "s": [ 18000000000000000000 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:aiptr", "a1": [ 1234 ], "s": [ 1234 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:auptr", "a1": [ 4321 ], "s": [ 4321 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:aiL", "a1": [ 2000000000 ], "s": [ 2000000000 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:auL", "a1": [ 4000000000 ], "s": [ 4000000000 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:hai8", "a1": [ "0x64" ], "s": [ "0x64" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:hau8", "a1": [ "0xC8" ], "s": [ "0xC8" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:hai16", "a1": [ "0x7530" ], "s": [ "0x7530" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:hau16", "a1": [ "0xEA60" ], "s": [ "0xEA60" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:hai32", "a1": [ "0x77359400" ], "s": [ "0x77359400" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:hau32", "a1": [ "0xEE6B2800" ], "s": [ "0xEE6B2800" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:hai64", "a1": [ "0x7CE66C50E2840000" ], "s": [ "0x7CE66C50E2840000" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:hau64", "a1": [ "0xF9CCD8A1C5080000" ], "s": [ "0xF9CCD8A1C5080000" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:haiptr", "a1": [ "0x4D2" ], "s": [ "0x4D2" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:hauptr", "a1": [ "0x10E1" ], "s": [ "0x10E1" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:haiL", "a1": [ "0x77359400" ], "s": [ "0x77359400" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:hauL", "a1": [ "0xEE6B2800" ], "s": [ "0xEE6B2800" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:af32", "a1": [ 3.1400001 ], "s": [ 3.1400001 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:af64", "a1": [ 6.2800000000000002 ], "s": [ 6.2800000000000002 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:ab8", "a1": [ true ], "s": [ true ], "meta": { "level": 5 } },
{ "n": "TestProviderC:ab32", "a1": [ true ], "s": [ true ], "meta": { "level": 5 } },
{ "n": "TestProviderC:ach", "a4": [ "H", "o", "w", "A" ], "s5": [ "H", "o", "w", "A", "r" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:ach16", "a4": [ "H", "o", "w", "A" ], "s5": [ "H", "o", "w", "A", "r" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:ach32", "a4": [ "H", "o", "w", "A" ], "s5": [ "H", "o", "w", "A", "r" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:awch", "a4": [ "G", "o", "o", "d" ], "s5": [ "G", "o", "o", "d", "b" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:aptr", "a1": [ "0xFFFFFFFFFFFFCFC7" ], "s": [ "0xFFFFFFFFFFFFCFC7" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:apid", "a1": [ 2000000000 ], "s": [ 2000000000 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:aport", "a1": [ 24810 ], "s": [ 24810 ], "meta": { "level": 5 } },
{ "n": "TestProviderC:aerrno", "a1": [ "ERRNO(2000000000)" ], "s": [ "ERRNO(2000000000)" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:aft", "a1": [ "2033-05-18T03:33:20" ], "s": [ "2033-05-18T03:33:20" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:auft", "a1": [ "TIME(9000000000000000000)" ], "s": [ "TIME(9000000000000000000)" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:ag", "s0": [ ], "s1": [ "01020304-0506-0708-0102-030405060708" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:ahexbytes", "s0": [ ], "s1": [ "01 02 03 04 05 06 07 08 01 02 03 04 05 06 07 08" ], "meta": { "level": 5 } },
{ "n": "TestProviderC:Default", "V8": 200, "V16": 60000, "V32": 4000000000, "V64": 18000000000000000000, "V128": "01 02 03 04 05 06 07 08 01 02 03 04 05 06 07 08", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderC:HexBytes", "V8": "01", "V16": "01 02", "V32": "01 02 03 04", "V64": "01 02 03 04 05 06 07 08", "V128": "01 02 03 04 05 06 07 08 01 02 03 04 05 06 07 08", "NChar8": "68 6a 6b 6c", "NChar16": "68 00 6a 00 6b 00 6c 00", "NChar32": "68 00 00 00 6a 00 00 00 6b 00 00 00 6c 00 00 00", "LChar8": "68 6a 6b 6c", "LChar16": "68 00 6a 00 6b 00 6c 00", "LChar32": "68 00 00 00 6a 00 00 00 6b 00 00 00 6c 00 00 00", "meta": { "level": 5 } },
{ "n": "TestProviderC:Bool16", "false16": false, "true16": true, "u16": 60000, "i16": 30000, "meta": { "level": 5 } },
{ "n": "TestProviderC:StringUtf", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderC:StringUtfBom-NoBom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderC:StringXml-NoBom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderC:StringJson-NoBom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderC:StringUtfBom-Bom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderC:StringXml-Bom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderC:StringJson-Bom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderC:StringUtfBom-XBom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderC:StringXml-XBom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderC:StringJson-XBom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderC:Packed", "five": 5, "Struct1": { "NChar8": "hjkl" }, "StructArray": [ { "Struct2": { "K": "A", "NChar16": "68 00 6a 00 6b 00 6c 00" } }, { "Struct2": { "K": "B", "NChar16": "ff fe 68 00 6a 00 6b 00 6c 00" } }, { "Struct2": { "K": "C", "NChar16": "fe ff 00 68 00 6a 00 6b 00 6c" } } ], "five": 5, "meta": { "level": 5 } },
{ "n": "TestProviderC:Packed0", "five": 5, "Struct1": { "NChar8": "hjkl" }, "StructArray": [ ], "five": 5, "meta": { "level": 5 } },
{ "n": "TestProviderC:PackedComplexArray", "five": 5, "MyStrings3": [ "ABC", "", "XYZ" ], "five": 5, "meta": { "level": 5 } },
{ "n": "TestProviderC:EventCG", "enabled": true, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:EventCppG", "enabled": true, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:CScalars1", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:CScalars2", "meta": { "level": 1, "keyword": "0xF123456789ABCDEF", "opcode": 2 } },
{ "n": "TestProviderCpp:CScalars3", "Struct20;tag=0xBEEF": { "hi;tag=0x12": "hi", "ch10;tag=0x10": [ "H", "o", "w" ] }, "meta": { "id": 1000, "version": 11, "level": 4, "keyword": "0x85", "opcode": 3, "tag": "0x1234" } },
{ "n": "TestProviderCpp:Transfer00", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Transfer10", "meta": { "level": 5, "activity": "01020304-0506-0708-0102-030405060708" } },
{ "n": "TestProviderCpp:Transfer11", "meta": { "level": 5, "activity": "01020304-0506-0708-0102-030405060708", "relatedActivity": "01020304-0506-0708-0102-030405060708" } },
{ "n": "TestProviderCpp:i8", "i8": 100, "(-128)": -128, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:u8", "u8": 200, "(255)": 255, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:i16", "i16": 30000, "(-32767-1)": -32768, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:u16", "u16": 60000, "(65535)": 65535, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:i32", "i32": 2000000000, "(-2147483647-1)": -2147483648, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:u32", "u32": 4000000000, "(4294967295U)": 4294967295, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:i64", "i64": 9000000000000000000, "(-9223372036854775807L-1)": -9223372036854775808, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:u64", "u64": 18000000000000000000, "(18446744073709551615UL)": 18446744073709551615, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:iptr", "iptr": 1234, "(-9223372036854775807L-1)": -9223372036854775808, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:uptr", "uptr": 4321, "(18446744073709551615UL)": 18446744073709551615, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:iL", "iL": 2000000000, "(-0x7fffffffffffffffL - 1L)": -9223372036854775808, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:uL", "uL": 4000000000, "(0x7fffffffffffffffL * 2UL + 1UL)": 18446744073709551615, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hi8", "i8": "0x64", "(-128)": "0x80", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hu8", "u8": "0xC8", "(255)": "0xFF", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hi16", "i16": "0x7530", "(-32767-1)": "0x8000", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hu16", "u16": "0xEA60", "(65535)": "0xFFFF", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hi32", "i32": "0x77359400", "(-2147483647-1)": "0x80000000", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hu32", "u32": "0xEE6B2800", "(4294967295U)": "0xFFFFFFFF", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hi64", "i64": "0x7CE66C50E2840000", "(-9223372036854775807L-1)": "0x8000000000000000", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hu64", "u64": "0xF9CCD8A1C5080000", "(18446744073709551615UL)": "0xFFFFFFFFFFFFFFFF", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hiptr", "iptr": "0x4D2", "(-9223372036854775807L-1)": "0x8000000000000000", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:huptr", "uptr": "0x10E1", "(18446744073709551615UL)": "0xFFFFFFFFFFFFFFFF", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hiL", "iL": "0x77359400", "(-0x7fffffffffffffffL - 1L)": "0x8000000000000000", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:huL", "uL": "0xEE6B2800", "(0x7fffffffffffffffL * 2UL + 1UL)": "0xFFFFFFFFFFFFFFFF", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:f32", "f32": 3.1400001, "1/3": 0.333333343, "NaN": "-nan(ind)", "-Inf": "-inf", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:f64", "f64": 6.2800000000000002, "1/3": 0.33333333333333331, "NaN": "-nan(ind)", "-Inf": "-inf", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:b8", "b0": false, "b1": true, "(255)": 255, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:b32", "b0": false, "b1": true, "(-2147483647-1)": -2147483648, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ch", "ch": "A", "FE": "Ăľ", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:u16ch", "u16ch": "A", "FFFE": "ďżľ", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:u32ch", "u32ch": "A", "FFFE": "ďżľ", "10FFFF": "ôŹżż", "FFFEFDFC": <>żżŻ·Ľ", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:wch", "wch": "B", "FFFE": "ďżľ", "10FFFF": "ôŹżż", "0x7fffffff": "ýżżżżż", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ptr", "pSamplePtr": "0xFFFFFFFFFFFFCFC7", "UINTPTR_MAX": "0xFFFFFFFFFFFFFFFF", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:pid", "i32": 2000000000, "(2147483647)": 2147483647, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:port", "port80": 80, "(65535)": 65535, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:errno", "(-2147483647-1)": "ERRNO(-2147483648)", "1": "EPERM(1)", "131": "ENOTRECOVERABLE(131)", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:t32", "i32": "2033-05-18T03:33:20", "(-2147483647-1)": "1901-12-13T20:45:52", "(2147483647)": "2038-01-19T03:14:07", "0": "1970-01-01T00:00:00", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:t64", "i64": "TIME(9000000000000000000)", "(-9223372036854775807L-1)": "TIME(-9223372036854775808)", "(9223372036854775807L)": "TIME(9223372036854775807)", "0": "1970-01-01T00:00:00", "-11644473600": "1601-01-01T00:00:00", "-11644473601": "TIME(-11644473601)", "910692730085": "30828-09-14T02:48:05", "910692730086": "TIME(910692730086)", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:guid", "guid": "01020304-0506-0708-0102-030405060708", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:sz", "NULL": "", "ch10": "HowAreU8?", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:sz8", "NULL": "", "ch10": "HowAreU8?", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:wsz", "NULL": "", "wch10": "Goodbye!!", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:sz16", "NULL": "", "u16ch10": "HowAreU16", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:sz32", "NULL": "", "u32ch10": "HowAreU32", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:csz", "NULL": "", "ch10": "HowAr", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:csz8", "NULL": "", "ch10": "HowAr", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:cwsz", "NULL": "", "wch10": "Goodb", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:csz16", "NULL": "", "u16ch10": "HowAr", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:csz32", "NULL": "", "u32ch10": "HowAr", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:bin", "NULL": "", "ch10": "48 6f 77 41 72", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ipV4", "ipv4": "127.0.0.1", "(4294967295U)": "255.255.255.255", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ipV6", "ipv6": "102:304:506:708:90a:b0c:d0e:f10", "fefe..fe00": "fefe:fefe:fefe:fefe:fefe:fefe:fefe:fe00", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ai8", "a1": [ 100 ], "s": [ 100 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:au8", "a1": [ 200 ], "s": [ 200 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ai16", "a1": [ 30000 ], "s": [ 30000 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:au16", "a1": [ 60000 ], "s": [ 60000 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ai32", "a1": [ 2000000000 ], "s": [ 2000000000 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:au32", "a1": [ 4000000000 ], "s": [ 4000000000 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ai64", "a1": [ 9000000000000000000 ], "s": [ 9000000000000000000 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:au64", "a1": [ 18000000000000000000 ], "s": [ 18000000000000000000 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:aiptr", "a1": [ 1234 ], "s": [ 1234 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:auptr", "a1": [ 4321 ], "s": [ 4321 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:aiL", "a1": [ 2000000000 ], "s": [ 2000000000 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:auL", "a1": [ 4000000000 ], "s": [ 4000000000 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hai8", "a1": [ "0x64" ], "s": [ "0x64" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hau8", "a1": [ "0xC8" ], "s": [ "0xC8" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hai16", "a1": [ "0x7530" ], "s": [ "0x7530" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hau16", "a1": [ "0xEA60" ], "s": [ "0xEA60" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hai32", "a1": [ "0x77359400" ], "s": [ "0x77359400" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hau32", "a1": [ "0xEE6B2800" ], "s": [ "0xEE6B2800" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hai64", "a1": [ "0x7CE66C50E2840000" ], "s": [ "0x7CE66C50E2840000" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hau64", "a1": [ "0xF9CCD8A1C5080000" ], "s": [ "0xF9CCD8A1C5080000" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:haiptr", "a1": [ "0x4D2" ], "s": [ "0x4D2" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hauptr", "a1": [ "0x10E1" ], "s": [ "0x10E1" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:haiL", "a1": [ "0x77359400" ], "s": [ "0x77359400" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:hauL", "a1": [ "0xEE6B2800" ], "s": [ "0xEE6B2800" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:af32", "a1": [ 3.1400001 ], "s": [ 3.1400001 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:af64", "a1": [ 6.2800000000000002 ], "s": [ 6.2800000000000002 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ab8", "a1": [ true ], "s": [ true ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ab32", "a1": [ true ], "s": [ true ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ach", "a4": [ "H", "o", "w", "A" ], "s5": [ "H", "o", "w", "A", "r" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ach16", "a4": [ "H", "o", "w", "A" ], "s5": [ "H", "o", "w", "A", "r" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ach32", "a4": [ "H", "o", "w", "A" ], "s5": [ "H", "o", "w", "A", "r" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:awch", "a4": [ "G", "o", "o", "d" ], "s5": [ "G", "o", "o", "d", "b" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:aptr", "a1": [ "0xFFFFFFFFFFFFCFC7" ], "s": [ "0xFFFFFFFFFFFFCFC7" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:apid", "a1": [ 2000000000 ], "s": [ 2000000000 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:aport", "a1": [ 24810 ], "s": [ 24810 ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:aerrno", "a1": [ "ERRNO(2000000000)" ], "s": [ "ERRNO(2000000000)" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:aft", "a1": [ "2033-05-18T03:33:20" ], "s": [ "2033-05-18T03:33:20" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:auft", "a1": [ "TIME(9000000000000000000)" ], "s": [ "TIME(9000000000000000000)" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ag", "s0": [ ], "s1": [ "01020304-0506-0708-0102-030405060708" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:ahexbytes", "s0": [ ], "s1": [ "01 02 03 04 05 06 07 08 01 02 03 04 05 06 07 08" ], "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Default", "V8": 200, "V16": 60000, "V32": 4000000000, "V64": 18000000000000000000, "V128": "01 02 03 04 05 06 07 08 01 02 03 04 05 06 07 08", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:HexBytes", "V8": "01", "V16": "01 02", "V32": "01 02 03 04", "V64": "01 02 03 04 05 06 07 08", "V128": "01 02 03 04 05 06 07 08 01 02 03 04 05 06 07 08", "NChar8": "68 6a 6b 6c", "NChar16": "68 00 6a 00 6b 00 6c 00", "NChar32": "68 00 00 00 6a 00 00 00 6b 00 00 00 6c 00 00 00", "LChar8": "68 6a 6b 6c", "LChar16": "68 00 6a 00 6b 00 6c 00", "LChar32": "68 00 00 00 6a 00 00 00 6b 00 00 00 6c 00 00 00", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Bool16", "false16": false, "true16": true, "u16": 60000, "i16": 30000, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:StringUtf", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:StringUtfBom-NoBom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:StringXml-NoBom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:StringJson-NoBom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:StringUtfBom-Bom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:StringXml-Bom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:StringJson-Bom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:StringUtfBom-XBom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:StringXml-XBom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:StringJson-XBom", "NChar8": "hjkl", "NChar16": "hjkl", "NChar32": "hjkl", "LChar8": "hjkl", "LChar16": "hjkl", "LChar32": "hjkl", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Packed", "five": 5, "Struct1": { "NChar8": "hjkl" }, "StructArray": [ { "Struct2": { "K": "A", "NChar16": "68 00 6a 00 6b 00 6c 00" } }, { "Struct2": { "K": "B", "NChar16": "ff fe 68 00 6a 00 6b 00 6c 00" } }, { "Struct2": { "K": "C", "NChar16": "fe ff 00 68 00 6a 00 6b 00 6c" } } ], "five": 5, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Packed0", "five": 5, "Struct1": { "NChar8": "hjkl" }, "StructArray": [ ], "five": 5, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:PackedComplexArray", "five": 5, "MyStrings3": [ "ABC", "", "XYZ" ], "five": 5, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:bool", "false;tag=0x1234": false, "true": true, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:char", "0": "\u0000", "A": "A", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:char16", "0": "\u0000", "A": "A", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:char32", "0": "\u0000", "A": "A", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:wchar", "0": "\u0000", "A": "A", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:schar", "0": 0, "A": 65, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:uchar", "0": 0, "A": 65, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:sshort", "0": 0, "A": 65, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:ushort", "0": 0, "A": 65, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:sint", "0": 0, "A": 65, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:uint", "0": 0, "A": 65, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:slong", "0": 0, "A": 65, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:ulong", "0": 0, "A": 65, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:slonglong", "0": 0, "A": 65, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:ulonglong", "0": 0, "A": 65, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:float", "0": 0, "65": 65, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:double", "0": 0, "65": 65, "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:void*", "0": "0x0", "p": "0xFFFFFFFFFFFFCFC7", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:cvoid*", "0": "0x0", "p": "0xFFFFFFFFFFFFCFC7", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:char*", "0": "", "hello": "hello", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:cchar*", "0": "", "hello": "hello", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:char16_t*", "0": "", "hello": "hello", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:cchar16_t*", "0": "", "hello": "hello", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:char32_t*", "0": "", "hello": "hello", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:cchar32_t*", "0": "", "hello": "hello", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:wchar_t*", "0": "", "hello": "hello", "meta": { "level": 5 } },
{ "n": "TestProviderCpp:Value:cwchar_t*", "0": "", "hello": "hello", "meta": { "level": 5 } } ]

View file

@ -0,0 +1,45 @@
cmake_minimum_required(VERSION 3.10)
include(../version.cmake)
project(eventheader-decode-cpp
VERSION ${LINUXTRACEPOINTS_VERSION}
DESCRIPTION "EventHeader tracepoint decoding for C/C++"
HOMEPAGE_URL "https://github.com/microsoft/LinuxTracepoints"
LANGUAGES CXX)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
set(BUILD_SAMPLES ON CACHE BOOL "Build sample code")
set(BUILD_TOOLS ON CACHE BOOL "Build tool code")
if(NOT TARGET tracepoint-decode)
find_package(tracepoint-decode ${TRACEPOINT_DECODE_MINVER} REQUIRED)
endif()
if(NOT TARGET eventheader-headers)
find_package(eventheader-headers ${EVENTHEADER_HEADERS_MINVER} REQUIRED)
endif()
if(WIN32)
add_compile_options(/W4 /WX /permissive-)
else()
add_compile_options(
-Wall
-Wextra
-Wformat
-Wformat-security
-Werror=format-security
-Wstack-protector
-Werror=stack-protector)
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
add_compile_options(-D_FORTIFY_SOURCE=2)
endif()
endif()
add_subdirectory(src)
if(BUILD_SAMPLES)
add_subdirectory(samples)
endif()
if(BUILD_TOOLS)
add_subdirectory(tools)
endif()

View file

@ -0,0 +1,11 @@
# libeventheader-decode-cpp
C++ library for decoding events that use the eventheader envelope.
- **[EventEnumerator.h](include/eventheader/EventEnumerator.h):**
Splits an eventheader-encoded event into fields.
- **[EventFormatter.h](include/eventheader/EventFormatter.h):**
Turns events or fields into strings.
- **[decode-perf](samples/decode-perf.cpp):**
Simple tool that uses `EventFormatter` and `PerfDataFile` to decode a
`perf.data` file into JSON text. Works on Linux or Windows.

View file

@ -0,0 +1,673 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#ifndef _included_EventEnumerator_h
#define _included_EventEnumerator_h 1
#include <eventheader/eventheader.h>
#include <stdint.h>
#include <errno.h>
#ifdef _WIN32
#include <sal.h>
#endif
#ifndef _In_reads_bytes_
#define _In_reads_bytes_(cb)
#endif
#ifndef _Out_
#define _Out_
#endif
#ifndef _Field_z_
#define _Field_z_
#endif
#ifndef _Field_size_bytes_
#define _Field_size_bytes_(cb)
#endif
#ifndef _Field_size_bytes_opt_
#define _Field_size_bytes_opt_(cb)
#endif
namespace eventheader_decode
{
// Forward declarations:
class EventEnumerator;
enum EventEnumeratorState : uint8_t;
enum EventEnumeratorError : uint8_t;
struct EventInfo;
struct EventItemInfo;
struct EventDataPosition;
/// <summary>
/// Helper for decoding EventHeader events.
/// </summary>
class EventEnumerator
{
static event_field_encoding const EncodingCountMask = static_cast<event_field_encoding>(
event_field_encoding_carray_flag | event_field_encoding_varray_flag);
static event_field_encoding const ReadFieldError = event_field_encoding_invalid;
/// <summary>
/// Substate allows us to flatten
/// "switch (state) { case X: if (condition) ... }" to
/// "switch (substate) { case X_condition: ... }"
/// which potentially improves performance.
/// </summary>
enum SubState : uint8_t
{
SubState_None,
SubState_Error,
SubState_AfterLastItem,
SubState_BeforeFirstItem,
SubState_Value_Metadata,
SubState_Value_Scalar,
SubState_Value_SimpleArrayElement,
SubState_Value_ComplexArrayElement,
SubState_ArrayBegin,
SubState_ArrayEnd,
SubState_StructBegin,
SubState_StructEnd,
};
struct StackEntry
{
uint16_t NextOffset; // m_metaBuf[NextOffset] starts next field's name.
uint16_t NameOffset; // m_metaBuf[NameOffset] starts current field's name.
uint16_t NameSize; // m_metaBuf[NameOffset + NameSize + 1] starts current field's type.
uint16_t ArrayIndex;
uint16_t ArrayCount;
uint8_t RemainingFieldCount; // Number of NextProperty() calls before popping stack.
uint8_t ArrayFlags; // Encoding & EncodingCountMask
};
struct FieldType
{
event_field_encoding Encoding : 8;
event_field_format Format : 8;
unsigned Tag : 16;
};
private:
// Set up by StartEvent:
eventheader m_header;
uint64_t m_keyword;
uint8_t const* m_metaBuf;
uint8_t const* m_dataBuf;
uint8_t const* m_activityIdBuf;
char const* m_tracepointName; // Not nul-terminated.
uint8_t m_tracepointNameLength;
uint8_t m_providerNameLength; // Index into m_tracepointName
uint8_t m_optionsIndex; // Index into m_tracepointName
uint16_t m_metaEnd;
uint8_t m_activityIdSize;
bool m_needByteSwap;
uint16_t m_eventNameSize; // Name starts at m_metaBuf
uint32_t m_dataEnd;
// Values change during enumeration:
uint32_t m_dataPosRaw;
uint32_t m_moveNextRemaining;
StackEntry m_stackTop;
uint8_t m_stackIndex; // Number of items currently on stack.
EventEnumeratorState m_state;
SubState m_subState;
EventEnumeratorError m_lastError;
uint8_t m_elementSize; // 0 if item is variable-size or complex.
FieldType m_fieldType; // Note: fieldType.Encoding is cooked.
uint32_t m_dataPosCooked;
uint32_t m_itemSizeRaw;
uint32_t m_itemSizeCooked;
// Limit events to 8 levels of nested structures.
StackEntry m_stack[8];
public:
static uint32_t const MoveNextLimitDefault = 4096;
/// <summary>
/// Initializes a new instance of EventEnumerator. Sets State to None.
/// </summary>
EventEnumerator() noexcept;
/// <summary>
/// Returns the current state.
/// </summary>
EventEnumeratorState
State() const noexcept;
/// <summary>
/// Gets status for the most recent call to StartEvent, MoveNext, or MoveNextSibling.
/// </summary>
EventEnumeratorError
LastError() const noexcept;
/// <summary>
/// Sets State to None.
/// </summary>
void
Clear() noexcept;
/// <summary>
/// <para>
/// Starts decoding the specified EventHeader event: decodes the header and
/// positions the enumerator before the first item.
/// </para><para>
/// On success, changes the state to BeforeFirstItem and returns true.
/// On failure, changes the state to None (not Error) and returns false.
/// </para><para>
/// Note that the enumerator stores the pchTracepointName and pData pointers but
/// does not copy the referenced data, so the referenced data must remain valid
/// and unchanged while you are processing the data with this enumerator (i.e.
/// do not deallocate or overwrite the name or data until you call Clear, make
/// another call to StartEvent, or destroy this EventEnumerator instance).
/// </para>
/// </summary>
/// <param name="pchTracepointName">Set to tep_event->name, e.g. "MyProvider_L4K1".
/// Must follow the tracepoint name rules described in eventheader.h.</param>
/// <param name="cchTracepointName">Set to strlen(tep_event->name). Must be less
/// than EVENTHEADER_NAME_MAX.</param>
/// <param name="pData">Set to pointer to the start of the event data (the eventheader_flags
/// field of the event header), usually something like
/// tep_record->data + tep_event->format.fields[0].offset.</param>
/// <param name="cbData">Set to size of the data, usually something like
/// tep_record->size - tep_event->format.fields[0].offset.</param>
/// <param name="moveNextLimit">Set to the maximum number of MoveNext calls to allow when
/// processing this event (to guard against DoS attacks from a maliciously-crafted
/// event).</param>
/// <returns>Returns false for failure. Check LastError for details.</returns>
bool
StartEvent(
_In_reads_bytes_(cchTracepointName) char const* pchTracepointName, // e.g. "MyProvider_L4K1"
size_t cchTracepointName, // e.g. strlen(pchTracepointName)
_In_reads_bytes_(cbData) void const* pData, // points at the eventheader_flags field
size_t cbData, // size in bytes of the pData buffer
uint32_t moveNextLimit = MoveNextLimitDefault) noexcept;
/// <summary>
/// <para>
/// Positions the enumerator before the first item.
/// </para><para>
/// PRECONDITION: Can be called when State != None, i.e. at any time after a
/// successful call to StartEvent, until a call to Clear.
/// </para>
/// </summary>
void
Reset(uint32_t moveNextLimit = MoveNextLimitDefault) noexcept;
/// <summary>
/// <para>
/// Moves the enumerator to the next item in the current event, or to the end
/// of the event if no more items. Returns true if moved to a valid item,
/// false if no more items or decoding error.
/// </para><para>
/// PRECONDITION: Can be called when State >= BeforeFirstItem, i.e. after a
/// successful call to StartEvent, until MoveNext returns false.
/// </para><para>
/// Typically called in a loop until it returns false, e.g.:
/// </para><code>
/// if (!e.StartEvent(...)) return e.LastError();
/// while (e.MoveNext())
/// {
/// EventItemInfo item = e.GetItemInfo();
/// switch (e.State())
/// {
/// case EventEnumeratorState_Value:
/// DoValue(item);
/// break;
/// case EventEnumeratorState_StructBegin:
/// DoStructBegin(item);
/// break;
/// case EventEnumeratorState_StructEnd:
/// DoStructEnd(item);
/// break;
/// case EventEnumeratorState_ArrayBegin:
/// DoArrayBegin(item);
/// break;
/// case EventEnumeratorState_ArrayEnd:
/// DoArrayEnd(item);
/// break;
/// }
/// }
/// return e.LastError();
/// </code>
/// </summary>
/// <returns>
/// Returns true if moved to a valid item.
/// Returns false and sets state to AfterLastItem if no more items.
/// Returns false and sets state to Error for decoding error.
/// Check LastError for details.
/// </returns>
bool
MoveNext() noexcept;
/// <summary>
/// <para>
/// Moves the enumerator to the next sibling of the current item, or to the end
/// of the event if no more items. Returns true if moved to a valid item, false
/// if no more items or decoding error.
/// </para><para>
/// PRECONDITION: Can be called when State >= BeforeFirstItem, i.e. after a
/// successful call to StartEvent, until MoveNext returns false.
/// </para><list type="bullet"><item>
/// If the current item is ArrayBegin or StructBegin, this efficiently moves
/// enumeration to AFTER the corresponding ArrayEnd or StructEnd.
/// </item><item>
/// Otherwise, this is the same as MoveNext.
/// </item></list><para>
/// Typical use for this method is to efficiently skip past an array of fixed-size
/// items (i.e. an array where ElementSize is nonzero) when you process all of the
/// array items within the ArrayBegin state.
/// </para><code>
/// if (!e.StartEvent(...)) return e.LastError(); // Error.
/// if (!e.MoveNext()) return e.LastError(); // AfterLastItem or Error.
/// while (true)
/// {
/// EventItemInfo item = e.GetItemInfo();
/// switch (e.State())
/// {
/// case EventEnumeratorState_Value:
/// DoValue(item);
/// break;
/// case EventEnumeratorState_StructBegin:
/// DoStructBegin(item);
/// break;
/// case EventEnumeratorState_StructEnd:
/// DoStructEnd(item);
/// break;
/// case EventEnumeratorState_ArrayBegin:
/// if (e.ElementSize == 0)
/// {
/// DoComplexArrayBegin(item);
/// }
/// else
/// {
/// // Process the entire array directly without using the enumerator.
/// DoSimpleArrayBegin(item);
/// for (unsigned i = 0; i != item.ArrayCount; i++)
/// {
/// DoArrayElement(item, i);
/// }
/// DoSimpleArrayEnd(item);
///
/// // Skip the entire array at once.
/// if (!e.MoveNextSibling()) // Instead of MoveNext().
/// {
/// return e.LastError(); // AfterLastItem or Error.
/// }
/// continue; // Skip the MoveNext().
/// }
/// break;
/// case EventEnumeratorState_ArrayEnd:
/// DoComplexArrayEnd(item);
/// break;
/// }
///
/// if (!e.MoveNext())
/// {
/// return e.LastError(); // AfterLastItem or Error.
/// }
/// }
/// </code>
/// </summary>
/// <returns>
/// Returns true if moved to a valid item.
/// Returns false and sets state to AfterLastItem if no more items.
/// Returns false and sets state to Error for decoding error.
/// Check LastError for details.
/// </returns>
bool
MoveNextSibling() noexcept;
/// <summary>
/// <para>
/// Advanced scenarios. This method is for extracting type information from an
/// event without looking at value information. Moves the enumerator to the next
/// field declaration (not the next field value). Returns true if moved to a valid
/// item, false if no more items or decoding error.
/// </para><para>
/// PRECONDITION: Can be called after a successful call to StartEvent, until
/// MoveNextMetadata returns false.
/// </para><para>
/// Note that metadata enumeration gives a flat view of arrays and structures.
/// There are only Value items, no BeginArray, EndArray, BeginStruct, EndStruct.
/// A struct shows up as a value with Encoding = Struct (Format holds field count).
/// An array shows up as a value with ArrayFlags != 0, and ArrayCount is either zero
/// (indicating a runtime-variable array length) or nonzero (indicating a compile-time
/// constant array length). An array of struct is a field with Encoding = Struct and
/// ArrayFlags != 0. ValueBytes will always be empty. ArrayIndex and ElementSize
/// will always be zero.
/// </para><para>
/// Note that when enumerating metadata for a structure, the enumeration may end before
/// the expected number of fields are seen. This is a supported scenario and is not an
/// error in the event. A large field count just means "this structure contains all the
/// remaining fields in the event".
/// </para><para>
/// Typically called in a loop until it returns false.
/// </para><code>
/// if (!e.StartEvent(...)) return e.LastError();
/// while (e.MoveNextMetadata())
/// {
/// DoFieldDeclaration(e.GetItemInfo());
/// }
/// return e.LastError();
/// </code>
/// </summary>
/// <returns>
/// Returns true if moved to a valid item.
/// Returns false and sets state to AfterLastItem if no more items.
/// Returns false and sets state to Error for decoding error.
/// Check LastError for details.
/// </returns>
bool
MoveNextMetadata() noexcept;
/// <summary>
/// <para>
/// Gets information that applies to the current event, e.g. the event name,
/// provider name, options, level, keyword, etc.
/// </para><para>
/// PRECONDITION: Can be called when State != None, i.e. at any time after a
/// successful call to StartEvent, until a call to Clear.
/// </para>
/// </summary>
EventInfo
GetEventInfo() const noexcept;
/// <summary>
/// <para>
/// Gets information that applies to the current item, e.g. the item's name,
/// the item's type (integer, string, float, etc.), data pointer, data size.
/// The current item changes each time MoveNext() is called.
/// </para><para>
/// PRECONDITION: Can be called when State > BeforeFirstItem, i.e. after MoveNext
/// returns true.
/// </para>
/// </summary>
EventItemInfo
GetItemInfo() const noexcept;
/// <summary>
/// <para>
/// Gets the remaining event payload, i.e. the event data that has not yet
/// been decoded. The data position can change each time MoveNext is called.
/// </para><para>
/// PRECONDITION: Can be called when State != None, i.e. at any time after a
/// successful call to StartEvent, until a call to Clear.
/// </para><para>
/// This can be useful after enumeration has completed to to determine
/// whether the event contains any trailing data (data not described by the
/// decoding information). Up to 3 bytes of trailing data is normal (padding
/// between events), but 4 or more bytes of trailing data might indicate some
/// kind of encoding problem or data corruption.
/// </para>
/// </summary>
EventDataPosition
GetRawDataPosition() const noexcept;
private:
void
ResetImpl(uint32_t moveNextLimit) noexcept;
bool
SkipStructMetadata() noexcept;
bool
NextProperty() noexcept;
/// <summary>
/// Requires m_metaEnd >= m_stackTop.NameOffset.
/// Reads name, encoding, format, tag starting at m_stackTop.NameOffset.
/// Updates m_stackTop.NameSize, m_stackTop.NextOffset.
/// On failure, returns Encoding = None.
/// </summary>
FieldType
ReadFieldNameAndType() noexcept;
/// <summary>
/// Requires m_metaEnd > typeOffset.
/// Reads encoding, format, tag starting at m_stackTop.NameOffset.
/// Updates m_stackTop.NextOffset.
/// On failure, returns Encoding = None.
/// </summary>
FieldType
ReadFieldType(uint16_t typeOffset) noexcept;
bool
StartArray() noexcept;
void
StartStruct() noexcept;
bool
StartValue() noexcept;
void
StartValueSimple() noexcept;
template<class CH>
void
StartValueStringNul() noexcept;
void
StartValueStringLength16(uint8_t charSizeShift) noexcept;
void
SetState(EventEnumeratorState newState, SubState newSubState) noexcept;
void
SetEndState(EventEnumeratorState newState, SubState newSubState) noexcept;
bool
SetNoneState(EventEnumeratorError error) noexcept;
bool
SetErrorState(EventEnumeratorError error) noexcept;
};
/// <summary>
/// Enumeration states.
/// </summary>
enum EventEnumeratorState : uint8_t
{
/// <summary>
/// After construction, a call to Clear, or a failed StartEvent.
/// </summary>
EventEnumeratorState_None,
/// <summary>
/// After an error has been returned by MoveNext.
/// </summary>
EventEnumeratorState_Error,
/// <summary>
/// Positioned after the last item in the event.
/// </summary>
EventEnumeratorState_AfterLastItem,
// MoveNext() is an invalid operation for all states above this line.
// MoveNext() is a valid operation for all states below this line.
/// <summary>
/// Positioned before the first item in the event.
/// </summary>
EventEnumeratorState_BeforeFirstItem,
// GetItemInfo() is an invalid operation for all states above this line.
// GetItemInfo() is a valid operation for all states below this line.
/// <summary>
/// Positioned at an item with data (a field or an array element).
/// </summary>
EventEnumeratorState_Value,
/// <summary>
/// Positioned before the first item in an array.
/// </summary>
EventEnumeratorState_ArrayBegin,
/// <summary>
/// Positioned after the last item in an array.
/// </summary>
EventEnumeratorState_ArrayEnd,
/// <summary>
/// Positioned before the first item in a struct.
/// </summary>
EventEnumeratorState_StructBegin,
/// <summary>
/// Positioned after the last item in a struct.
/// </summary>
EventEnumeratorState_StructEnd,
};
/// <summary>
/// Values for the LastError property.
/// </summary>
enum EventEnumeratorError : uint8_t
{
/// <summary>
/// No error.
/// </summary>
EventEnumeratorError_Success = 0,
/// <summary>
/// Event is smaller than 8 bytes or larger than 2GB,
/// or TracepointName is longer than 255 characters.
/// </summary>
EventEnumeratorError_InvalidParameter = EINVAL,
/// <summary>
/// Event does not follow the EventHeader naming/layout rules,
/// is big-endian, has unrecognized flags, or unrecognized types.
/// </summary>
EventEnumeratorError_NotSupported = ENOTSUP,
/// <summary>
/// Resource usage limit (moveNextLimit) reached.
/// </summary>
EventEnumeratorError_ImplementationLimit = E2BIG,
/// <summary>
/// Event has an out-of-range value.
/// </summary>
EventEnumeratorError_InvalidData = EBADMSG,
/// <summary>
/// Event has more than 8 levels of nested structs.
/// </summary>
EventEnumeratorError_StackOverflow = EOVERFLOW,
/// <summary>
/// Method call invalid for current State().
/// </summary>
EventEnumeratorError_InvalidState = EPERM,
};
struct EventDataPosition
{
_Field_size_bytes_(Size) void const* Data;
uint32_t Size;
};
struct EventInfo
{
// "EventName" followed by 0 or more event attributes.
// Each attribute is ";AttribName=AttribValue".
// EventName should not contain ';'.
// AttribName should not contain ';' or '='.
// AttribValue may contain ";;" which should be unescaped to ";".
_Field_z_ char const* Name;
// TracepointName, e.g. "ProviderName_LnKnnnOptions".
// May not be nul-terminated. Length is TracepointNameLength.
_Field_size_bytes_(TracepointNameLength) char const* TracepointName;
// 128-bit big-endian activity id, or NULL if none.
_Field_size_bytes_opt_(16) uint8_t const* ActivityId;
// 128-bit big-endian related activity id, or NULL if none.
_Field_size_bytes_opt_(16) uint8_t const* RelatedActivityId;
// flags, version, id, tag, opcode, level.
eventheader Header;
// Event category bits.
uint64_t Keyword;
// Length of TracepointName.
uint8_t TracepointNameLength;
// Length of the ProviderName part of TracepointName, e.g. if
// TracepointName is "ProviderName_LnKnnnOptions", this will be 12
// since strlen("ProviderName") = 12.
uint8_t ProviderNameLength;
// Index to the Options part of TracepointName, i.e. the part of the
// TracepointName after level and keyword, e.g. if TracepointName is
// "ProviderName_LnKnnnOptions", this will be 19.
uint8_t OptionsIndex;
};
struct EventItemInfo
{
// "FieldName" followed by 0 or more field attributes.
// Each attribute is ";AttribName=AttribValue".
// FieldName should not contain ';'.
// AttribName should not contain ';' or '='.
// AttribValue may contain ";;" which should be unescaped to ";".
_Field_z_ char const* Name;
// Raw field value bytes.
// May need byte-swap (check e.NeedByteSwap() or eventInfo.header.flags).
// For strings, does not include length prefix or nul-termination.
_Field_size_bytes_(ValueSize) void const* ValueData;
// Raw field value size (in bytes).
// This is nonzero for Value items and for ArrayBegin of array of simple values.
// This is zero for everything else, including ArrayBegin of array of complex items.
// For strings, does not include length prefix or nul-termination.
uint32_t ValueSize;
// Array element index.
// For non-array, this is 0.
// For ArrayBegin, this is 0.
// For ArrayEnd, this is ArrayCount.
uint16_t ArrayIndex;
// Array element count. For non-array, this is 1.
uint16_t ArrayCount;
// Nonzero for simple items (fixed-size non-struct).
// Zero for complex items (variable-size or struct).
uint32_t ElementSize : 8;
// Field's underlying encoding. The encoding indicates how to determine the field's
// size and the semantic type to use when Format = Default.
event_field_encoding Encoding : 8;
// Field's semantic type. May be Default, in which case the semantic type should be
// determined based on the default format for the field's encoding.
// For StructBegin/StructEnd, this contains the struct field count.
event_field_format Format : 8;
// 0 if item is a non-array Value.
// event_field_encoding_carray_flag if item is a fixed-length ArrayBegin, ArrayEnd, or array Value.
// event_field_encoding_varray_flag if item is a variable-length ArrayBegin, ArrayEnd, or array Value.
event_field_encoding ArrayFlags : 8;
// True if event's byte order != host byte order.
bool NeedByteSwap;
// Field tag, or 0 if none.
uint16_t FieldTag;
};
}
// namespace eventheader_decode
#endif // _included_EventEnumerator_h

View file

@ -0,0 +1,234 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#ifndef _included_EventFormatter_h
#define _included_EventFormatter_h 1
#include "EventEnumerator.h"
#include <string>
namespace tracepoint_decode
{
// Forward declarations from libtracepoint-decode
struct PerfSampleEventInfo;
class PerfFieldMetadata;
}
namespace eventheader_decode
{
// Forward declarations from this file.
enum EventFormatterJsonFlags : unsigned;
enum EventFormatterMetaFlags : unsigned;
/*
Helper for converting event fields to strings.
*/
class EventFormatter
{
public:
/*
Formats the specified sample as a UTF-8 JSON string and appends the
result to dest.
If the Name flag is not specified, the appended string is a valid JSON object.
If the Name flag is specified, the appended string is a valid JSON member,
i.e. it is a "FieldName": prefix followed by a valid JSON object.
If the Space flag is specified, the appended string will begin with a space
and will have spaces between elements, e.g. after ',' and ':'.
Returns 0 for success, errno for error. May throw bad_alloc.
*/
int
AppendSampleAsJson(
std::string& dest,
tracepoint_decode::PerfSampleEventInfo const& sampleEventInfo,
bool fileBigEndian,
EventFormatterJsonFlags jsonFlags = static_cast<EventFormatterJsonFlags>(0),
EventFormatterMetaFlags metaFlags = static_cast<EventFormatterMetaFlags>(0xffff),
uint32_t moveNextLimit = 4096);
/*
Formats the specified sample field as a UTF-8 JSON string and appends the
result to dest.
If the Name flag is not specified, the appended string is a valid JSON value.
If the Name flag is specified, the appended string is a valid JSON member,
i.e. it is a "FieldName": prefix followed by a valid JSON value.
If the Space flag is specified, the appended string will begin with a space
and will have spaces between elements, e.g. after ',' and ':'.
Returns 0 for success, errno for error. May throw bad_alloc.
*/
int
AppendSampleFieldAsJson(
std::string& dest,
_In_reads_bytes_(fieldRawDataSize) void const* fieldRawData,
size_t fieldRawDataSize,
tracepoint_decode::PerfFieldMetadata const& fieldMetadata,
bool fileBigEndian,
EventFormatterJsonFlags jsonFlags = static_cast<EventFormatterJsonFlags>(0));
/*
Formats the enumerator's event as a UTF-8 JSON string and appends the
result to dest. Moves the enumerator to the end of the event.
If the Name flag is not specified, the appended string is a valid JSON object.
If the Name flag is specified, the appended string is a valid JSON member,
i.e. it is a "FieldName": prefix followed by a valid JSON object.
If the Space flag is specified, the appended string will begin with a space
and will have spaces between elements, e.g. after ',' and ':'.
Returns 0 for success, errno for error. May throw bad_alloc.
Requires: enumerator.State is BeforeFirstItem.
*/
int
AppendEventAsJsonAndMoveToEnd(
std::string& dest,
EventEnumerator& enumerator,
EventFormatterJsonFlags jsonFlags = static_cast<EventFormatterJsonFlags>(0),
EventFormatterMetaFlags metaFlags = static_cast<EventFormatterMetaFlags>(0xffff));
/*
Formats the item at the enumerator's current position (a value, array
begin, or structure begin) as UTF-8 JSON string and appends the result to
dest. Moves the enumerator to the next item as if by MoveNextSibling.
Returns 0 for success, errno for error. May throw bad_alloc.
Requires: enumerator.State is Value, ArrayBegin, or StructBegin.
Booleans, decimal integers, and finite floating-point data values will be
unquoted. Other simple data values (including hexadecimal integers, infinities
and NaNs) will be quoted. Complex items (structures and arrays) will be
converted to JSON objects and arrays.
If the Name flag is not specified, the appended string is a valid JSON value.
If the Name flag is specified, the appended string is a valid JSON member,
i.e. it is a "FieldName": prefix followed by a valid JSON value.
If the Space flag is specified, the appended string will begin with a space
and will have spaces between elements, e.g. after ',' and ':'.
*/
int
AppendItemAsJsonAndMoveNextSibling(
std::string& dest,
EventEnumerator& enumerator,
EventFormatterJsonFlags jsonFlags = static_cast<EventFormatterJsonFlags>(0));
/*
Formats the event field value at the enumerator's current position as a
UTF-8 string and appends the result to dest.
Returns 0 for success, errno for error. May throw bad_alloc.
Requires: enumerator.State is Value.
*/
int
AppendValue(
std::string& dest,
EventEnumerator const& enumerator);
/*
Formats the event field value as a UTF-8 string and appends the
result to dest.
Returns 0 for success, errno for error. May throw bad_alloc.
Requires: valueItem is a Value, not an ArrayBegin, StructEnd, etc.
*/
int
AppendValue(
std::string& dest,
EventItemInfo const& valueItemInfo);
/*
Formats the specified event field value as a UTF-8 string and appends the
result to dest.
Returns 0 for success, errno for error. May throw bad_alloc.
*/
int
AppendValue(
std::string& dest,
_In_reads_bytes_(valueSize) void const* valueData,
uint32_t valueSize,
event_field_encoding encoding,
event_field_format format,
bool needsByteSwap);
/*
Formats the specified big-endian UUID value as a UTF-8 string and appends
the result to dest. UUID is formatted as 36 chars with dashes, e.g.
"00000000-0000-0000-0000-000000000000".
May throw bad_alloc.
*/
void
AppendUuid(
std::string& dest,
_In_reads_bytes_(16) uint8_t const* uuid);
};
/*
Flags for use when formatting an item as a JSON string with
AppendItemAsJsonAndMoveNextSibling.
*/
enum EventFormatterJsonFlags : unsigned
{
EventFormatterJsonFlags_None = 0,
EventFormatterJsonFlags_Name = 0x1, // Include a "Name": prefix for root item.
EventFormatterJsonFlags_Space = 0x2, // Include a space between values.
EventFormatterJsonFlags_FieldTag = 0x4, // Append ";tag=0xNNNN" to name of fields if tag != 0.
};
/*
Flags controlling the metadata to be included in the "meta" suffix of a JSON
event string.
Note that the "n" field is for the convenience of human readers of the JSON file.
It contains the provider and event names and appears at the start of the event
rather than in the "meta" section even though it is technically metadata.
Note that the format of the "time" field depends on the clock information that was
provided by the session:
- If clock information is available: "yyyy-mm-ddThh:mm:ss.nnnnnnnnnZ"
- Else, timestamp is seconds relative to an unknown epoch: 123.123456789
For consistent behavior, always include clock information in the trace,
e.g. "perf record -k monotonic -e ...".
*/
enum EventFormatterMetaFlags : unsigned
{
EventFormatterMetaFlags_None = 0, // disable the "meta" suffix.
EventFormatterMetaFlags_n = 0x1, // "n":"provider:event" before the user fields (not in the suffix).
EventFormatterMetaFlags_time = 0x2, // timestamp (only for sample events).
EventFormatterMetaFlags_cpu = 0x4, // cpu index (only for sample events).
EventFormatterMetaFlags_pid = 0x8, // process id (only for sample events).
EventFormatterMetaFlags_tid = 0x10, // thread id (only for sample events).
EventFormatterMetaFlags_id = 0x20, // eventheader id (decimal integer, omitted if 0).
EventFormatterMetaFlags_version = 0x40, // eventheader version (decimal integer, omitted if 0).
EventFormatterMetaFlags_level = 0x80, // eventheader level (decimal integer, omitted if 0).
EventFormatterMetaFlags_keyword = 0x100, // eventheader keyword (hexadecimal string, omitted if 0).
EventFormatterMetaFlags_opcode = 0x200, // eventheader opcode (decimal integer, omitted if 0).
EventFormatterMetaFlags_tag = 0x400, // eventheader tag (hexadecimal string, omitted if 0).
EventFormatterMetaFlags_activity = 0x800, // eventheader activity ID (UUID string, omitted if 0).
EventFormatterMetaFlags_relatedActivity = 0x1000,// eventheader related activity ID (UUID string, omitted if not set).
EventFormatterMetaFlags_provider = 0x10000, // provider name or system name (string).
EventFormatterMetaFlags_event = 0x20000, // event name or tracepoint name (string).
EventFormatterMetaFlags_options = 0x40000, // eventheader provider options (string, omitted if none).
EventFormatterMetaFlags_flags = 0x80000, // eventheader flags (hexadecimal string).
EventFormatterMetaFlags_common = 0x100000, // Include the common_* fields before the user fields (only for sample events).
EventFormatterMetaFlags_Default = 0xffff, // Include n..relatedActivity.
EventFormatterMetaFlags_All = ~0u
};
}
// namespace eventheader_decode
#endif // _included_EventFormatter_h

View file

@ -0,0 +1,20 @@
add_executable(eventheader-decode-file
decode-file.cpp)
target_link_libraries(eventheader-decode-file
eventheader-decode)
if(NOT WIN32)
find_library(TRACEFS_LIBRARIES tracefs)
find_library(TRACEEVENT_LIBRARIES traceevent)
if(TRACEFS_LIBRARIES AND TRACEEVENT_LIBRARIES)
add_executable(eventheader-decode-live
decode-live.cpp)
target_link_libraries(eventheader-decode-live
eventheader-decode
"${TRACEFS_LIBRARIES}" "${TRACEEVENT_LIBRARIES}")
endif()
endif()

View file

@ -0,0 +1,173 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include <eventheader/EventFormatter.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <memory>
#include <string>
#include <vector>
#ifdef _WIN32
#include <share.h>
#define fopen(filename, mode) _fsopen(filename, mode, _SH_DENYWR)
#define strerror_r(errnum, buf, buflen) (strerror_s(buf, buflen, errnum), buf)
#define le32toh(x) x
#endif // _WIN32
using namespace eventheader_decode;
struct fcloseDelete
{
void operator()(FILE* file) const noexcept
{
fclose(file);
}
};
static bool
ReadFromFile(FILE* file, void* buffer, uint32_t size)
{
size_t actual = fread(buffer, 1, size, file);
if (size == actual)
{
return true;
}
int err = ferror(file);
if (err)
{
char errBuf[80];
printf("\n- fread error %u %s",
err,
strerror_r(err, errBuf, sizeof(errBuf)));
}
else if (actual != 0)
{
printf("\n- fread early eof (asked for %u, got %u)", size, static_cast<unsigned>(actual));
}
return false;
}
int main(int argc, char* argv[])
{
int err;
if (argc <= 1)
{
printf("\nUsage: %s [InterceptorSampleFileName1] ...\n", argv[0]);
err = 1;
goto Done;
}
try
{
std::vector<char> buffer(4096);
std::string eventText;
EventEnumerator enumerator;
EventFormatter formatter;
bool comma = false;
for (int argi = 1; argi < argc; argi += 1)
{
char const* const filename = argv[argi];
printf("%s\n\"%s\": [",
comma ? "," : "",
filename);
comma = false;
std::unique_ptr<FILE, fcloseDelete> file(fopen(filename, "rb"));
if (!file)
{
err = errno;
char errBuf[80];
printf("\n- fopen(%s) error %u %s",
filename,
err,
strerror_r(err, errBuf, sizeof(errBuf)));
}
else for (;;)
{
uint32_t recordSize;
if (!ReadFromFile(file.get(), &recordSize, sizeof(recordSize)))
{
break;
}
recordSize = le32toh(recordSize);
if (recordSize <= sizeof(recordSize))
{
printf("\n- Unexpected recordSize %u", recordSize);
break;
}
recordSize -= sizeof(recordSize); // File's recordSize includes itself.
if (buffer.size() < recordSize)
{
buffer.reserve(recordSize);
buffer.resize(buffer.capacity());
}
if (!ReadFromFile(file.get(), buffer.data(), recordSize))
{
break;
}
auto const nameSize = static_cast<uint32_t>(strnlen(buffer.data(), recordSize));
if (nameSize == recordSize)
{
printf("\n- TracepointName not nul-terminated.");
continue;
}
fputs(comma ? ",\n " : "\n ", stdout);
comma = true;
if (!enumerator.StartEvent(
buffer.data(), // tracepoint name
nameSize, // tracepoint name length
buffer.data() + nameSize + 1, // event data
recordSize - nameSize - 1)) // event data length
{
printf("\n- StartEvent error %d.", enumerator.LastError());
}
else
{
eventText.clear();
err = formatter.AppendEventAsJsonAndMoveToEnd(
eventText, enumerator, static_cast<EventFormatterJsonFlags>(
EventFormatterJsonFlags_Space |
EventFormatterJsonFlags_FieldTag));
if (err != 0)
{
printf("\n- AppendEvent error.");
}
else
{
fputs(eventText.c_str(), stdout);
}
}
}
fputs(" ]", stdout);
comma = true;
}
printf("\n");
err = 0;
}
catch (std::exception const& ex)
{
printf("\nException: %s\n", ex.what());
err = 1;
}
Done:
return err;
}

View file

@ -0,0 +1,102 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include <eventheader/EventFormatter.h>
extern "C" {
#include <tracefs/tracefs.h>
}
#include <errno.h>
#include <stdio.h>
struct CallbackContext
{
std::string Value;
EventEnumerator Enumerator;
EventFormatter Formatter;
};
static int
IterateCallback(tep_event* ev, tep_record* rec, int cpu, void* pContext)
{
auto& ctx = *static_cast<CallbackContext*>(pContext);
if (ev->format.nr_fields < 1 ||
0 != strcmp(ev->format.fields[0].name, "eventheader_flags"))
{
fprintf(stdout, "- No eventheader_flags: \"%s\"\n", ev->name);
}
else
{
auto const flagsOffset = ev->format.fields[0].offset;
auto const pData = static_cast<uint8_t const*>(rec->data) + flagsOffset;
auto const cData = rec->size - flagsOffset;
if (!ctx.Enumerator.StartEvent(ev->name, pData, cData))
{
fprintf(stdout, "- StartEvent error %u: \"%s\"\n", ctx.Enumerator.LastError(), ev->name);
}
else
{
ctx.Value.clear();
int err = ctx.Formatter.AppendEventAsJsonAndMoveToEnd(ctx.Value, ctx.Enumerator);
if (err != 0)
{
fprintf(stdout, "- AppendEvent error %u: \"%s\"\n", err, ev->name);
}
else
{
fprintf(stdout, "%s,\n", ctx.Value.c_str());
}
}
}
return 0;
}
int main()
{
int err;
CallbackContext callbackContext;
auto tep = tracefs_local_events(nullptr);
if (!tep)
{
err = errno;
char errBuf[80];
strerror_r(err, errBuf, sizeof(errBuf));
printf("tracefs_local_events: errno=%u %s\n", err, errBuf);
goto Done;
}
int parsingFailures;
if (tracefs_fill_local_events(nullptr, tep, &parsingFailures))
{
err = errno;
char errBuf[80];
strerror_r(err, errBuf, sizeof(errBuf));
printf("tracefs_fill_local_events: errno=%u %s\n", err, errBuf);
goto Done;
}
printf("tracefs_fill_local_events parsing failures: %u\n", parsingFailures);
if (tracefs_iterate_raw_events(tep, nullptr, nullptr, 0, &IterateCallback, &callbackContext))
{
err = errno;
char errBuf[80];
strerror_r(err, errBuf, sizeof(errBuf));
printf("tracefs_iterate_raw_events: errno=%u %s\n", err, errBuf);
goto Done;
}
printf("Done.\n");
Done:
if (tep)
{
tep_free(tep);
}
return err;
}

View file

@ -0,0 +1,37 @@
# eventheader-decode = libeventheader-decode, DECODE_HEADERS
add_library(eventheader-decode
EventEnumerator.cpp
EventFormatter.cpp)
target_include_directories(eventheader-decode
PUBLIC
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include/>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
target_link_libraries(eventheader-decode
PUBLIC eventheader-headers
PRIVATE tracepoint-decode)
set(DECODE_HEADERS
"${PROJECT_SOURCE_DIR}/include/eventheader/EventEnumerator.h"
"${PROJECT_SOURCE_DIR}/include/eventheader/EventFormatter.h")
set_target_properties(eventheader-decode PROPERTIES
PUBLIC_HEADER "${DECODE_HEADERS}")
target_compile_features(eventheader-decode
PRIVATE cxx_std_17)
install(TARGETS eventheader-decode
EXPORT eventheader-decodeTargets
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/eventheader)
install(EXPORT eventheader-decodeTargets
FILE "eventheader-decodeTargets.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/eventheader-decode")
configure_package_config_file(
"${CMAKE_CURRENT_SOURCE_DIR}/eventheader-decodeConfig.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/eventheader-decodeConfig.cmake"
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/eventheader-decode"
NO_SET_AND_CHECK_MACRO
NO_CHECK_REQUIRED_COMPONENTS_MACRO)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/eventheader-decodeConfigVersion.cmake"
COMPATIBILITY SameMinorVersion)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/eventheader-decodeConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/eventheader-decodeConfigVersion.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/eventheader-decode")

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,2 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/eventheader-decodeTargets.cmake")

View file

@ -0,0 +1,8 @@
add_executable(decode-perf
decode-perf.cpp)
target_link_libraries(decode-perf
eventheader-decode
tracepoint-decode)
target_compile_features(decode-perf
PRIVATE cxx_std_17)
install(TARGETS decode-perf)

View file

@ -0,0 +1,140 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include <tracepoint/PerfEventInfo.h>
#include <tracepoint/PerfDataFile.h>
#include <tracepoint/PerfEventAbi.h>
#include <eventheader/EventFormatter.h>
#include <string.h>
#include <map>
#ifdef _WIN32
#define strerror_r(errnum, buf, buflen) (strerror_s(buf, buflen, errnum), buf)
#endif // _WIN32
using namespace eventheader_decode;
using namespace tracepoint_decode;
static bool
FlushEvents(std::multimap<uint64_t, std::string>& events, bool comma) noexcept
{
for (auto const& pair : events)
{
fputs(comma ? ",\n " : "\n ", stdout);
comma = true;
fputs(pair.second.c_str(), stdout);
}
events.clear();
return comma;
}
int main(int argc, char* argv[])
{
int err;
if (argc <= 1)
{
fprintf(stderr, "\nUsage: %s [perf.data] ... (use - for stdin)\n", argv[0]);
err = 1;
goto Done;
}
try
{
std::multimap<uint64_t, std::string> events;
EventFormatter formatter;
PerfDataFile file;
bool comma = false;
for (int argi = 1; argi < argc; argi += 1)
{
bool const isStdin =
0 == strcmp(argv[argi], "") ||
0 == strcmp(argv[argi], "-") ||
0 == strcmp(argv[argi], "--");
char const* const filename = isStdin ? "stdin" : argv[argi];
fprintf(stdout, "%s\n\"%s\": [",
comma ? "," : "",
filename);
comma = false;
// CodeQL [SM01937] This is non shipping sample code that is not intended to be secure. Users should be able
// to specify the output file path.
err = isStdin ? file.OpenStdin() : file.Open(filename);
if (err != 0)
{
char errBuf[80];
fprintf(stderr, "\n- Open(\"%s\") error %d: \"%s\"\n",
filename,
err,
strerror_r(err, errBuf, sizeof(errBuf)));
}
else for (;;)
{
perf_event_header const* pHeader;
err = file.ReadEvent(&pHeader);
if (!pHeader)
{
if (err)
{
fprintf(stderr, "\n- ReadEvent error %d.\n", err);
}
break;
}
if (pHeader->type != PERF_RECORD_SAMPLE)
{
if (pHeader->type == PERF_RECORD_FINISHED_ROUND)
{
comma = FlushEvents(events, comma);
}
continue; // Only interested in sample events for now.
}
PerfSampleEventInfo sampleEventInfo;
err = file.GetSampleEventInfo(pHeader, &sampleEventInfo);
if (err)
{
fprintf(stderr, "\n- GetSampleEventInfo error %d.\n", err);
continue;
}
// Events are returned out-of-order and need to be sorted. Use a map to
// put them into timestamp order. Flush the map at the end of each round.
auto it = events.emplace(
(sampleEventInfo.SampleType() & PERF_SAMPLE_TIME) ? sampleEventInfo.time : 0u,
std::string());
err = formatter.AppendSampleAsJson(
it->second,
sampleEventInfo,
file.FileBigEndian(),
static_cast<EventFormatterJsonFlags>(
EventFormatterJsonFlags_Space |
EventFormatterJsonFlags_FieldTag));
if (err)
{
fprintf(stderr, "\n- Format error %d.\n", err);
}
}
comma = FlushEvents(events, comma);
fputs(" ]", stdout);
comma = true;
}
fprintf(stdout, "\n");
err = 0;
}
catch (std::exception const& ex)
{
fprintf(stderr, "\nException: %s\n", ex.what());
err = 1;
}
Done:
return err;
}

View file

@ -0,0 +1,46 @@
cmake_minimum_required(VERSION 3.10)
include(../version.cmake)
project(eventheader-tracepoint
VERSION ${LINUXTRACEPOINTS_VERSION}
DESCRIPTION "EventHeader-encoded Linux tracepoints for C/C++"
HOMEPAGE_URL "https://github.com/microsoft/LinuxTracepoints"
LANGUAGES C CXX)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
set(BUILD_SAMPLES ON CACHE BOOL "Build sample code")
if(WIN32)
add_compile_options(/W4 /WX /permissive-)
else()
add_compile_options(
-Wall
-Wextra
-Wformat
-Wformat-security
-Werror=format-security
-Wstack-protector
-Werror=stack-protector)
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
add_compile_options(-D_FORTIFY_SOURCE=2)
endif()
endif()
add_subdirectory(include)
if(NOT WIN32)
if(NOT TARGET tracepoint-headers)
find_package(tracepoint-headers ${TRACEPOINT_HEADERS_MINVER} REQUIRED)
endif()
if(NOT TARGET tracepoint)
find_package(tracepoint ${TRACEPOINT_MINVER} REQUIRED)
endif()
add_subdirectory(src)
if(BUILD_SAMPLES)
add_subdirectory(samples)
endif()
endif()

View file

@ -0,0 +1,316 @@
# libeventheader-tracepoint
- [EventHeader](include/eventheader/eventheader.h)
is a convention for packing events.
- [libeventheader-tracepoint](src/eventheader-tracepoint.c)
is a library that supports writing eventheader-packed events to any
implementation of the `tracepoint.h` interface.
- [TraceLoggingProvider.h](include/eventheader/TraceLoggingProvider.h)
is a high-level C/C++ tracing API with multiple implementations targeting
different tracing systems, e.g. Windows ETW or Linux LTTng. The
implementation in this project packs data using the `EventHeader` convention
and logs events using `libeventheader-tracepoint`.
- [EventHeaderDynamic.h](include/eventheader/EventHeaderDynamic.h)
is a mid-level C++ tracing API for generating runtime-specified events
using the `EventHeader` convention and logging via `libeventheader-tracepoint`.
This is intended for use as an implementation layer for a higher-level API
like OpenTelemetry. Developers instrumenting their own C/C++ code would
normally use `TraceLoggingProvider.h` instead of `EventHeaderDynamic.h`.
- [Similar APIs](https://github.com/microsoft/LinuxTracepoints-Rust)
are available for Rust.
## EventHeader
EventHeader is a tracing convention layered on top of Linux Tracepoints. It
extends the Tracepoint system with additional event attributes and a stronger
field value type system.
To reduce the number of unique Tracepoint names tracked by the kernel, we
use a small number of Tracepoints to manage a larger number of events. All
events with the same attributes (provider name, severity level, category
keyword, etc.) will share one Tracepoint.
- This means we cannot enable/disable events individually. Instead, all events
with similar attributes will be enabled/disabled as a group.
- This means we cannot rely on the kernel's Tracepoint metadata for event
identity or event field names/types. Instead, all events contain a common
header that provides event identity, core event attributes, and support for
optional event attributes. The kernel's Tracepoint metadata is used only for
the Tracepoint's name and to determine whether the event follows the
EventHeader conventions.
- Since any eventheader-conforming tracepoint with a particular name will be
identical from the perspective of the kernel (they all have the same
tracepoint fields), it is possible to pre-register tracepoints. For example,
if I have the names of eventheader-compliant tracepoints that I need to
collect but the tracepoints have not been registered yet (because the
programs that generate the tracepoints haven't run yet), I can just register
them myself since any registration will be equivalent to the "real"
registrations.
We define a naming scheme to be used for the shared Tracepoints:
`TracepointName = ProviderName + '_' + 'L' + EventLevel + 'K' + EventKeyword + [Options]`
We define a common event layout to be used by all EventHeader events. The
event has a header, optional header extensions, and then the event data:
`Event = eventheader + [HeaderExtensions] + Data`
We define a format to be used for header extensions:
`HeaderExtension = eventheader_extension + ExtensionData`
We define a header extension to be used for activity IDs.
We define a header extension to be used for event metadata (event name, field
names, field types).
For use in the event metadata extension, we define a field type system that
supports scalar, string, binary, array, and struct.
Note that we assume that the Tracepoint name corresponding to the event is
available during event decoding. The event decoder obtains the provider name
and the keyword for an event by parsing the event's Tracepoint name.
Details are provided in [eventheader.h](include/eventheader/eventheader.h).
### Provider Names
A provider is a component that generates events. Each event from a provider is
associated with a Provider Name that uniquely identifies the provider.
The provider name should be short, yet descriptive enough to minimize the
chance of collision and to help developers track down the component generating
the events. Hierarchical namespaces may be useful for provider names, e.g.
`MyCompany_MyOrg_MyComponent`.
Restrictions:
- ProviderName may not contain `' '` or `':'` characters.
- `strlen(ProviderName + '_' + Attributes)` must be less than
`EVENTHEADER_NAME_MAX` (256) characters.
- Some event APIs (e.g. tracefs) might impose additional restrictions on
tracepoint names. For best compatibility, use only ASCII identifier
characters `[A-Za-z0-9_]` in provider names.
Event attribute semantics should be consistent within a given provider. While
some event attributes have generally-accepted semantics (e.g. level value 3
is defined below as "warning"), the precise semantics of the attribute values
are defined at the scope of a provider (e.g. different providers will use
different criteria for what constitutes a warning). In addition, some
attributes (tag, keyword) are completely provider-defined. All events with a
particular provider name should use consistent semantics for all attributes
(e.g. keyword bit `0x1` should have a consistent meaning for all events from a
particular provider but will mean something different for other providers).
### Tracepoint Names
A Tracepoint is registered with the kernel for each unique combination of
ProviderName + Attributes. This allows a larger number of distinct events to
be controlled by a smaller number of kernel Tracepoints while still allowing
events to be enabled/disabled at a reasonable granularity.
The Tracepoint name for an EventHeader event is defined as:
`ProviderName + '_' + 'L' + eventLevel + 'K' + eventKeyword + [Options]`
or `printf("%s_L%xK%lx%s", providerName, eventLevel, eventKeyword, options)`,
e.g. `MyProvider_L3K2a` or `OtherProvider_L5K0Gperf`.
Event level is a `uint8` value 1..255 indicating event severity, formatted as
lowercase hexadecimal, e.g. `printf("L%x", eventLevel)`. The defined level values
are: `1` = critical error, `2` = error, `3` = warning, `4` = information, `5` = verbose.
Event keyword is a `uint64` bitmask indicating event category membership,
formatted as lowercase hexadecimal, e.g. `printf("K%lx", eventKeyword)`. Each
bit in the keyword corresponds to a provider-defined category, e.g. a provider
might define `0x2` = networking and `0x4` = I/O so that keyword value of
`0x2|0x4` = `0x6` would indicate that an event is in both the networking and
I/O categories.
Options (optional attributes) can be specified after the keyword attribute.
Each option consists of an uppercase ASCII letter (option type) followed by 0
or more ASCII digits or lowercase ASCII letters (option value). To support
consistent event names, the options must be sorted in alphabetical order, e.g.
"Aoption" should come before "Boption".
The currently defined options are:
- `G` = provider Group name. Defines a group of providers. This can be used by
event analysis tools to find all providers that generate a certain kind of
information.
Restrictions:
- ProviderName may not contain `' '` or `':'` characters.
- Tracepoint name must be less than `EVENTHEADER_NAME_MAX` (256)
characters in length.
- Some event APIs (e.g. tracefs) might impose additional restrictions on
tracepoint names. For best compatibility, use only ASCII identifier
characters `[A-Za-z0-9_]` in provider names.
### Header
Because multiple events may share a single Tracepoint, each event must contain
information to distinguish it from other events. To enable this, each event
starts with an EventHeader structure which contains information about the
event:
```c
typedef struct eventheader {
uint8_t flags; // eventheader_flags: pointer64, little_endian, extension.
uint8_t version; // If id != 0 then increment version when event layout changes.
uint16_t id; // Stable id for this event, or 0 if none.
uint16_t tag; // Provider-defined event tag, or 0 if none.
uint8_t opcode; // event_opcode: info, start activity, stop activity, etc.
uint8_t level; // event_level: critical, error, warning, info, verbose.
// Followed by: eventheader_extension block(s), then event payload.
} eventheader;
```
- **flags:** Bits indicating pointer size (32 or 64 bits), byte order
(big-endian or little), and whether any header extensions are present.
- **opcode:** Indicates special event semantics e.g. "normal event",
"activity start event", "activity end event".
- **tag:** Provider-defined 16-bit value. Can be used for anything.
- **id:** 16-bit stable event identifier, or 0 if no identifier is assigned.
- **version:** 8-bit event version, incremented for e.g. field type changes.
- **level:** 8-bit event severity level, `1` = critical .. `5` = verbose.
(level value in event header must match the level in the Tracepoint name.)
If the extension flag is not set, the header is immediately followed by the
event payload.
If the extension flag is set, the header is immediately followed by one or more
header extensions. Each header extension has a 16-bit size, a 15-bit type code,
and a 1-bit flag indicating whether another header extension follows the
current extension. The final header extension is immediately followed by the
event payload.
### Extension
Each event may have any number of header extensions that contain information
associated with the event.
```c
typedef struct eventheader_extension {
uint16_t size;
uint16_t kind; // eventheader_extension_kind
// Followed by size bytes of data. No padding/alignment.
} eventheader_extension;
```
The following header extensions are defined:
- **Activity ID:** Contains a 128-bit ID that can be used to correlate events. May
also contain the 128-bit ID of the parent activity (typically used only for
the first event of an activity).
- **Metadata:** Contains the event's metadata: event name, event attributes, field
names, field attributes, and field types. Both simple (e.g. Int32, HexInt16,
Float64, Char32, Uuid) and complex (e.g. NulTerminatedString8,
CountedString16, Binary, Struct, Array) types are supported.
### Metadata
Each event may have a header extension that defines event metadata, i.e.
event name, event attributes, field names, field attributes, and field types.
Event definition format:
```c
char event_name[]; // Nul-terminated utf-8 string: "eventName{;attribName=attribValue}"
followed by 0 or more field definition blocks, tightly-packed (no padding).
```
Field definition block:
```c
char field_name[]; // Nul-terminated utf-8 string: "fieldName{;attribName=attribValue}"
uint8_t encoding; // encoding is 0..31, with 3 flag bits.
uint8_t format; // Present if 0 != (encoding & 128). format is 0..127, with 1 flag bit.
uint16_t tag; // Present if 0 != (format & 128). Contains provider-defined value.
uint16_t array_length; // Present if 0 != (encoding & 32). Contains element count of constant-length array.
```
Notes:
- `event_name` and `field_name` may not contain any `';'` characters.
- `event_name` and `field_name` may be followed by attribute strings.
- attribute string is: `';' + attribName + '=' + attribValue`.
- `attribName` may not contain any `';'` or `'='` characters.
- Semicolons in attribValue must be escaped by doubling, e.g.
`"my;value"` is escaped as `"my;;value"`.
- `array_length` may not be `0`, i.e. constant-length arrays may not be empty.
Each field's type has an arity, an encoding, and a format.
- **Arity** indicates how many values are in a field.
- A scalar field contains one value.
- A const (fixed-length) array contains one or more values. The number of
values is specified in the event metadata and does not vary from one event
to another.
- A variable (dynamic-length) array contains zero or more values. The number
of values is specified in the event data and may vary from one event to
another.
- **Encoding** indicates how to determine the field size. Supported encodings:
- Fixed-size 8-bit, 16-bit, 32-bit, 64-bit, and 128-bit values.
- 0-terminated blobs containing 8-bit, 16-bit, or 32-bit elements (used for
NUL-terminated strings).
- Counted blobs containing 8-bit, 16-bit, or 32-bit elements (used for both
strings and for binary data).
- Structures (fields that logically contain other fields).
- **Format** indicates how to interpret the bytes of the field data.
- Integer: signed decimal, unsigned decimal, hexadecimal, `errno`, `pid`,
`time_t`, Boolean, IP port.
- String: Latin-1, UTF, UTF-BOM, XML, JSON.
- Other: Float, IPv4 address, IPv6 address, UUID, Binary.
## TraceLoggingProvider.h
[TraceLoggingProvider.h](include/eventheader/TraceLoggingProvider.h)
is a high-level C/C++ tracing interface with multiple implementations
targeting different tracing systems. The implementation in this library packs
data using the `EventHeader` convention and logs to any implementation of the
`tracepoint.h` interface.
Other implementations of this header support tracing for
[Windows ETW](https://learn.microsoft.com/windows/win32/tracelogging/trace-logging-portal)
and [Linux LTTng](https://github.com/microsoft/tracelogging/tree/main/LTTng).
Basic usage:
```C
#include <eventheader/TraceLoggingProvider.h>
TRACELOGGING_DEFINE_PROVIDER(
MyProvider,
"MyProviderName", // utf-8 event source name
// {b7aa4d18-240c-5f41-5852-817dbf477472} - UUID event source identifier
(0xb7aa4d18, 0x240c, 0x5f41, 0x58, 0x52, 0x81, 0x7d, 0xbf, 0x47, 0x74, 0x72));
int main(int argc, char* argv[])
{
// Register the provider during component initialization.
TraceLoggingRegister(MyProvider);
for (int i = 0; i < argc; i += 1)
{
// Log some events.
// Note that the value expressions are only evaluated if the event is enabled.
TraceLoggingWrite(
MyProvider, // The event will use the "MyProviderName" event source.
"argv", // Human-readable event name.
TraceLoggingLevel(EventLevel_Warning), // Event severity = Warning
TraceLoggingInt32(i, "arg_index"), // int32 field arg_index with value = i
TraceLoggingString(argv[i], "arg_string") // char* field arg_string with value = argv[i]
);
}
// Unregister the provider during component cleanup.
TraceLoggingUnregister(MyProvider);
}
```
Additional TraceLogging examples:
- [samples/sample.cpp](samples/sample.cpp)
- [samples/interceptor-sample.cpp](samples/interceptor-sample.cpp)

View file

@ -0,0 +1,47 @@
cmake_minimum_required(VERSION 3.10)
include(../../version.cmake)
project(eventheader-tracepoint-headers
VERSION ${LINUXTRACEPOINTS_VERSION}
DESCRIPTION "EventHeader-encoded Linux tracepoints for C/C++ (headers)"
HOMEPAGE_URL "https://github.com/microsoft/LinuxTracepoints"
LANGUAGES C CXX)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
list(APPEND EVENTHEADER_HEADERS
"${PROJECT_SOURCE_DIR}/eventheader/eventheader.h")
if(NOT WIN32)
list(APPEND EVENTHEADER_HEADERS
"${PROJECT_SOURCE_DIR}/eventheader/eventheader-tracepoint.h"
"${PROJECT_SOURCE_DIR}/eventheader/EventHeaderDynamic.h"
"${PROJECT_SOURCE_DIR}/eventheader/TraceLoggingProvider.h")
endif()
# eventheader-headers = EVENTHEADER_HEADERS
add_library(eventheader-headers INTERFACE)
target_include_directories(eventheader-headers
INTERFACE
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
set_target_properties(eventheader-headers PROPERTIES
PUBLIC_HEADER "${EVENTHEADER_HEADERS}")
install(TARGETS eventheader-headers
EXPORT eventheader-headersTargets
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/eventheader)
install(EXPORT eventheader-headersTargets
FILE "eventheader-headersTargets.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/eventheader-headers")
configure_package_config_file(
"${CMAKE_CURRENT_SOURCE_DIR}/eventheader-headersConfig.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/eventheader-headersConfig.cmake"
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/eventheader-headers"
NO_SET_AND_CHECK_MACRO
NO_CHECK_REQUIRED_COMPONENTS_MACRO)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/eventheader-headersConfigVersion.cmake"
COMPATIBILITY SameMinorVersion)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/eventheader-headersConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/eventheader-headersConfigVersion.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/eventheader-headers")

View file

@ -0,0 +1,2 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/eventheader-headersTargets.cmake")

View file

@ -0,0 +1,192 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#ifndef _included_eventheader_tracepoint_h
#define _included_eventheader_tracepoint_h 1
#include <tracepoint/tracepoint-state.h>
#include <sys/uio.h> // struct iovec
#include <eventheader/eventheader.h>
/*
Read-only event info, typically a const static/global for each event.
Caller must provide one for each event, initialized to metadata for the
event and with a pointer to the state for the event.
*/
typedef struct eventheader_tracepoint {
tracepoint_state* state;
eventheader_extension const* metadata;
eventheader header;
uint64_t keyword;
} eventheader_tracepoint;
/*
Read-only provider info, typically a const static/global for each provider.
Caller must provide one for each provider, initialized to metadata for the
provider and with a pointer to the state for the provider.
*/
typedef struct eventheader_provider {
tracepoint_provider_state* state;
/*
Nul-terminated provider options string. May be "" or NULL if none.
The options string contains 0 or more options, ordered by option type
(e.g. option "Gvalue" would come before option "Hvalue").
Each option is an uppercase ASCII letter (option type) followed by 0 or
more ASCII digits or **lowercase** ASCII letters (option value), e.g.
"Gmygroupname".
Recognized option types:
- 'G' = provider group name, e.g. "Gmygroupname".
Total strlen(name + "_" + level + keyword + options) must be less than
EVENTHEADER_NAME_MAX (256).
*/
char const* options;
/*
Nul-terminated provider name string.
Provider Name may not contain ' ' or ':' characters.
Some decoding tools (e.g. tracefs) might impose additional restrictions on
provider name. For best compatibility, use only ASCII identifier characters
[A-Za-z0-9_] in provider names.
Total strlen(name + "_" + level + keyword + options) must be less than
EVENTHEADER_NAME_MAX (256).
*/
char const* name;
} eventheader_provider;
enum {
// 1. For use by tracepoint_write
// 2. header + activity_id
EVENTHEADER_PREFIX_DATAVEC_COUNT_NO_METADATA = 2,
// 1. For use by tracepoint_write
// 2. header + activity_id
// 3. header extension block (metadata)
EVENTHEADER_PREFIX_DATAVEC_COUNT = 3
};
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
/*
Closes the specified provider. Calling Close on an already-closed provider
is a safe no-op.
*/
void
eventheader_close_provider(
eventheader_provider const* pProvider);
/*
Opens the specified provider.
- Returns 0 for success, errno otherwise. Result is primarily for
debugging/diagnostics and is usually ignored for production code.
PRECONDITION:
- It is an error to call Open(pProvider) while Open or Close for that
pProvider is running on another thread.
- It is an error to call Open(pProvider) on a pProvider that is already
open.
*/
int
eventheader_open_provider(
eventheader_provider const* pProvider);
/*
Opens the specified provider and connects the specified events.
- Returns 0 for success, errno otherwise. Result is primarily for
debugging/diagnostics and is usually ignored for production code.
- This function is intended for use when the caller is using
__attribute__(section) to generate the event list. It sorts and
de-duplicates the list (moving NULLs to the end), then initializes events
starting at pEventsStart and stopping at pEventsStop or NULL, whichever
comes first.
PRECONDITION:
- It is an error to call Open(pProvider) while Open or Close for that
pProvider is running on another thread.
- It is an error to call Open(pProvider) on a pProvider that is already
open.
*/
int
eventheader_open_provider_with_events(
eventheader_provider const* pProvider,
eventheader_tracepoint const** pEventsStart,
eventheader_tracepoint const** pEventsStop);
/*
Opens the specified event and associates it with the specified provider.
Returns 0 for success, errno for failure. In case of failure, the event
state is unchanged.
Call eventheader_connect(pEvent, NULL) to disconnect an event.
*/
int
eventheader_connect(
eventheader_tracepoint const* pEvent,
eventheader_provider const* pProvider);
/*
Writes an event. Safe no-op if event is disabled.
- Returns 0 for success, EBADF if event is disabled, errno for error.
Result is primarily for debugging/diagnostics and is usually ignored
for production code.
- If pActivityId is not NULL, event will have an activity_id extension.
- If pEvent->metadata is not NULL, event will have a extension with the
data from pEvent->metadata.
PRECONDITION:
- The ActivityId parameters must either be NULL or must point at 16-byte
IDs.
- If pActivityId is NULL then pRelatedActivityId must be NULL.
- If pEvent->metadata is not NULL then the first
EVENTHEADER_PREFIX_DATAVEC_COUNT iovecs will be used for event headers,
so dataCount must be >= EVENTHEADER_PREFIX_DATAVEC_COUNT. Event payload
(if any) should start at dataVecs[EVENTHEADER_PREFIX_DATAVEC_COUNT].
- If pEvent->metadata is NULL then the first
EVENTHEADER_PREFIX_DATAVEC_COUNT_NO_METADATA iovecs will be used for
event headers, so dataCount must be >=
EVENTHEADER_PREFIX_DATAVEC_COUNT_NO_METADATA. Event payload (if any)
should start at dataVecs[EVENTHEADER_PREFIX_DATAVEC_COUNT_NO_METADATA].
Implementation details:
- Always: dataVecs[0] will be populated with the event's write_index.
- Always: dataVecs[1] will be populated with headers.
- Always: eventheader.
- If pActivityId != NULL: eventheader_extension + activity ids.
- If pEvent->metadata != NULL: dataVecs[2] will be populated with the
header extension block from pEvent->metadata.
- Remaining dataVecs (if any) are populated by caller (event payload).
- If you have header extensions in the payload:
- If pEvent->metadata == NULL: set pEvent->header.flags's extension bit.
- If pEvent->metadata != NULL: set pEvent->metadata.kind's chain bit.
*/
int
eventheader_write(
eventheader_tracepoint const* pEvent,
void const* pActivityId,
void const* pRelatedActivityId,
uint32_t dataCount,
struct iovec* dataVecs);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // _included_eventheader_tracepoint_h

View file

@ -0,0 +1,680 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#ifndef _included_eventheader_h
#define _included_eventheader_h 1
#include <stddef.h>
#include <stdint.h>
#ifdef _WIN32
#define EVENTHEADER_LITTLE_ENDIAN 1
#else // _WIN32
#include <endian.h>
#define EVENTHEADER_LITTLE_ENDIAN (__BYTE_ORDER == __LITTLE_ENDIAN)
#endif // _WIN32
/*--EventHeader Events--------------------------------------------------------
EventHeader is a tracing convention layered on top of Linux Tracepoints.
To reduce the number of unique Tracepoint names tracked by the kernel, we
use a small number of Tracepoints to manage a larger number of events. All
events with the same attributes (provider name, severity level, category
keyword, etc.) will share one Tracepoint.
- This means we cannot enable/disable events individually. Instead, all events
with similar attributes will be enabled/disabled as a group.
- This means we cannot rely on the kernel's Tracepoint metadata for event
identity or event field names/types. Instead, all events contain a common
header that provides event identity, core event attributes, and support for
optional event attributes. The kernel's Tracepoint metadata is used only for
the Tracepoint's name and to determine whether the event follows the
EventHeader conventions.
We define a naming scheme to be used for the shared Tracepoints:
TracepointName = ProviderName + '_' + 'L' + EventLevel + 'K' + EventKeyword +
[Options]
We define a common event layout to be used by all EventHeader events. The
event has a header, optional header extensions, and then the event data:
Event = eventheader + [HeaderExtensions] + Data
We define a format to be used for header extensions:
HeaderExtension = eventheader_extension + ExtensionData
We define a header extension to be used for activity IDs.
We define a header extension to be used for event metadata (event name, field
names, field types).
For use in the event metadata extension, we define a field type system that
supports scalar, string, binary, array, and struct.
Note that we assume that the Tracepoint name corresponding to the event is
available during event decoding. The event decoder obtains the provider name
and keyword for an event by parsing the event's Tracepoint name.
--Provider Names--------------------------------------------------------------
A provider is a component that generates events. Each event from a provider is
associated with a Provider Name that uniquely identifies the provider.
The provider name should be short, yet descriptive enough to minimize the
chance of collision and to help developers track down the component generating
the events. Hierarchical namespaces may be useful for provider names, e.g.
"MyCompany_MyOrg_MyComponent".
Restrictions:
- ProviderName may not contain ' ' or ':' characters.
- strlen(ProviderName + '_' + Attributes) must be less than
EVENTHEADER_NAME_MAX (256) characters.
- Some event APIs (e.g. tracefs) might impose additional restrictions on
tracepoint names. For best compatibility, use only ASCII identifier
characters [A-Za-z0-9_] in provider names.
Event attribute semantics should be consistent within a given provider. While
some event attributes have generally-accepted semantics (e.g. level value 3
is defined below as "warning"), the precise semantics of the attribute values
are defined at the scope of a provider (e.g. different providers will use
different criteria for what constitutes a warning). In addition, some
attributes (tag, keyword) are completely provider-defined. All events with a
particular provider name should use consistent semantics for all attributes
(e.g. keyword bit 0x1 should have a consistent meaning for all events from a
particular provider but will mean something different for other providers).
--Tracepoint Names------------------------------------------------------------
A Tracepoint is registered with the kernel for each unique combination of
ProviderName + Attributes. This allows a larger number of distinct events to
be controlled by a smaller number of kernel Tracepoints while still allowing
events to be enabled/disabled at a reasonable granularity.
The Tracepoint name for an EventHeader event is defined as:
ProviderName + '_' + 'L' + eventLevel + 'K' + eventKeyword + [Options]
or printf("%s_L%xK%lx%s", providerName, eventLevel, eventKeyword, options),
e.g. "MyProvider_L3K2a" or "OtherProvider_L5K0Gperf".
Event level is a uint8 value 1..255 indicating event severity, formatted as
lowercase hexadecimal, e.g. printf("L%x", eventLevel). The defined level values
are: 1 = critical error, 2 = error, 3 = warning, 4 = information, 5 = verbose.
Event keyword is a uint64 bitmask indicating event category membership,
formatted as lowercase hexadecimal, e.g. printf("K%lx", eventKeyword). Each
bit in the keyword corresponds to a provider-defined category, e.g. a provider
might define 0x2 = networking and 0x4 = I/O so that keyword value of 0x2|0x4 =
0x6 would indicate that an event is in both the networking and I/O categories.
Options (optional attributes) can be specified after the keyword attribute.
Each option consists of an uppercase ASCII letter (option type) followed by 0
or more ASCII digits or lowercase ASCII letters (option value). To support
consistent event names, the options must be sorted in alphabetical order, e.g.
"Aoption" should come before "Boption".
The currently defined options are:
- 'G' = provider Group name. Defines a group of providers. This can be used by
event analysis tools to find all providers that generate a certain kind of
information.
Restrictions:
- ProviderName may not contain ' ' or ':' characters.
- Tracepoint name must be less than EVENTHEADER_NAME_MAX (256)
characters in length.
- Some event APIs (e.g. tracefs) might impose additional restrictions on
tracepoint names. For best compatibility, use only ASCII identifier
characters [A-Za-z0-9_] in provider names.
--Header-----------------------------------------------------------------------
Because multiple events may share a single Tracepoint, each event must contain
information to distinguish it from other events. To enable this, each event
starts with an EventHeader structure which contains information about the
event:
- flags: Bits indicating pointer size (32 or 64 bits), byte order
(big-endian or little), and whether any header extensions are present.
- opcode: Indicates special event semantics e.g. "normal event",
"activity start event", "activity end event".
- tag: Provider-defined 16-bit value. Can be used for anything.
- id: 16-bit stable event identifier, or 0 if no identifier is assigned.
- version: 8-bit event version, incremented for e.g. field type changes.
- level: 8-bit event severity level, 1 = critical .. 5 = verbose.
(level value in event header must match the level in the Tracepoint name.)
If the extension flag is not set, the header is immediately followed by the
event payload.
If the extension flag is set, the header is immediately followed by one or more
header extensions. Each header extension has a 16-bit size, a 15-bit type code,
and a 1-bit flag indicating whether another header extension follows the
current extension. The final header extension is immediately followed by the
event payload.
The following header extensions are defined:
- Activity ID: Contains a 128-bit ID that can be used to correlate events. May
also contain the 128-bit ID of the parent activity (typically used only for
the first event of an activity).
- Metadata: Contains the event's metadata: event name, event attributes, field
names, field attributes, and field types. Both simple (e.g. Int32, HexInt16,
Float64, Char32, Uuid) and complex (e.g. NulTerminatedString8,
CountedString16, Binary, Struct, Array) types are supported.
*/
/*
eventheader struct: Core metadata for an EventHeader event.
Each EventHeader event starts with an instance of the eventheader structure.
It contains core information recorded for every event to help with event
identification, filtering, and decoding.
If eventheader.flags has the extension bit set then the eventheader is followed
by one or more eventheader_extension blocks. Otherwise the eventheader is
followed by the event payload data.
If eventheader_extension.kind has the chain flag set then the
eventheader_extension block is followed immediately (no alignment/padding) by
another extension block. Otherwise it is followed immediately (no
alignment/padding) by the event payload data.
If there is a Metadata extension then it contains the event name, field names,
and field types needed to decode the payload data. Otherwise, the payload
decoding system is defined externally, i.e. you will use the provider name to
find the appropriate decoding manifest, then use the event's id+version to
find the decoding information within the manifest, then use that decoding
information to decode the event payload data.
For a particular event definition (i.e. for a particular event name, or for a
particular nonzero event id+version), the information in the eventheader (and
in the Metadata extension, if present) should be constant. For example, instead
of having a single event with a runtime-variable level, you should have a
distinct event definition (with distinct event name and/or distinct event id)
for each level.
*/
typedef struct eventheader {
uint8_t flags; // eventheader_flags: pointer64, little_endian, extension.
uint8_t version; // If id != 0 then increment version when event layout changes.
uint16_t id; // Stable id for this event, or 0 if none.
uint16_t tag; // Provider-defined event tag, or 0 if none.
uint8_t opcode; // event_opcode: info, start activity, stop activity, etc.
uint8_t level; // event_level: critical, error, warning, info, verbose.
// Followed by: eventheader_extension block(s), then event payload.
} eventheader;
/*
Type string for use in the DIAG_IOCSREG command string.
Use EVENTHEADER_FORMAT_COMMAND to generate the full command string.
*/
#define EVENTHEADER_COMMAND_TYPES "u8 eventheader_flags; u8 version; u16 id; u16 tag; u8 opcode; u8 level"
/*
eventheader_flags enum: Values for eventheader.flags.
*/
typedef enum eventheader_flags {
eventheader_flag_none = 0, // Pointer-32, big-endian, no extensions.
eventheader_flag_pointer64 = 0x01, // Pointer is 64 bits, not 32 bits.
eventheader_flag_little_endian = 0x02, // Event uses little-endian, not big-endian.
eventheader_flag_extension = 0x04, // There is at least one eventheader_extension block.
// Pointer-size and byte-order as appropriate for the target, no
// eventheader_extension blocks present.
eventheader_flag_default = 0
| (EVENTHEADER_LITTLE_ENDIAN ? eventheader_flag_little_endian : 0)
| (sizeof(void*) == 8 ? eventheader_flag_pointer64 : 0),
// Pointer-size and byte-order as appropriate for the target, one or more
// eventheader_extension blocks present.
eventheader_flag_default_with_extension = eventheader_flag_default | eventheader_flag_extension,
} eventheader_flags;
/*
event_opcode enum: Values for eventheader.opcode. Special semantics for events.
Most events set opcode = info (0). Other opcode values add special semantics to
an event that help the event analysis tool with grouping related events. The
most frequently-used special semantics are activity-start and activity-stop.
To record an activity:
- Generate a new activity id. An activity id is a 128-bit value that must be
unique within the trace. This can be a UUID or it can be generated by any
other id-generation system that is unlikely to create the same value for any
other activity id in the same trace.
- Write an event with opcode = activity_start and with an ActivityId header
extension. The ActivityId extension should have the newly-generated activity
id, followed by the id of a parent activity (if any). If there is a parent
activity, the extension length will be 32; otherwise it will be 16.
- As appropriate, write any number of normal events (events with opcode set to
something other than activity_start or activity_stop, e.g. opcode = info). To
indicate that the events are part of the activity, each of these events
should have an ActivityId header extension with the new activity id
(extension length will be 16).
- When the activity ends, write an event with opcode = activity_stop and with
an ActivityId header extension containing the activity id of the activity
that is ending (extension length will be 16).
*/
typedef enum event_opcode {
event_opcode_info = 0, // Normal informational event.
event_opcode_activity_start, // Begins an activity (the first event to use a particular activity id).
event_opcode_activity_stop, // Ends an activity (the last event to use the particular activity id).
event_opcode_collection_start,
event_opcode_collection_stop,
event_opcode_extension,
event_opcode_reply,
event_opcode_resume,
event_opcode_suspend,
event_opcode_send,
event_opcode_receive = 0xf0,
} event_opcode;
/*
event_level enum: Values for eventheader.level.
0 is not a valid level. Values greater than 5 are permitted but are not
well-defined.
*/
typedef enum event_level {
event_level_invalid = 0,
event_level_critical_error,
event_level_error,
event_level_warning,
event_level_information,
event_level_verbose,
} event_level;
/*
eventheader_extension struct: Optional information about an EventHeader event.
If eventheader.flags has the extension bit set then the eventheader is
followed by one or more eventheader_extension blocks. Otherwise the eventheader
is followed by the event payload data.
If eventheader_extension.kind has the chain flag set then the
eventheader_extension block is followed immediately (no alignment/padding) by
another extension block. Otherwise it is followed immediately (no
alignment/padding) by the event payload data.
*/
typedef struct eventheader_extension {
uint16_t size;
uint16_t kind; // eventheader_extension_kind
// Followed by size bytes of data. No padding/alignment.
} eventheader_extension;
/*
eventheader_extension_kind enum: Values for eventheader_extension.kind.
*/
typedef enum eventheader_extension_kind {
eventheader_extension_kind_value_mask = 0x7FFF,
/*
If not set, this is the last extension block (event payload data follows).
If set, this is not the last extension block (another extension block
follows).
*/
eventheader_extension_kind_chain_flag = 0x8000,
/*
Invalid extension kind.
*/
eventheader_extension_kind_invalid = 0,
/*
Extension contains an event definition (i.e. event metadata).
Event definition format:
- char event_name[]; // Nul-terminated utf-8 string: "eventName{;attribName=attribValue}"
- 0 or more field definition blocks, tightly-packed (no padding).
Field definition block:
- char field_name[]; // Nul-terminated utf-8 string: "fieldName{;attribName=attribValue}"
- uint8_t encoding; // encoding is 0..31, with 3 flag bits.
- uint8_t format; // Present if 0 != (encoding & 128). format is 0..127, with 1 flag bit.
- uint16_t tag; // Present if 0 != (format & 128). Contains provider-defined value.
- uint16_t array_length; // Present if 0 != (encoding & 32). Contains element count of constant-length array.
Notes:
- event_name and field_name may not contain any ';' characters.
- event_name and field_name may be followed by attribute strings.
- attribute string is: ';' + attribName + '=' + attribValue.
- attribName may not contain any ';' or '=' characters.
- Semicolons in attribValue must be escaped by doubling, e.g.
"my;value" is escaped as "my;;value".
- array_length may not be 0, i.e. constant-length arrays may not be empty.
*/
eventheader_extension_kind_metadata,
/*
Extension contains activity id information.
Any event that is part of an activity has an ActivityId extension.
- Activity is started by an event with opcode = activity_start. The
ActivityId extension for the start event must contain a newly-generated
activity id and may optionally contain the parent activity id.
- Activity may contain any number of normal events (opcode something other
than activity_start or activity_stop). The ActivityId extension for each
normal event must contain the id of the associated activity (otherwise
it is not considered to be part of the activity).
- Activity is ended by an event with opcode = activity_stop. The ActivityId
extension for the stop event must contain the id of the activity that is
ending.
An activity id is a 128-bit value that is unique within this trace
session. It may be a UUID. Since UUID generation can be non-trivial, this
may also be a 128-bit LUID (locally-unique id) generated using any method
that is unlikely to conflict with any other activity ids in the same trace.
If extension.size == 16 then value is a 128-bit activity id.
If extension.size == 32 then value is a 128-bit activity id followed by a
128-bit related (parent) activity id.
*/
eventheader_extension_kind_activity_id,
} eventheader_extension_kind;
/*
event_field_encoding enum: Values for the encoding byte of a field definition.
The low 5 bits of the encoding byte contain the field's encoding. The encoding
indicates how a decoder should determine the size of the field. It also
indicates a default format behavior that should be used if the field has no
format specified or if the specified format is 0, unrecognized, or unsupported.
The top 3 bits of the field encoding byte are flags:
- carray_flag indicates that this field is a constant-length array, with the
element count specified as a 16-bit value in the event metadata. (The
element count must not be 0, i.e. constant-length arrays may not be empty.)
- varray_flag indicates that this field is a variable-length array, with the
element count specified as a 16-bit value in the event payload (immediately
before the array elements, may be 0).
- chain_flag indicates that a format byte is present after the encoding byte.
If chain_flag is not set, the format byte is omitted and is assumed to be 0.
Setting both carray_flag and varray_flag is invalid (reserved).
*/
typedef enum event_field_encoding {
// Mask for the encoding types.
event_field_encoding_value_mask = 0x1F,
// Mask for the encoding flags.
event_field_encoding_flag_mask = 0xE0,
// Flag indicating that the field is a constant-length array, with a 16-bit
// element count in the event metadata (must not be 0).
event_field_encoding_carray_flag = 0x20,
// Flag indicating that the field is a variable-length array, with a 16-bit
// element count in the payload immediately before the elements (may be 0).
event_field_encoding_varray_flag = 0x40,
// Flag indicating that an event_field_format byte follows the
// event_field_encoding byte.
event_field_encoding_chain_flag = 0x80,
// Invalid encoding value.
event_field_encoding_invalid = 0,
// 0-byte value, logically groups subsequent N fields, N = (format & 0x7F),
// N must not be 0 (empty structs are not allowed).
event_field_encoding_struct,
// 1-byte value, default format unsigned_int.
//
// Usable formats: unsigned_int, signed_int, hex_int, boolean, hex_bytes,
// string8.
event_field_encoding_value8,
// 2-byte value, default format unsigned_int.
//
// Usable formats: unsigned_int, signed_int, hex_int, boolean, hex_bytes,
// string_utf, port.
event_field_encoding_value16,
// 4-byte value, default format unsigned_int.
//
// Usable formats: unsigned_int, signed_int, hex_int, errno, pid, time,
// boolean, float, hex_bytes, string_utf, IPv4.
event_field_encoding_value32,
// 8-byte value, default format unsigned_int.
//
// Usable formats: unsigned_int, signed_int, hex_int, time, float,
// hex_bytes.
event_field_encoding_value64,
// 16-byte value, default format hex_bytes.
//
// Usable formats: hex_bytes, uuid, ipv6.
event_field_encoding_value128,
// zero-terminated uint8[], default format string_utf.
//
// Usable formats: hex_bytes, string8, string_utf, string_utf_bom,
// string_xml, string_json.
event_field_encoding_zstring_char8,
// zero-terminated uint16[], default format string_utf.
//
// Usable formats: hex_bytes, string_utf, string_utf_bom, string_xml,
// string_json.
event_field_encoding_zstring_char16,
// zero-terminated uint32[], default format string_utf.
//
// Usable formats: hex_bytes, string_utf, string_utf_bom, string_xml,
// string_json.
event_field_encoding_zstring_char32,
// uint16 Length followed by uint8 Data[Length], default format string_utf.
// Also used for binary data (format hex_bytes).
//
// Usable formats: hex_bytes, String8, string_utf, string_utf_bom,
// string_xml, string_json.
event_field_encoding_string_length16_char8,
// uint16 Length followed by uint16 Data[Length], default format
// string_utf.
//
// Usable formats: hex_bytes, string_utf, string_utf_bom, string_xml,
// string_json.
event_field_encoding_string_length16_char16,
// uint16 Length followed by uint32 Data[Length], default format
// string_utf.
//
// Usable formats: hex_bytes, string_utf, string_utf_bom, string_xml,
// string_json.
event_field_encoding_string_length16_char32,
// Invalid encoding value. Value will change in future versions of this
// header.
event_field_encoding_max,
// long-sized value, default format unsigned_int.
// This is an alias for either value32 or value64.
//
// Usable formats: unsigned_int, signed_int, hex_int, Time, Float,
// hex_bytes.
event_field_encoding_value_long =
sizeof(long) == 4 ? event_field_encoding_value32 :
sizeof(long) == 8 ? event_field_encoding_value64 :
event_field_encoding_invalid,
// pointer-sized value, default format unsigned_int.
// This is an alias for either value32 or value64.
//
// Usable formats: unsigned_int, signed_int, hex_int, Time, Float,
// hex_bytes.
event_field_encoding_value_ptr =
sizeof(void*) == 4 ? event_field_encoding_value32 :
sizeof(void*) == 8 ? event_field_encoding_value64 :
event_field_encoding_invalid,
// float-sized value, default format unsigned_int. (To treat as float,
// use event_field_format_float.)
//
// This is an alias for either value32 or value64.
event_field_encoding_value_float =
sizeof(float) == 4 ? event_field_encoding_value32 :
sizeof(float) == 8 ? event_field_encoding_value64 :
event_field_encoding_invalid,
// double-sized value, default format unsigned_int. (To treat as float,
// use event_field_format_float.)
//
// This is an alias for either value32 or value64.
event_field_encoding_value_double =
sizeof(double) == 4 ? event_field_encoding_value32 :
sizeof(double) == 8 ? event_field_encoding_value64 :
event_field_encoding_invalid,
// wchar-sized value, default format unsigned_int. (To treat as char, use
// event_field_format_string_utf.)
//
// This is an alias for either value16 or value32.
event_field_encoding_value_wchar =
sizeof(wchar_t) == 2 ? event_field_encoding_value16 :
sizeof(wchar_t) == 4 ? event_field_encoding_value32 :
event_field_encoding_invalid,
// zero-terminated wchar_t[], default format string_utf.
//
// This is an alias for either zstring_char16 or zstring_char32.
event_field_encoding_zstring_wchar =
sizeof(wchar_t) == 2 ? event_field_encoding_zstring_char16 :
sizeof(wchar_t) == 4 ? event_field_encoding_zstring_char32 :
event_field_encoding_invalid,
// uint16 Length followed by uint16 Data[Length], default format
// string_utf.
//
// This is an alias for either string_length16_char16 or
// string_length16_char32.
event_field_encoding_string_length16_wchar =
sizeof(wchar_t) == 2 ? event_field_encoding_string_length16_char16 :
sizeof(wchar_t) == 4 ? event_field_encoding_string_length16_char32 :
event_field_encoding_invalid,
} event_field_encoding;
#if defined(__cplusplus) || defined(static_assert)
static_assert(event_field_encoding_max <= event_field_encoding_carray_flag, "Too many encodings.");
static_assert(event_field_encoding_invalid != event_field_encoding_value_long, "Unsupported sizeof(long).");
static_assert(event_field_encoding_invalid != event_field_encoding_value_ptr, "Unsupported sizeof(void*).");
static_assert(event_field_encoding_invalid != event_field_encoding_value_float, "Unsupported sizeof(float).");
static_assert(event_field_encoding_invalid != event_field_encoding_value_double, "Unsupported sizeof(double).");
static_assert(event_field_encoding_invalid != event_field_encoding_value_wchar, "Unsupported sizeof(wchar_t).");
#endif
/*
event_field_format enum: Values for the format byte of a field definition.
The low 7 bits of the format byte contain the field's format.
In the case of the Struct encoding, the low 7 bits of the format byte contain
the number of logical fields in the struct (which must not be 0).
The top bit of the field format byte is the FlagChain. If set, it indicates
that a field tag (uint16) is present after the format byte. If not set, the
field tag is not present and is assumed to be 0.
*/
typedef enum event_field_format {
event_field_format_value_mask = 0x7F,
event_field_format_chain_flag = 0x80, // A field tag (uint16) follows the format byte.
event_field_format_default = 0, // Use the default format of the encoding.
event_field_format_unsigned_int, // unsigned integer, event byte order. Use with Value8..Value64 encodings.
event_field_format_signed_int, // signed integer, event byte order. Use with Value8..Value64 encodings.
event_field_format_hex_int, // hex integer, event byte order. Use with Value8..Value64 encodings.
event_field_format_errno, // errno, event byte order. Use with Value32 encoding.
event_field_format_pid, // process id, event byte order. Use with Value32 encoding.
event_field_format_time, // signed integer, event byte order, seconds since 1970. Use with Value32 or Value64 encodings.
event_field_format_boolean, // 0 = false, 1 = true, event byte order. Use with Value8..Value32 encodings.
event_field_format_float, // floating point, event byte order. Use with Value32..Value64 encodings.
event_field_format_hex_bytes, // binary, decoded as hex dump of bytes. Use with any encoding.
event_field_format_string8, // 8-bit char string, unspecified character set (usually treated as ISO-8859-1 or CP-1252). Use with Value8 and Char8 encodings.
event_field_format_string_utf, // UTF string, event byte order, code unit size based on encoding. Use with Value16..Value32 and Char8..Char32 encodings.
event_field_format_string_utf_bom,// UTF string, BOM used if present, otherwise behaves like string_utf. Use with Char8..Char32 encodings.
event_field_format_string_xml, // XML string, otherwise behaves like string_utf_bom. Use with Char8..Char32 encodings.
event_field_format_string_json, // JSON string, otherwise behaves like string_utf_bom. Use with Char8..Char32 encodings.
event_field_format_uuid, // UUID, network byte order (RFC 4122 format). Use with Value128 encoding.
event_field_format_port, // IP port, network byte order (in_port_t layout). Use with Value16 encoding.
event_field_format_ipv4, // IPv4 address, network byte order (in_addr layout). Use with Value32 encoding.
event_field_format_ipv6, // IPv6 address, in6_addr layout. Use with Value128 encoding.
} event_field_format;
enum {
// Maximum length of a Tracepoint name "ProviderName_Attributes", including nul termination.
EVENTHEADER_NAME_MAX = 256,
// Maximum length needed for a DIAG_IOCSREG command "ProviderName_Attributes CommandTypes".
EVENTHEADER_COMMAND_MAX = EVENTHEADER_NAME_MAX + sizeof(EVENTHEADER_COMMAND_TYPES)
};
/*
Macro EVENTHEADER_FORMAT_TRACEPOINT_NAME generates the Tracepoint name for an
event, tracepointName = "ProviderName_LnKnnnOptions":
char tracepointName[EVENTHEADER_NAME_MAX];
EVENTHEADER_FORMAT_TRACEPOINT_NAME(
tracepointName, sizeof(tracepointName),
providerName, eventLevel, eventKeyword, options);
Returns the value returned by snprintf:
- return >= 0 && return < tracepointNameMax indicates success.
- return < 0 || return >= tracepointNameMax indicates error.
Requires: #include <stdio.h>, #include <inttypes.h>
*/
#define EVENTHEADER_FORMAT_TRACEPOINT_NAME( \
tracepointName, tracepointNameMax, providerName, eventLevel, eventKeyword, options) \
({ \
/* Put arguments into temp vars for type-checking: */ \
char const* const _providerName = (providerName); \
uint8_t const _eventLevel = (eventLevel); \
uint64_t const _eventKeyword = (eventKeyword); \
char const* const _options = (options); \
snprintf(tracepointName, tracepointNameMax, "%s_L%xK%" PRIx64 "%s", \
_providerName, _eventLevel, _eventKeyword, _options ? _options : ""); \
}) \
/*
Macro EVENTHEADER_FORMAT_COMMAND generates the DIAG_IOCSREG command string for
an event, command = "ProviderName_LnKnnnOptions CommandTypes":
char command[EVENTHEADER_COMMAND_MAX];
EVENTHEADER_FORMAT_COMMAND(
command, sizeof(command),
providerName, eventLevel, eventKeyword, options);
Returns the value returned by snprintf:
- return >= 0 && return < commandMax indicates success.
- return < 0 || return >= commandMax indicates error.
Requires: #include <stdio.h>, #include <inttypes.h>
*/
#define EVENTHEADER_FORMAT_COMMAND( \
command, commandMax, providerName, eventLevel, eventKeyword, options) \
({ \
/* Put arguments into temp vars for type-checking: */ \
char const* const _providerName = (providerName); \
uint8_t const _eventLevel = (eventLevel); \
uint64_t const _eventKeyword = (eventKeyword); \
char const* const _options = (options); \
snprintf(command, commandMax, "%s_L%xK%" PRIx64 "%s %s", \
_providerName, _eventLevel, _eventKeyword, _options ? _options : "", \
EVENTHEADER_COMMAND_TYPES); \
}) \
#endif // _included_eventheader_h

View file

@ -0,0 +1,30 @@
add_executable(eventheader-dynamic-sample
dynamic-sample.cpp)
target_link_libraries(eventheader-dynamic-sample
PUBLIC eventheader-tracepoint tracepoint)
target_compile_features(eventheader-dynamic-sample
PRIVATE cxx_std_17)
add_executable(eventheader-sample
sample.cpp)
target_link_libraries(eventheader-sample
PUBLIC eventheader-tracepoint tracepoint)
add_executable(eventheader-tracepoint-sample
tracepoint-sample.cpp
TestProviderC.c
TestProviderCpp.cpp)
target_link_libraries(eventheader-tracepoint-sample
PUBLIC eventheader-tracepoint tracepoint)
target_compile_features(eventheader-tracepoint-sample
PRIVATE cxx_std_17)
add_executable(eventheader-interceptor-sample
interceptor-sample.cpp
TestProviderC.c
TestProviderCpp.cpp
tracepoint-file.cpp)
target_link_libraries(eventheader-interceptor-sample
PUBLIC eventheader-tracepoint)
target_compile_features(eventheader-interceptor-sample
PRIVATE cxx_std_17)

View file

@ -0,0 +1,42 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include <eventheader/TraceLoggingProvider.h>
#include <stdbool.h>
#include <uchar.h>
#define TestProvider TestProviderC
#include "TestProviderCommon.h"
static bool TestTraceLoggingLangSpecific()
{
return true;
}
TRACELOGGING_DEFINE_PROVIDER(
TestProviderC,
"TestProviderC",
// {0da7a945-e9b1-510f-0ccf-ab1af0bc095b}
(0x0da7a945, 0xe9b1, 0x510f, 0x0c, 0xcf, 0xab, 0x1a, 0xf0, 0xbc, 0x09, 0x5b));
TRACELOGGING_DEFINE_PROVIDER(
TestProviderCG,
"TestProviderC",
// {0da7a945-e9b1-510f-0ccf-ab1af0bc095b}
(0x0da7a945, 0xe9b1, 0x510f, 0x0c, 0xcf, 0xab, 0x1a, 0xf0, 0xbc, 0x09, 0x5b),
TraceLoggingOptionGroupName("msft"));
#include <stdio.h>
bool TestC()
{
printf("TestProvider Name: %s\n", TraceLoggingProviderName(TestProvider));
int err = TraceLoggingRegister(TestProviderC);
printf("TestProviderC register: %d\n", err);
bool ok = TestCommon() && TestTraceLoggingLangSpecific();
TraceLoggingUnregister(TestProviderC);
return ok && err == 0;
}

View file

@ -0,0 +1,504 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include <arpa/inet.h>
#include <limits.h>
#ifdef __cplusplus
extern "C" {
#endif
bool TestC(void);
bool TestCpp(void);
#ifdef __cplusplus
} // extern "C"
#endif
TRACELOGGING_DECLARE_PROVIDER(TestProviderC);
TRACELOGGING_DECLARE_PROVIDER(TestProviderCG);
TRACELOGGING_DECLARE_PROVIDER(TestProviderCpp);
TRACELOGGING_DECLARE_PROVIDER(TestProviderCppG);
static bool TestCommon(void)
{
bool ok = true;
// Validation of C/C++ provider interop.
{
bool enabled;
ok = TraceLoggingRegister(TestProviderCG) == 0 && ok;
ok = TraceLoggingRegister(TestProviderCppG) == 0 && ok;
enabled = TraceLoggingProviderEnabled(TestProviderCG, 5, 0);
TraceLoggingWrite(TestProviderCG, "EventCG", TraceLoggingBoolean(enabled));
enabled = TraceLoggingProviderEnabled(TestProviderCppG, 5, 0);
TraceLoggingWrite(TestProviderCppG, "EventCppG", TraceLoggingBoolean(enabled));
TraceLoggingUnregister(TestProviderCG);
TraceLoggingUnregister(TestProviderCppG);
TraceLoggingUnregister(TestProviderCG);
TraceLoggingUnregister(TestProviderCppG);
enabled = TraceLoggingProviderEnabled(TestProviderCG, 5, 0);
TraceLoggingWrite(TestProviderCG, "EventCG", TraceLoggingBoolean(enabled));
enabled = TraceLoggingProviderEnabled(TestProviderCppG, 5, 0);
TraceLoggingWrite(TestProviderCppG, "EventCppG", TraceLoggingBoolean(enabled));
}
const bool b0 = 0;
const bool b1 = 1;
const uint8_t b8 = 1;
const int32_t b32 = 1;
const int8_t i8 = 100;
const uint8_t u8 = 200;
const int16_t i16 = 30000;
const uint16_t u16 = 60000;
const int32_t i32 = 2000000000;
const uint32_t u32 = 4000000000;
const long iL = 2000000000;
const unsigned long uL = 4000000000;
const int64_t i64 = 9000000000000000000;
const uint64_t u64 = 18000000000000000000u;
const float f32 = 3.14f;
const double f64 = 6.28;
const char ch = 'A';
const char16_t u16ch = u'A';
const char32_t u32ch = U'A';
const wchar_t wch = L'B';
const intptr_t iptr = 1234;
const uintptr_t uptr = 4321;
char ch10[10] = "HowAreU8?";
char16_t u16ch10[10] = u"HowAreU16";
char32_t u32ch10[10] = U"HowAreU32";
wchar_t wch10[10] = L"Goodbye!!";
uint8_t const guid[16] = { 1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8};
void const* const pSamplePtr = (void*)(intptr_t)(-12345);
unsigned short n1 = 1;
unsigned short n5 = 5;
const uint16_t port80 = htons(80);
const uint8_t ipv4data[] = { 127, 0, 0, 1 };
in_addr_t ipv4;
memcpy(&ipv4, ipv4data, sizeof(ipv4));
const uint8_t ipv6data[] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 };
struct in6_addr ipv6;
memcpy(&ipv6, ipv6data, sizeof(ipv6));
TraceLoggingWrite(TestProvider, "CScalars1");
TraceLoggingWrite(
TestProvider,
"CScalars2",
TraceLoggingLevel(1),
TraceLoggingKeyword(0xf123456789abcdef),
TraceLoggingOpcode(2));
TraceLoggingWrite(
TestProvider,
"CScalars3",
TraceLoggingIdVersion(1000, 11),
TraceLoggingLevel(2),
TraceLoggingKeyword(0x80),
TraceLoggingOpcode(3),
TraceLoggingLevel(4),
TraceLoggingKeyword(0x05),
TraceLoggingEventTag(0x1234),
TraceLoggingDescription("Hello"),
TraceLoggingStruct(20, "Struct20", "Desc", 0xbeef),
TraceLoggingString("hi", "hi", "Descr", 0x12),
TraceLoggingCharArray(ch10, 3, "ch10", "Descri", 0x10));
TraceLoggingWriteActivity(TestProvider, "Transfer00", NULL, NULL);
TraceLoggingWriteActivity(TestProvider, "Transfer10", guid, NULL);
TraceLoggingWriteActivity(TestProvider, "Transfer11", guid, guid);
TraceLoggingWrite(TestProvider, "i8", TraceLoggingInt8(i8), TraceLoggingInt8(INT8_MIN));
TraceLoggingWrite(TestProvider, "u8", TraceLoggingUInt8(u8), TraceLoggingUInt8(UINT8_MAX));
TraceLoggingWrite(TestProvider, "i16", TraceLoggingInt16(i16), TraceLoggingInt16(INT16_MIN));
TraceLoggingWrite(TestProvider, "u16", TraceLoggingUInt16(u16), TraceLoggingUInt16(UINT16_MAX));
TraceLoggingWrite(TestProvider, "i32", TraceLoggingInt32(i32), TraceLoggingInt32(INT32_MIN));
TraceLoggingWrite(TestProvider, "u32", TraceLoggingUInt32(u32), TraceLoggingUInt32(UINT32_MAX));
TraceLoggingWrite(TestProvider, "i64", TraceLoggingInt64(i64), TraceLoggingInt64(INT64_MIN));
TraceLoggingWrite(TestProvider, "u64", TraceLoggingUInt64(u64), TraceLoggingUInt64(UINT64_MAX));
TraceLoggingWrite(TestProvider, "iptr", TraceLoggingIntPtr(iptr), TraceLoggingIntPtr(INTPTR_MIN));
TraceLoggingWrite(TestProvider, "uptr", TraceLoggingUIntPtr(uptr), TraceLoggingUIntPtr(UINTPTR_MAX));
TraceLoggingWrite(TestProvider, "iL", TraceLoggingLong(iL), TraceLoggingLong(LONG_MIN));
TraceLoggingWrite(TestProvider, "uL", TraceLoggingULong(uL), TraceLoggingULong(ULONG_MAX));
TraceLoggingWrite(TestProvider, "hi8", TraceLoggingHexInt8(i8), TraceLoggingHexInt8(INT8_MIN));
TraceLoggingWrite(TestProvider, "hu8", TraceLoggingHexUInt8(u8), TraceLoggingHexUInt8(UINT8_MAX));
TraceLoggingWrite(TestProvider, "hi16", TraceLoggingHexInt16(i16), TraceLoggingHexInt16(INT16_MIN));
TraceLoggingWrite(TestProvider, "hu16", TraceLoggingHexUInt16(u16), TraceLoggingHexUInt16(UINT16_MAX));
TraceLoggingWrite(TestProvider, "hi32", TraceLoggingHexInt32(i32), TraceLoggingHexInt32(INT32_MIN));
TraceLoggingWrite(TestProvider, "hu32", TraceLoggingHexUInt32(u32), TraceLoggingHexUInt32(UINT32_MAX));
TraceLoggingWrite(TestProvider, "hi64", TraceLoggingHexInt64(i64), TraceLoggingHexInt64(INT64_MIN));
TraceLoggingWrite(TestProvider, "hu64", TraceLoggingHexUInt64(u64), TraceLoggingHexUInt64(UINT64_MAX));
TraceLoggingWrite(TestProvider, "hiptr", TraceLoggingHexIntPtr(iptr), TraceLoggingHexIntPtr(INTPTR_MIN));
TraceLoggingWrite(TestProvider, "huptr", TraceLoggingHexUIntPtr(uptr), TraceLoggingHexUIntPtr(UINTPTR_MAX));
TraceLoggingWrite(TestProvider, "hiL", TraceLoggingHexLong(iL), TraceLoggingHexLong(LONG_MIN));
TraceLoggingWrite(TestProvider, "huL", TraceLoggingHexULong(uL), TraceLoggingHexULong(ULONG_MAX));
TraceLoggingWrite(TestProvider, "f32", TraceLoggingFloat32(f32), TraceLoggingFloat32(1.0f / 3.0f, "1/3"), TraceLoggingFloat32(0.0f / 0.0f, "NaN"), TraceLoggingFloat32(-1.0f / 0.0f, "-Inf"));
TraceLoggingWrite(TestProvider, "f64", TraceLoggingFloat64(f64), TraceLoggingFloat64(1.0 / 3.0, "1/3"), TraceLoggingFloat64(0.0 / 0.0, "NaN"), TraceLoggingFloat64(-1.0 / 0.0, "-Inf"));
TraceLoggingWrite(TestProvider, "b8", TraceLoggingBoolean(b0), TraceLoggingBoolean(b1), TraceLoggingBoolean(UINT8_MAX));
TraceLoggingWrite(TestProvider, "b32", TraceLoggingBool(b0), TraceLoggingBool(b1), TraceLoggingBool(INT32_MIN));
TraceLoggingWrite(TestProvider, "ch", TraceLoggingChar(ch), TraceLoggingChar('\xFE', "FE"));
TraceLoggingWrite(TestProvider, "u16ch", TraceLoggingChar16(u16ch), TraceLoggingChar16(u'\xFFFE', "FFFE"));
TraceLoggingWrite(TestProvider, "u32ch", TraceLoggingChar32(u32ch), TraceLoggingChar32(U'\xFFFE', "FFFE"), TraceLoggingChar32(U'\x10FFFF', "10FFFF"), TraceLoggingChar32(U'\xFFFEFDFC', "FFFEFDFC"));
TraceLoggingWrite(TestProvider, "wch", TraceLoggingWChar(wch), TraceLoggingWChar(L'\xFFFE', "FFFE"), TraceLoggingWChar(L'\x10FFFF', "10FFFF"), TraceLoggingWChar(WCHAR_MAX));
TraceLoggingWrite(TestProvider, "ptr", TraceLoggingPointer(pSamplePtr), TraceLoggingPointer((void*)UINTPTR_MAX, "UINTPTR_MAX"));
TraceLoggingWrite(TestProvider, "pid", TraceLoggingPid(i32), TraceLoggingPid(INT32_MAX));
TraceLoggingWrite(TestProvider, "port", TraceLoggingPort(port80), TraceLoggingPort(UINT16_MAX));
TraceLoggingWrite(TestProvider, "errno", TraceLoggingErrno(INT32_MIN), TraceLoggingErrno(1), TraceLoggingErrno(131));
TraceLoggingWrite(TestProvider, "t32", TraceLoggingTime32(i32), TraceLoggingTime32(INT32_MIN), TraceLoggingTime32(INT32_MAX), TraceLoggingTime32(0));
TraceLoggingWrite(TestProvider, "t64", TraceLoggingTime64(i64), TraceLoggingTime64(INT64_MIN), TraceLoggingTime64(INT64_MAX),
TraceLoggingTime64(0),
TraceLoggingTime64(-11644473600),
TraceLoggingTime64(-11644473601),
TraceLoggingTime64(910692730085),
TraceLoggingTime64(910692730086));
TraceLoggingWrite(TestProvider, "guid", TraceLoggingGuid(guid));
TraceLoggingWrite(TestProvider, "sz",
TraceLoggingString(NULL, "NULL"),
TraceLoggingString(ch10));
TraceLoggingWrite(TestProvider, "sz8",
TraceLoggingUtf8String(NULL, "NULL"),
TraceLoggingUtf8String(ch10));
TraceLoggingWrite(TestProvider, "wsz",
TraceLoggingWideString(NULL, "NULL"),
TraceLoggingWideString(wch10));
TraceLoggingWrite(TestProvider, "sz16",
TraceLoggingString16(NULL, "NULL"),
TraceLoggingString16(u16ch10));
TraceLoggingWrite(TestProvider, "sz32",
TraceLoggingString32(NULL, "NULL"),
TraceLoggingString32(u32ch10));
TraceLoggingWrite(TestProvider, "csz",
TraceLoggingCountedString(NULL, 0, "NULL"),
TraceLoggingCountedString(ch10, 5));
TraceLoggingWrite(TestProvider, "csz8",
TraceLoggingCountedUtf8String(NULL, 0, "NULL"),
TraceLoggingCountedUtf8String(ch10, 5));
TraceLoggingWrite(TestProvider, "cwsz",
TraceLoggingCountedWideString(NULL, 0, "NULL"),
TraceLoggingCountedWideString(wch10, 5));
TraceLoggingWrite(TestProvider, "csz16",
TraceLoggingCountedString16(NULL, 0, "NULL"),
TraceLoggingCountedString16(u16ch10, 5));
TraceLoggingWrite(TestProvider, "csz32",
TraceLoggingCountedString32(NULL, 0, "NULL"),
TraceLoggingCountedString32(u32ch10, 5));
TraceLoggingWrite(TestProvider, "bin",
TraceLoggingBinary(NULL, 0, "NULL"),
TraceLoggingBinary(ch10, 5));
TraceLoggingWrite(TestProvider, "ipV4",
TraceLoggingIPv4Address(ipv4),
TraceLoggingIPv4Address(UINT32_MAX));
TraceLoggingWrite(TestProvider, "ipV6",
TraceLoggingIPv6Address(&ipv6, "ipv6"),
TraceLoggingIPv6Address("\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe", "fefe..fe00"));
TraceLoggingWrite(TestProvider, "ai8",
TraceLoggingInt8FixedArray(&i8, 1, "a1"),
TraceLoggingInt8Array(&i8, n1, "s"));
TraceLoggingWrite(TestProvider, "au8",
TraceLoggingUInt8FixedArray(&u8, 1, "a1"),
TraceLoggingUInt8Array(&u8, n1, "s"));
TraceLoggingWrite(TestProvider, "ai16",
TraceLoggingInt16FixedArray(&i16, 1, "a1"),
TraceLoggingInt16Array(&i16, n1, "s"));
TraceLoggingWrite(TestProvider, "au16",
TraceLoggingUInt16FixedArray(&u16, 1, "a1"),
TraceLoggingUInt16Array(&u16, n1, "s"));
TraceLoggingWrite(TestProvider, "ai32",
TraceLoggingInt32FixedArray(&i32, 1, "a1"),
TraceLoggingInt32Array(&i32, n1, "s"));
TraceLoggingWrite(TestProvider, "au32",
TraceLoggingUInt32FixedArray(&u32, 1, "a1"),
TraceLoggingUInt32Array(&u32, n1, "s"));
TraceLoggingWrite(TestProvider, "ai64",
TraceLoggingInt64FixedArray(&i64, 1, "a1"),
TraceLoggingInt64Array(&i64, n1, "s"));
TraceLoggingWrite(TestProvider, "au64",
TraceLoggingUInt64FixedArray(&u64, 1, "a1"),
TraceLoggingUInt64Array(&u64, n1, "s"));
TraceLoggingWrite(TestProvider, "aiptr",
TraceLoggingIntPtrFixedArray(&iptr, 1, "a1"),
TraceLoggingIntPtrArray(&iptr, n1, "s"));
TraceLoggingWrite(TestProvider, "auptr",
TraceLoggingUIntPtrFixedArray(&uptr, 1, "a1"),
TraceLoggingUIntPtrArray(&uptr, n1, "s"));
TraceLoggingWrite(TestProvider, "aiL",
TraceLoggingLongFixedArray(&iL, 1, "a1"),
TraceLoggingLongArray(&iL, n1, "s"));
TraceLoggingWrite(TestProvider, "auL",
TraceLoggingULongFixedArray(&uL, 1, "a1"),
TraceLoggingULongArray(&uL, n1, "s"));
TraceLoggingWrite(TestProvider, "hai8",
TraceLoggingHexInt8FixedArray(&i8, 1, "a1"),
TraceLoggingHexInt8Array(&i8, n1, "s"));
TraceLoggingWrite(TestProvider, "hau8",
TraceLoggingHexUInt8FixedArray(&u8, 1, "a1"),
TraceLoggingHexUInt8Array(&u8, n1, "s"));
TraceLoggingWrite(TestProvider, "hai16",
TraceLoggingHexInt16FixedArray(&i16, 1, "a1"),
TraceLoggingHexInt16Array(&i16, n1, "s"));
TraceLoggingWrite(TestProvider, "hau16",
TraceLoggingHexUInt16FixedArray(&u16, 1, "a1"),
TraceLoggingHexUInt16Array(&u16, n1, "s"));
TraceLoggingWrite(TestProvider, "hai32",
TraceLoggingHexInt32FixedArray(&i32, 1, "a1"),
TraceLoggingHexInt32Array(&i32, n1, "s"));
TraceLoggingWrite(TestProvider, "hau32",
TraceLoggingHexUInt32FixedArray(&u32, 1, "a1"),
TraceLoggingHexUInt32Array(&u32, n1, "s"));
TraceLoggingWrite(TestProvider, "hai64",
TraceLoggingHexInt64FixedArray(&i64, 1, "a1"),
TraceLoggingHexInt64Array(&i64, n1, "s"));
TraceLoggingWrite(TestProvider, "hau64",
TraceLoggingHexUInt64FixedArray(&u64, 1, "a1"),
TraceLoggingHexUInt64Array(&u64, n1, "s"));
TraceLoggingWrite(TestProvider, "haiptr",
TraceLoggingHexIntPtrFixedArray(&iptr, 1, "a1"),
TraceLoggingHexIntPtrArray(&iptr, n1, "s"));
TraceLoggingWrite(TestProvider, "hauptr",
TraceLoggingHexUIntPtrFixedArray(&uptr, 1, "a1"),
TraceLoggingHexUIntPtrArray(&uptr, n1, "s"));
TraceLoggingWrite(TestProvider, "haiL",
TraceLoggingHexLongFixedArray(&iL, 1, "a1"),
TraceLoggingHexLongArray(&iL, n1, "s"));
TraceLoggingWrite(TestProvider, "hauL",
TraceLoggingHexULongFixedArray(&uL, 1, "a1"),
TraceLoggingHexULongArray(&uL, n1, "s"));
TraceLoggingWrite(TestProvider, "af32",
TraceLoggingFloat32FixedArray(&f32, 1, "a1"),
TraceLoggingFloat32Array(&f32, n1, "s"));
TraceLoggingWrite(TestProvider, "af64",
TraceLoggingFloat64FixedArray(&f64, 1, "a1"),
TraceLoggingFloat64Array(&f64, n1, "s"));
TraceLoggingWrite(TestProvider, "ab8",
TraceLoggingBooleanFixedArray(&b8, 1, "a1"),
TraceLoggingBooleanArray(&b8, n1, "s"));
TraceLoggingWrite(TestProvider, "ab32",
TraceLoggingBoolFixedArray(&b32, 1, "a1"),
TraceLoggingBoolArray(&b32, n1, "s"));
TraceLoggingWrite(TestProvider, "ach",
TraceLoggingCharFixedArray(ch10, 4, "a4"),
TraceLoggingCharArray(ch10, n5, "s5"));
TraceLoggingWrite(TestProvider, "ach16",
TraceLoggingChar16FixedArray(u16ch10, 4, "a4"),
TraceLoggingChar16Array(u16ch10, n5, "s5"));
TraceLoggingWrite(TestProvider, "ach32",
TraceLoggingChar32FixedArray(u32ch10, 4, "a4"),
TraceLoggingChar32Array(u32ch10, n5, "s5"));
TraceLoggingWrite(TestProvider, "awch",
TraceLoggingWCharFixedArray(wch10, 4, "a4"),
TraceLoggingWCharArray(wch10, n5, "s5"));
TraceLoggingWrite(TestProvider, "aptr",
TraceLoggingPointerFixedArray(&pSamplePtr, 1, "a1"),
TraceLoggingPointerArray(&pSamplePtr, n1, "s"));
TraceLoggingWrite(TestProvider, "apid",
TraceLoggingPidFixedArray(&i32, 1, "a1"),
TraceLoggingPidArray(&i32, n1, "s"));
TraceLoggingWrite(TestProvider, "aport",
TraceLoggingPortFixedArray(&u16, 1, "a1"),
TraceLoggingPortArray(&u16, n1, "s"));
TraceLoggingWrite(TestProvider, "aerrno",
TraceLoggingErrnoFixedArray(&i32, 1, "a1"),
TraceLoggingErrnoArray(&i32, n1, "s"));
TraceLoggingWrite(TestProvider, "aft",
TraceLoggingTime32FixedArray(&i32, 1, "a1"),
TraceLoggingTime32Array(&i32, n1, "s"));
TraceLoggingWrite(TestProvider, "auft",
TraceLoggingTime64FixedArray(&i64, 1, "a1"),
TraceLoggingTime64Array(&i64, n1, "s"));
TraceLoggingWrite(TestProvider, "ag",
TraceLoggingPackedMetadataEx(event_field_encoding_value128 | event_field_encoding_varray_flag, event_field_format_uuid, "s0"),
TraceLoggingPackedData(u"\x0", 2), // 0 elements in array
TraceLoggingPackedMetadataEx(event_field_encoding_value128 | event_field_encoding_varray_flag, event_field_format_uuid, "s1"),
TraceLoggingPackedData(u"\x1", 2), // 1 element in array
TraceLoggingPackedData(guid, sizeof(guid)));
TraceLoggingWrite(TestProvider, "ahexbytes",
TraceLoggingPackedMetadataEx(event_field_encoding_value128 | event_field_encoding_varray_flag, event_field_format_hex_bytes, "s0"),
TraceLoggingPackedData(u"\x0", 2), // 0 elements in array
TraceLoggingPackedMetadataEx(event_field_encoding_value128 | event_field_encoding_varray_flag, event_field_format_hex_bytes, "s1"),
TraceLoggingPackedData(u"\x1", 2), // 1 element in array
TraceLoggingPackedData(guid, sizeof(guid)));
struct LChar8 { uint16_t x; uint16_t l; char c[10]; };
struct LChar16 { uint16_t x; uint16_t l; char16_t c[10]; };
struct LChar32 { uint16_t x; uint16_t l; char32_t c[10]; };
static struct LChar8 const lchar8 = { 0, 4, { 'h','j','k','l' } };
static struct LChar16 const lchar16 = { 0, 4, { 'h','j','k','l' } };
static struct LChar32 const lchar32 = { 0, 4, { 'h','j','k','l' } };
TraceLoggingWrite(TestProvider, "Default",
TraceLoggingPackedField(&u8, 1, event_field_encoding_value8, "V8"),
TraceLoggingPackedField(&u16, 2, event_field_encoding_value16, "V16"),
TraceLoggingPackedField(&u32, 4, event_field_encoding_value32, "V32"),
TraceLoggingPackedField(&u64, 8, event_field_encoding_value64, "V64"),
TraceLoggingPackedField(&guid, 16, event_field_encoding_value128, "V128"),
TraceLoggingPackedField(lchar8.c, 5, event_field_encoding_zstring_char8, "NChar8"),
TraceLoggingPackedField(lchar16.c, 10, event_field_encoding_zstring_char16, "NChar16"),
TraceLoggingPackedField(lchar32.c, 20, event_field_encoding_zstring_char32, "NChar32"),
TraceLoggingPackedField(&lchar8.l, 6, event_field_encoding_string_length16_char8, "LChar8"),
TraceLoggingPackedField(&lchar16.l, 10, event_field_encoding_string_length16_char16, "LChar16"),
TraceLoggingPackedField(&lchar32.l, 18, event_field_encoding_string_length16_char32, "LChar32"));
TraceLoggingWrite(TestProvider, "HexBytes",
TraceLoggingPackedFieldEx(&guid, 1, event_field_encoding_value8, event_field_format_hex_bytes, "V8"),
TraceLoggingPackedFieldEx(&guid, 2, event_field_encoding_value16, event_field_format_hex_bytes, "V16"),
TraceLoggingPackedFieldEx(&guid, 4, event_field_encoding_value32, event_field_format_hex_bytes, "V32"),
TraceLoggingPackedFieldEx(&guid, 8, event_field_encoding_value64, event_field_format_hex_bytes, "V64"),
TraceLoggingPackedFieldEx(&guid, 16, event_field_encoding_value128, event_field_format_hex_bytes, "V128"),
TraceLoggingPackedFieldEx(lchar8.c, 5, event_field_encoding_zstring_char8, event_field_format_hex_bytes, "NChar8"),
TraceLoggingPackedFieldEx(lchar16.c, 10, event_field_encoding_zstring_char16, event_field_format_hex_bytes, "NChar16"),
TraceLoggingPackedFieldEx(lchar32.c, 20, event_field_encoding_zstring_char32, event_field_format_hex_bytes, "NChar32"),
TraceLoggingPackedFieldEx(&lchar8.l, 6, event_field_encoding_string_length16_char8, event_field_format_hex_bytes, "LChar8"),
TraceLoggingPackedFieldEx(&lchar16.l, 10, event_field_encoding_string_length16_char16, event_field_format_hex_bytes, "LChar16"),
TraceLoggingPackedFieldEx(&lchar32.l, 18, event_field_encoding_string_length16_char32, event_field_format_hex_bytes, "LChar32"));
uint16_t false16 = 0;
uint16_t true16 = 1;
TraceLoggingWrite(TestProvider, "Bool16",
TraceLoggingPackedFieldEx(&false16, 2, event_field_encoding_value16, event_field_format_boolean, "false16"),
TraceLoggingPackedFieldEx(&true16, 2, event_field_encoding_value16, event_field_format_boolean, "true16"),
TraceLoggingPackedFieldEx(&u16, 2, event_field_encoding_value16, event_field_format_boolean, "u16"),
TraceLoggingPackedFieldEx(&i16, 2, event_field_encoding_value16, event_field_format_boolean, "i16"));
TraceLoggingWrite(TestProvider, "StringUtf",
TraceLoggingPackedFieldEx(lchar8.c, 5, event_field_encoding_zstring_char8, event_field_format_string_utf, "NChar8"),
TraceLoggingPackedFieldEx(lchar16.c, 10, event_field_encoding_zstring_char16, event_field_format_string_utf, "NChar16"),
TraceLoggingPackedFieldEx(lchar32.c, 20, event_field_encoding_zstring_char32, event_field_format_string_utf, "NChar32"),
TraceLoggingPackedFieldEx(&lchar8.l, 6, event_field_encoding_string_length16_char8, event_field_format_string_utf, "LChar8"),
TraceLoggingPackedFieldEx(&lchar16.l, 10, event_field_encoding_string_length16_char16, event_field_format_string_utf, "LChar16"),
TraceLoggingPackedFieldEx(&lchar32.l, 18, event_field_encoding_string_length16_char32, event_field_format_string_utf, "LChar32"));
TraceLoggingWrite(TestProvider, "StringUtfBom-NoBom",
TraceLoggingPackedFieldEx(lchar8.c, 5, event_field_encoding_zstring_char8, event_field_format_string_utf_bom, "NChar8"),
TraceLoggingPackedFieldEx(lchar16.c, 10, event_field_encoding_zstring_char16, event_field_format_string_utf_bom, "NChar16"),
TraceLoggingPackedFieldEx(lchar32.c, 20, event_field_encoding_zstring_char32, event_field_format_string_utf_bom, "NChar32"),
TraceLoggingPackedFieldEx(&lchar8.l, 6, event_field_encoding_string_length16_char8, event_field_format_string_utf_bom, "LChar8"),
TraceLoggingPackedFieldEx(&lchar16.l, 10, event_field_encoding_string_length16_char16, event_field_format_string_utf_bom, "LChar16"),
TraceLoggingPackedFieldEx(&lchar32.l, 18, event_field_encoding_string_length16_char32, event_field_format_string_utf_bom, "LChar32"));
TraceLoggingWrite(TestProvider, "StringXml-NoBom",
TraceLoggingPackedFieldEx(lchar8.c, 5, event_field_encoding_zstring_char8, event_field_format_string_xml, "NChar8"),
TraceLoggingPackedFieldEx(lchar16.c, 10, event_field_encoding_zstring_char16, event_field_format_string_xml, "NChar16"),
TraceLoggingPackedFieldEx(lchar32.c, 20, event_field_encoding_zstring_char32, event_field_format_string_xml, "NChar32"),
TraceLoggingPackedFieldEx(&lchar8.l, 6, event_field_encoding_string_length16_char8, event_field_format_string_xml, "LChar8"),
TraceLoggingPackedFieldEx(&lchar16.l, 10, event_field_encoding_string_length16_char16, event_field_format_string_xml, "LChar16"),
TraceLoggingPackedFieldEx(&lchar32.l, 18, event_field_encoding_string_length16_char32, event_field_format_string_xml, "LChar32"));
TraceLoggingWrite(TestProvider, "StringJson-NoBom",
TraceLoggingPackedFieldEx(lchar8.c, 5, event_field_encoding_zstring_char8, event_field_format_string_json, "NChar8"),
TraceLoggingPackedFieldEx(lchar16.c, 10, event_field_encoding_zstring_char16, event_field_format_string_json, "NChar16"),
TraceLoggingPackedFieldEx(lchar32.c, 20, event_field_encoding_zstring_char32, event_field_format_string_json, "NChar32"),
TraceLoggingPackedFieldEx(&lchar8.l, 6, event_field_encoding_string_length16_char8, event_field_format_string_json, "LChar8"),
TraceLoggingPackedFieldEx(&lchar16.l, 10, event_field_encoding_string_length16_char16, event_field_format_string_json, "LChar16"),
TraceLoggingPackedFieldEx(&lchar32.l, 18, event_field_encoding_string_length16_char32, event_field_format_string_json, "LChar32"));
static struct LChar8 const lcharBom8 = { 0, 7, { '\xEF', '\xBB', '\xBF', 'h','j','k','l' } };
static struct LChar16 const lcharBom16 = { 0, 5, { 0xFEFF, 'h','j','k','l' } };
static struct LChar32 const lcharBom32 = { 0, 5, { 0xFEFF, 'h','j','k','l' } };
TraceLoggingWrite(TestProvider, "StringUtfBom-Bom",
TraceLoggingPackedFieldEx(lcharBom8.c, 8, event_field_encoding_zstring_char8, event_field_format_string_utf_bom, "NChar8"),
TraceLoggingPackedFieldEx(lcharBom16.c, 12, event_field_encoding_zstring_char16, event_field_format_string_utf_bom, "NChar16"),
TraceLoggingPackedFieldEx(lcharBom32.c, 24, event_field_encoding_zstring_char32, event_field_format_string_utf_bom, "NChar32"),
TraceLoggingPackedFieldEx(&lcharBom8.l, 9, event_field_encoding_string_length16_char8, event_field_format_string_utf_bom, "LChar8"),
TraceLoggingPackedFieldEx(&lcharBom16.l, 12, event_field_encoding_string_length16_char16, event_field_format_string_utf_bom, "LChar16"),
TraceLoggingPackedFieldEx(&lcharBom32.l, 22, event_field_encoding_string_length16_char32, event_field_format_string_utf_bom, "LChar32"));
TraceLoggingWrite(TestProvider, "StringXml-Bom",
TraceLoggingPackedFieldEx(lcharBom8.c, 8, event_field_encoding_zstring_char8, event_field_format_string_xml, "NChar8"),
TraceLoggingPackedFieldEx(lcharBom16.c, 12, event_field_encoding_zstring_char16, event_field_format_string_xml, "NChar16"),
TraceLoggingPackedFieldEx(lcharBom32.c, 24, event_field_encoding_zstring_char32, event_field_format_string_xml, "NChar32"),
TraceLoggingPackedFieldEx(&lcharBom8.l, 9, event_field_encoding_string_length16_char8, event_field_format_string_xml, "LChar8"),
TraceLoggingPackedFieldEx(&lcharBom16.l, 12, event_field_encoding_string_length16_char16, event_field_format_string_xml, "LChar16"),
TraceLoggingPackedFieldEx(&lcharBom32.l, 22, event_field_encoding_string_length16_char32, event_field_format_string_xml, "LChar32"));
TraceLoggingWrite(TestProvider, "StringJson-Bom",
TraceLoggingPackedFieldEx(lcharBom8.c, 8, event_field_encoding_zstring_char8, event_field_format_string_json, "NChar8"),
TraceLoggingPackedFieldEx(lcharBom16.c, 12, event_field_encoding_zstring_char16, event_field_format_string_json, "NChar16"),
TraceLoggingPackedFieldEx(lcharBom32.c, 24, event_field_encoding_zstring_char32, event_field_format_string_json, "NChar32"),
TraceLoggingPackedFieldEx(&lcharBom8.l, 9, event_field_encoding_string_length16_char8, event_field_format_string_json, "LChar8"),
TraceLoggingPackedFieldEx(&lcharBom16.l, 12, event_field_encoding_string_length16_char16, event_field_format_string_json, "LChar16"),
TraceLoggingPackedFieldEx(&lcharBom32.l, 22, event_field_encoding_string_length16_char32, event_field_format_string_json, "LChar32"));
static struct LChar8 const lcharXBom8 = { 0, 7, { '\xEF', '\xBB', '\xBF', 'h','j','k','l' } };
static struct LChar16 const lcharXBom16 = { 0, 5, { 0xFFFE, 0x6800, 0x6a00, 0x6b00, 0x6c00 } };
static struct LChar32 const lcharXBom32 = { 0, 5, { 0xFFFE0000, 0x68000000, 0x6a000000, 0x6b000000, 0x6c000000 } };
TraceLoggingWrite(TestProvider, "StringUtfBom-XBom",
TraceLoggingPackedFieldEx(lcharXBom8.c, 8, event_field_encoding_zstring_char8, event_field_format_string_utf_bom, "NChar8"),
TraceLoggingPackedFieldEx(lcharXBom16.c, 12, event_field_encoding_zstring_char16, event_field_format_string_utf_bom, "NChar16"),
TraceLoggingPackedFieldEx(lcharXBom32.c, 24, event_field_encoding_zstring_char32, event_field_format_string_utf_bom, "NChar32"),
TraceLoggingPackedFieldEx(&lcharXBom8.l, 9, event_field_encoding_string_length16_char8, event_field_format_string_utf_bom, "LChar8"),
TraceLoggingPackedFieldEx(&lcharXBom16.l, 12, event_field_encoding_string_length16_char16, event_field_format_string_utf_bom, "LChar16"),
TraceLoggingPackedFieldEx(&lcharXBom32.l, 22, event_field_encoding_string_length16_char32, event_field_format_string_utf_bom, "LChar32"));
TraceLoggingWrite(TestProvider, "StringXml-XBom",
TraceLoggingPackedFieldEx(lcharXBom8.c, 8, event_field_encoding_zstring_char8, event_field_format_string_xml, "NChar8"),
TraceLoggingPackedFieldEx(lcharXBom16.c, 12, event_field_encoding_zstring_char16, event_field_format_string_xml, "NChar16"),
TraceLoggingPackedFieldEx(lcharXBom32.c, 24, event_field_encoding_zstring_char32, event_field_format_string_xml, "NChar32"),
TraceLoggingPackedFieldEx(&lcharXBom8.l, 9, event_field_encoding_string_length16_char8, event_field_format_string_xml, "LChar8"),
TraceLoggingPackedFieldEx(&lcharXBom16.l, 12, event_field_encoding_string_length16_char16, event_field_format_string_xml, "LChar16"),
TraceLoggingPackedFieldEx(&lcharXBom32.l, 22, event_field_encoding_string_length16_char32, event_field_format_string_xml, "LChar32"));
TraceLoggingWrite(TestProvider, "StringJson-XBom",
TraceLoggingPackedFieldEx(lcharXBom8.c, 8, event_field_encoding_zstring_char8, event_field_format_string_json, "NChar8"),
TraceLoggingPackedFieldEx(lcharXBom16.c, 12, event_field_encoding_zstring_char16, event_field_format_string_json, "NChar16"),
TraceLoggingPackedFieldEx(lcharXBom32.c, 24, event_field_encoding_zstring_char32, event_field_format_string_json, "NChar32"),
TraceLoggingPackedFieldEx(&lcharXBom8.l, 9, event_field_encoding_string_length16_char8, event_field_format_string_json, "LChar8"),
TraceLoggingPackedFieldEx(&lcharXBom16.l, 12, event_field_encoding_string_length16_char16, event_field_format_string_json, "LChar16"),
TraceLoggingPackedFieldEx(&lcharXBom32.l, 22, event_field_encoding_string_length16_char32, event_field_format_string_json, "LChar32"));
uint16_t const n3 = 3;
TraceLoggingWrite(TestProvider, "Packed",
TraceLoggingInt32(5, "five"),
TraceLoggingPackedStruct(1, "Struct1"),
TraceLoggingPackedData(lchar8.c, 5),
TraceLoggingPackedMetadata(event_field_encoding_zstring_char8, "NChar8"),
TraceLoggingPackedStructArray(1, "StructArray"),
TraceLoggingPackedData(&n3, 2),
TraceLoggingPackedStruct(2, "Struct2"),
TraceLoggingPackedMetadataEx(event_field_encoding_value8, event_field_format_string8, "K"),
TraceLoggingPackedMetadataEx(event_field_encoding_zstring_char16, event_field_format_hex_bytes, "NChar16"),
TraceLoggingPackedData("A", 1),
TraceLoggingPackedData(lchar16.c, 10),
TraceLoggingPackedData("B", 1),
TraceLoggingPackedData(lcharBom16.c, 12),
TraceLoggingPackedData("C", 1),
TraceLoggingPackedData(lcharXBom16.c, 12),
TraceLoggingInt32(5, "five"));
TraceLoggingWrite(TestProvider, "Packed0",
TraceLoggingInt32(5, "five"),
TraceLoggingPackedStruct(1, "Struct1"),
TraceLoggingPackedData(lchar8.c, 5),
TraceLoggingPackedMetadata(event_field_encoding_zstring_char8, "NChar8"),
TraceLoggingPackedStructArray(1, "StructArray"),
TraceLoggingPackedData(u"", 2), // Zero items in array
TraceLoggingPackedStruct(2, "Struct2"),
TraceLoggingPackedMetadataEx(event_field_encoding_value8, event_field_format_string8, "K"),
TraceLoggingPackedMetadataEx(event_field_encoding_zstring_char16, event_field_format_hex_bytes, "NChar16"),
TraceLoggingInt32(5, "five"));
static const char MyStrings3[] = "ABC\0\0XYZ";
TraceLoggingWrite(TestProvider, "PackedComplexArray",
TraceLoggingInt32(5, "five"),
TraceLoggingPackedData(u"\x03", 2), // 3 items in array
TraceLoggingPackedField(
MyStrings3, sizeof(MyStrings3),
event_field_encoding_zstring_char8 | event_field_encoding_varray_flag,
"MyStrings3"),
TraceLoggingInt32(5, "five"));
return true;
}

View file

@ -0,0 +1,124 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include <eventheader/TraceLoggingProvider.h>
#define TestProvider TestProviderCpp
#include "TestProviderCommon.h"
static bool TestTraceLoggingLangSpecific()
{
void const* const pSamplePtr = (void*)(intptr_t)(-12345);
TraceLoggingWrite(TestProvider, "Value:bool",
TraceLoggingValue(false, "false", "desc", 0x1234),
TraceLoggingValue(true));
TraceLoggingWrite(TestProvider, "Value:char",
TraceLoggingValue('\0', "0"),
TraceLoggingValue('A', "A"));
TraceLoggingWrite(TestProvider, "Value:char16",
TraceLoggingValue(u'\0', "0"),
TraceLoggingValue(u'A', "A"));
TraceLoggingWrite(TestProvider, "Value:char32",
TraceLoggingValue(U'\0', "0"),
TraceLoggingValue(U'A', "A"));
TraceLoggingWrite(TestProvider, "Value:wchar",
TraceLoggingValue(L'\0', "0"),
TraceLoggingValue(L'A', "A"));
TraceLoggingWrite(TestProvider, "Value:schar",
TraceLoggingValue((signed char)0, "0"),
TraceLoggingValue((signed char)L'A', "A"));
TraceLoggingWrite(TestProvider, "Value:uchar",
TraceLoggingValue((unsigned char)0, "0"),
TraceLoggingValue((unsigned char)L'A', "A"));
TraceLoggingWrite(TestProvider, "Value:sshort",
TraceLoggingValue((signed short)0, "0"),
TraceLoggingValue((signed short)L'A', "A"));
TraceLoggingWrite(TestProvider, "Value:ushort",
TraceLoggingValue((unsigned short)0, "0"),
TraceLoggingValue((unsigned short)L'A', "A"));
TraceLoggingWrite(TestProvider, "Value:sint",
TraceLoggingValue((signed int)0, "0"),
TraceLoggingValue((signed int)L'A', "A"));
TraceLoggingWrite(TestProvider, "Value:uint",
TraceLoggingValue((unsigned int)0, "0"),
TraceLoggingValue((unsigned int)L'A', "A"));
TraceLoggingWrite(TestProvider, "Value:slong",
TraceLoggingValue((signed long)0, "0"),
TraceLoggingValue((signed long)L'A', "A"));
TraceLoggingWrite(TestProvider, "Value:ulong",
TraceLoggingValue((unsigned long)0, "0"),
TraceLoggingValue((unsigned long)L'A', "A"));
TraceLoggingWrite(TestProvider, "Value:slonglong",
TraceLoggingValue((signed long long)0, "0"),
TraceLoggingValue((signed long long)L'A', "A"));
TraceLoggingWrite(TestProvider, "Value:ulonglong",
TraceLoggingValue((unsigned long long)0, "0"),
TraceLoggingValue((unsigned long long)L'A', "A"));
TraceLoggingWrite(TestProvider, "Value:float",
TraceLoggingValue(0.0f, "0"),
TraceLoggingValue(65.0f, "65"));
TraceLoggingWrite(TestProvider, "Value:double",
TraceLoggingValue(0.0, "0"),
TraceLoggingValue(65.0, "65"));
TraceLoggingWrite(TestProvider, "Value:void*",
TraceLoggingValue((void*)0, "0"),
TraceLoggingValue((void*)pSamplePtr, "p"));
TraceLoggingWrite(TestProvider, "Value:cvoid*",
TraceLoggingValue((void const*)0, "0"),
TraceLoggingValue((void const*)pSamplePtr, "p"));
TraceLoggingWrite(TestProvider, "Value:char*",
TraceLoggingValue((char*)0, "0"),
TraceLoggingValue((char*)"hello", "hello"));
TraceLoggingWrite(TestProvider, "Value:cchar*",
TraceLoggingValue((char const*)0, "0"),
TraceLoggingValue((char const*)"hello", "hello"));
TraceLoggingWrite(TestProvider, "Value:char16_t*",
TraceLoggingValue((char16_t*)0, "0"),
TraceLoggingValue((char16_t*)u"hello", "hello"));
TraceLoggingWrite(TestProvider, "Value:cchar16_t*",
TraceLoggingValue((char16_t const*)0, "0"),
TraceLoggingValue((char16_t const*)u"hello", "hello"));
TraceLoggingWrite(TestProvider, "Value:char32_t*",
TraceLoggingValue((char32_t*)0, "0"),
TraceLoggingValue((char32_t*)U"hello", "hello"));
TraceLoggingWrite(TestProvider, "Value:cchar32_t*",
TraceLoggingValue((char32_t const*)0, "0"),
TraceLoggingValue((char32_t const*)U"hello", "hello"));
TraceLoggingWrite(TestProvider, "Value:wchar_t*",
TraceLoggingValue((wchar_t*)0, "0"),
TraceLoggingValue((wchar_t*)L"hello", "hello"));
TraceLoggingWrite(TestProvider, "Value:cwchar_t*",
TraceLoggingValue((wchar_t const*)0, "0"),
TraceLoggingValue((wchar_t const*)L"hello", "hello"));
return true;
}
TRACELOGGING_DEFINE_PROVIDER(
TestProviderCpp,
"TestProviderCpp",
// {3f3dc547-92d7-59d6-ed26-053336a36f9b}
(0x3f3dc547, 0x92d7, 0x59d6, 0xed, 0x26, 0x05, 0x33, 0x36, 0xa3, 0x6f, 0x9b));
TRACELOGGING_DEFINE_PROVIDER(
TestProviderCppG,
"TestProviderCpp",
// {3f3dc547-92d7-59d6-ed26-053336a36f9b}
(0x3f3dc547, 0x92d7, 0x59d6, 0xed, 0x26, 0x05, 0x33, 0x36, 0xa3, 0x6f, 0x9b),
TraceLoggingOptionGroupName("msft"));
#include <stdio.h>
bool TestCpp()
{
printf("TestProvider Name: %s\n", TraceLoggingProviderName(TestProvider));
int err = TraceLoggingRegister(TestProviderCpp);
printf("TestProviderCpp register: %d\n", err);
bool ok = TestCommon() && TestTraceLoggingLangSpecific();
TraceLoggingUnregister(TestProviderCpp);
return ok && err == 0;
}

View file

@ -0,0 +1,171 @@
#include <eventheader/EventHeaderDynamic.h>
#include <stdio.h>
static char const guid1[16] = "123456789abcdef";
static char const* const CharStrings[2] = {
"abc", "123"
};
static wchar_t const* const WcharStrings[2] = {
L"Labc", L"L123"
};
static uint8_t arrayOf5Bytes[5] = { 1, 2, 3, 4, 5 };
int main()
{
int err;
/*
A provider is a group of events. The provider has a provider name and an
optional group name.
Provider name must be a valid C identifier: starts with underscore or ASCII
letter; remaining chars may be underscores, ASCII letters, or ASCII digits.
The provider is not used to directly write events. Instead, you use the
provider to create an EventSet or look up an existing EventSet, and then
you use the EventSet to write the events.
The EventSet is thread-safe, but the provider is not thread-safe. Possible
multi-threaded usage patterns for the provider would include the following:
- Have a reader-writer lock for the provider. Take an exclusive lock for
non-const operations like RegisterSet() and Unregister(). Take a shared
lock for other provider operations like FindSet().
- Create the provider and do all of the necessary RegisterSet() calls
before any other threads start using it. Then you can call the const
methods like FindSet() on any thread as needed without any lock as long
as nobody is calling any non-const methods.
- Use your own thread-safe data structure to keep track of all of the
EventSets you need. Take a lock if you ever need to register a new set.
*/
ehd::Provider provider1("EhdProv1");
printf("provider1: Name=\"%.*s\" Options=\"%.*s\"\n",
(int)provider1.Name().size(), provider1.Name().data(),
(int)provider1.Options().size(), provider1.Options().data());
/*
A provider may optionally have a group name. If present, it must contain
only ASCII lowercase letters and ASCII digits.
*/
ehd::Provider provider2("EhdProv2", "mygroup");
printf("provider2: Name=\"%.*s\" Options=\"%.*s\"\n",
(int)provider2.Name().size(), provider2.Name().data(),
(int)provider2.Options().size(), provider2.Options().data());
/*
An event set is required for writing events. Each event set is for a
provider + event level + event keyword combination. Each event set
corresponds to one unique tracepoint name.
- Get an event set by calling provider.RegisterSet(). It will return a
previously-created event set if one already exists, or make a new one
if one does not already exist. It returns shared_ptr<EventSet> on
success or nullptr if out of memory.
- Get a previously-created event set by calling provider.FindSet(),
which returns shared_ptr<EventSet>, or nullptr if not found.
The shared_ptr<EventSet> will stop working if the provider is closed,
but nothing bad will happen if you use it after the provider closes.
Note that provider.RegisterSet() is not thread-safe, but the returned
shared_ptr<EventSet> is thread-safe.
If RegisterSet() hits an out-of-memory error, it returns nullptr.
If RegisterSet() hits any other error, it returns an inactive EventSet.
It is safe to use an inactive EventSet -- it will just always be disabled.
If RegisterSet() succeeds, it returns an active EventSet. The EventSet
becomes inactive when the provider is unregistered or destroyed.
*/
auto EhdProv1_L5K1 = provider1.RegisterSet(event_level_verbose, 1);
/*
For debugging purposes, you can check eventSet->Errno() to see whether the
event set was registered successfully.
*/
if (EhdProv1_L5K1) // Protect against possible out-of-memory condition
{
printf("EhdProv1_L5K1: err=%u enabled=%u\n",
EhdProv1_L5K1->Errno(), EhdProv1_L5K1->Enabled());
}
auto EhdProv2_L4K2Gmygroup = provider2.RegisterSet(event_level_information, 2);
if (EhdProv2_L4K2Gmygroup)
{
printf("EhdProv2_L4K2Gmygroup: err=%u enabled=%u\n",
EhdProv2_L4K2Gmygroup->Errno(), EhdProv2_L4K2Gmygroup->Enabled());
}
/*
Use an EventBuilder to create the event. Call eventBuilder.Reset() to
clear the eventBuilder and set the name and tag, call other methods to set
attributes or add fields, and call eventBuilder.Write() to send the event
to the kernel.
Note that EventBuilder is reusable. If you need to write several events,
you might see a small performance improvement by reusing the same
EventBuilder for several events instead of creating a new one for each
event (it stores two std::vector<char> buffers, so reusing the builder
can reduce heap allocation/deallocation).
*/
ehd::EventBuilder eb;
size_t bookmark;
/*
Building and writing an event is a waste of CPU time if the event is not
enabled. It's usually more efficient to check whether the event is enabled
before building and writing the event.
*/
if (EhdProv1_L5K1 && // If non-null (guard against out-of-memory from RegisterSet).
EhdProv1_L5K1->Enabled()) // Only build and write if event is enabled.
{
eb.Reset("Name1", 0x123); // Clear the previous event (if any), then set event name and tag.
eb.IdVersion(1, 2); // Set the event's durable id (if any).
eb.Opcode(event_opcode_activity_start); // Set the event's opcode (if any).
eb.AddValue("u8", (uint8_t)1, event_field_format_default); // Default format for 8-bit is unsigned.
eb.AddValue("guid", *(ehd::Value128 const*)guid1, event_field_format_uuid); // Use Value128 struct for GUID and IPv6.
eb.AddStruct("struct", 1, 0, &bookmark); // The next N fields are sub-fields of "struct".
eb.AddString<char>("str", "str_val", event_field_format_default); // Default format for string is UTF.
eb.AddNulTerminatedString("str", std::wstring_view(L"zstr_\0val"), event_field_format_default); // Chars after '\0' ignored.
eb.SetStructFieldCount(bookmark, 2); // Update N to be 2.
eb.AddValueRange("UintRange", &arrayOf5Bytes[0], &arrayOf5Bytes[5], event_field_format_default);
eb.AddStringRange<char>("StringRange", &CharStrings[0], &CharStrings[2], event_field_format_default);
eb.AddNulTerminatedStringRange<wchar_t>("NtStringRange", &WcharStrings[0], &WcharStrings[2], event_field_format_default);
eb.AddValue("u32", (uint32_t)1, event_field_format_default);
err = eb.Write(*EhdProv1_L5K1, guid1, guid1); // Write the event. Error code is only for debugging.
printf("EhdProv1_L5K1: %u\n", err);
}
/*
For convenience (nicer syntax), the Enabled(eventSet) function returns
true if eventSet is non-null and enabled.
*/
if (Enabled(EhdProv2_L4K2Gmygroup)) // If non-null and enabled.
{
/*
If you prefer, you can use functional style to build and write the
event in one statement.
*/
err = eb.Reset("Name2")
.IdVersion(1, 2) // Set the event's durable id (if any).
.Opcode(event_opcode_activity_start) // Set the event's opcode (if any).
.AddValue("u8", (uint8_t)1, event_field_format_default) // Default format for 8-bit is unsigned.
.AddValue("guid", *(ehd::Value128 const*)guid1, event_field_format_uuid) // Use Value128 struct for GUID and IPv6.
.AddStruct("struct", 2) // The next 2 fields are sub-fields of "struct".
.AddString<char>("str", "str_val", event_field_format_default) // Default format for string is UTF.
.AddNulTerminatedString("str", std::wstring_view(L"zstr_\0val"), event_field_format_default) // Chars after '\0' ignored.
.AddValueRange("UintRange", &arrayOf5Bytes[0], &arrayOf5Bytes[5], event_field_format_default)
.AddStringRange<char>("StringRange", &CharStrings[0], &CharStrings[2], event_field_format_default)
.AddNulTerminatedStringRange<wchar_t>("NtStringRange", &WcharStrings[0], &WcharStrings[2], event_field_format_default)
.AddValue("u32", (uint32_t)1, event_field_format_default)
.Write(*EhdProv2_L4K2Gmygroup);
printf("EhdProv2_L4K2Gmygroup: %u\n", err);
}
return 0;
}
#include <errno.h>
static_assert(EBADF == 9, "EBADF != 9");

View file

@ -0,0 +1,82 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include <eventheader/TraceLoggingProvider.h>
#include <stdio.h>
#include <errno.h>
#include <inttypes.h>
TRACELOGGING_DEFINE_PROVIDER(
LongProvider,
"Long_Provider_Name_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX0123456789",
// {7a442600-4333-5126-6401-08ff132396f0}
(0x7a442600, 0x4333, 0x5126, 0x64, 0x01, 0x08, 0xff, 0x13, 0x23, 0x96, 0xf0),
TraceLoggingOptionGroupName("asdf"));
extern "C" {
bool TestC(void);
bool TestCpp(void);
} // extern "C"
extern char const* g_interceptorFileName;
int main(int argc, char* argv[])
{
if (argc > 1)
{
g_interceptorFileName = argv[1];
}
else
{
g_interceptorFileName = "EventHeaderInterceptor"
#if __BYTE_ORDER == __LITTLE_ENDIAN
"LE"
#elif __BYTE_ORDER == __BIG_ENDIAN
"BE"
#endif
#if __SIZEOF_POINTER__ == 8
"64"
#elif __SIZEOF_POINTER__ == 4
"32"
#endif
".dat"
;
}
bool allOk = true;
bool oneOk;
int result;
if (remove(g_interceptorFileName))
{
printf("Error %u clearing output file %s\n", errno, g_interceptorFileName);
allOk = false;
}
char str[EVENTHEADER_COMMAND_MAX];
result = EVENTHEADER_FORMAT_COMMAND(str, EVENTHEADER_COMMAND_MAX,
TraceLoggingProviderName(LongProvider), -1, -1, TraceLoggingProviderOptions(LongProvider));
printf("%d %s\n", result, str);
result = EVENTHEADER_FORMAT_TRACEPOINT_NAME(str, EVENTHEADER_NAME_MAX,
TraceLoggingProviderName(LongProvider), -1, -1, TraceLoggingProviderOptions(LongProvider));
printf("%d %s\n", result, str);
TraceLoggingRegister(LongProvider);
TraceLoggingWrite(LongProvider, "LongProviderEvent");
TraceLoggingUnregister(LongProvider);
oneOk = TestC();
printf("TestProvider: %s\n", oneOk ? "ok" : "ERROR");
allOk &= oneOk;
oneOk = TestCpp();
printf("TestProvider: %s\n", oneOk ? "ok" : "ERROR");
allOk &= oneOk;
printf("Events saved to \"%s\".\n", g_interceptorFileName);
return !allOk;
}

View file

@ -0,0 +1,94 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include <eventheader/TraceLoggingProvider.h>
#include <inttypes.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
TRACELOGGING_DEFINE_PROVIDER(
MyProvider,
"MyProviderName",
// {b7aa4d18-240c-5f41-5852-817dbf477472}
(0xb7aa4d18, 0x240c, 0x5f41, 0x58, 0x52, 0x81, 0x7d, 0xbf, 0x47, 0x74, 0x72));
TRACELOGGING_DEFINE_PROVIDER(
OtherProvider,
"OtherProviderName",
// {8ec53ac6-09b4-535e-5d19-e499de8832b4}
(0x8ec53ac6, 0x09b4, 0x535e, 0x5d, 0x19, 0xe4, 0x99, 0xde, 0x88, 0x32, 0xb4),
TraceLoggingOptionGroupName("mygroup"));
int
main(int argc, char** argv)
{
int err = 0;
printf("\n");
TraceLoggingRegister(MyProvider);
TraceLoggingRegister(OtherProvider);
for (unsigned iteration = 1;; iteration += 1)
{
event_level const event1_level = event_level_information;
uint64_t const event1_keyword = 0x1;
// For sample purposes, show whether Event1 is currently enabled.
// TraceLoggingProviderEnabled is usually unnecessary because every
// TraceLoggingWrite automatically checks its own enable state.
printf("MyProviderName_L4K1 Event1 status=%x\n",
TraceLoggingProviderEnabled(MyProvider, event1_level, event1_keyword));
// If Event1 is enabled then evaluate args, pack fields, write the event.
TraceLoggingWrite(
MyProvider, // Provider to use for the event.
"Event1", // Event name.
TraceLoggingLevel(event1_level), // Event severity level.
TraceLoggingKeyword(event1_keyword), // Event category bits.
TraceLoggingInt32(argc, "ArgC"), // Int32 field named "ArgC".
TraceLoggingStruct(2, "Structure"), // The following 2 fields are part of "Structure".
TraceLoggingValue(argc, "ArgCount"), // int field named "ArgCount".
TraceLoggingString(argv[0], "Arg0"), // char string field named "Arg0".
TraceLoggingUInt32(iteration)); // uint32 field named "iteration".
event_level const event2_level = event_level_verbose;
uint64_t const event2_keyword = 0x23;
// For sample purposes, show whether Event2 is currently enabled.
printf("OtherProviderName_L5K23Gmygroup Event2 status=%x\n",
TraceLoggingProviderEnabled(OtherProvider, event2_level, event2_keyword));
// If Event2 is enabled then evaluate args, pack fields, write the event.
TraceLoggingWrite(
OtherProvider,
"Event2",
TraceLoggingLevel(event2_level),
TraceLoggingKeyword(event2_keyword),
TraceLoggingUInt32(iteration),
TraceLoggingString(NULL),
TraceLoggingString(argv[0], "argv0"),
TraceLoggingStruct(1, "struct"),
TraceLoggingCountedString(argv[0], (uint16_t)strlen(argv[0]), "cargv0"),
TraceLoggingBinary(argv[0], 2, "bin", "desc"),
TraceLoggingCharArray(argv[0], 2, "vchar", "desc", 123),
TraceLoggingCharFixedArray(argv[0], 2, "cchar"));
printf("Press enter to refresh, x + enter to exit...\n");
char ch = (char)getchar();
if (ch == 'x' || ch == 'X')
{
break;
}
while (ch != '\n')
{
ch = (char)getchar();
}
}
TraceLoggingUnregister(MyProvider);
TraceLoggingUnregister(OtherProvider);
return err;
}

View file

@ -0,0 +1,270 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
/*
Implementation of the tracepoint.h interface that writes events to a file.
*/
#include <tracepoint/tracepoint.h>
#include <tracepoint/tracepoint-impl.h>
#include <eventheader/eventheader.h>
#include <assert.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <string>
#include <string_view>
#include <vector>
#include <shared_mutex>
static uint16_t const WriteIndexMax = 65535;
struct TracepointInfo
{
std::string Name;
unsigned* StatusPtr = nullptr;
};
static std::shared_mutex s_eventsMutex; // Also guards access to any tracepoint_provider_state
static std::vector<TracepointInfo> s_eventsByWriteIndex;
static int s_eventsFile = -1;
static unsigned s_eventsFileRefCount = 0;
static char const* const InterceptorFileNameDefault = "Interceptor.dat";
char const* g_interceptorFileName = InterceptorFileNameDefault;
void
tracepoint_close_provider(tracepoint_provider_state* providerState)
{
int fileToClose = -1;
// Scope for lock.
{
auto lock = std::lock_guard(s_eventsMutex);
if (providerState->data_file != -1)
{
assert(providerState->data_file > -1);
assert(s_eventsFileRefCount != 0);
s_eventsFileRefCount -= 1;
if (s_eventsFileRefCount == 0)
{
fileToClose = s_eventsFile;
s_eventsFile = -1;
s_eventsByWriteIndex.clear();
}
}
tracepoint_close_provider_impl(providerState);
}
if (fileToClose != -1)
{
close(fileToClose);
}
}
int
tracepoint_open_provider(tracepoint_provider_state* providerState)
{
int err;
auto lock = std::lock_guard(s_eventsMutex);
if (providerState->data_file != -1)
{
assert(providerState->data_file == -1); // PRECONDITION
abort(); // PRECONDITION
}
if (s_eventsFile == -1)
{
assert(s_eventsFileRefCount == 0);
s_eventsFile = open(g_interceptorFileName,
O_WRONLY | O_CLOEXEC | O_CREAT | O_APPEND,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (s_eventsFile == -1)
{
err = errno;
goto Done;
}
}
s_eventsFileRefCount += 1;
tracepoint_open_provider_impl(providerState, s_eventsFile);
err = 0;
Done:
return err;
}
int
tracepoint_connect(
tracepoint_state* eventState,
tracepoint_provider_state* providerState,
char const* eventNameArgs)
{
int err;
int writeIndex = -1;
auto lock = std::lock_guard(s_eventsMutex);
if (providerState == NULL)
{
auto const wi = static_cast<uint32_t>(__atomic_load_n(&eventState->write_index, __ATOMIC_RELAXED));
if (wi >= s_eventsByWriteIndex.size())
{
err = EINVAL;
}
else if (auto& e = s_eventsByWriteIndex[wi];
e.StatusPtr == nullptr)
{
err = EINVAL;
}
else
{
e.StatusPtr = nullptr;
e.Name.clear();
err = 0;
}
}
else try
{
// eventNameArgs = "EventName ArgList". We just want EventName.
auto const eventNameEnd = strchr(eventNameArgs, ' ');
if (eventNameEnd == nullptr)
{
err = EINVAL;
goto Done;
}
auto const eventName = std::string_view(eventNameArgs, eventNameEnd - eventNameArgs);
if (eventName.size() >= EVENTHEADER_NAME_MAX)
{
err = EINVAL;
goto Done;
}
if (s_eventsByWriteIndex.size() > WriteIndexMax)
{
err = E2BIG;
goto Done;
}
auto const pStatusWord = &eventState->status_word;
s_eventsByWriteIndex.push_back({ std::string(eventName), pStatusWord });
// In this sample, events are always enabled.
__atomic_store_n(pStatusWord, 1, __ATOMIC_RELAXED);
writeIndex = static_cast<int>(s_eventsByWriteIndex.size() - 1);
err = 0;
}
catch (std::bad_alloc const&)
{
err = ENOMEM;
}
catch (...)
{
err = EINVAL;
}
Done:
tracepoint_connect_impl(eventState, providerState, writeIndex);
return err;
}
int
tracepoint_open_provider_with_tracepoints(
tracepoint_provider_state* provider_state,
tracepoint_definition const** tp_definition_start,
tracepoint_definition const** tp_definition_stop)
{
return tracepoint_open_provider_with_tracepoints_impl(
provider_state,
tp_definition_start,
tp_definition_stop);
}
int
tracepoint_write(
tracepoint_state const* eventState,
unsigned dataCount,
struct iovec* dataVecs)
{
assert((int)dataCount >= 1);
assert(dataVecs[0].iov_len == 0);
if (!TRACEPOINT_ENABLED(eventState))
{
return EBADF;
}
size_t size = 0;
for (unsigned i = 1; i < dataCount; i += 1)
{
size += dataVecs[i].iov_len;
if (size < dataVecs[i].iov_len)
{
return E2BIG;
}
}
auto const providerState = __atomic_load_n(&eventState->provider_state, __ATOMIC_RELAXED);
if (providerState == NULL)
{
return EBADF;
}
auto lock = std::shared_lock(s_eventsMutex);
// Look up our tracking info for this event.
auto const writeIndex = static_cast<uint32_t>(__atomic_load_n(&eventState->write_index, __ATOMIC_RELAXED));
if (writeIndex >= s_eventsByWriteIndex.size())
{
return EINVAL;
}
auto const& tpi = s_eventsByWriteIndex[writeIndex];
if (tpi.StatusPtr == nullptr)
{
return EINVAL;
}
auto const headerSize = sizeof(uint32_t) + tpi.Name.size() + 1;
size += headerSize;
if (size < headerSize || size != (uint32_t)size)
{
return E2BIG;
}
struct
{
uint32_t LittleEndianRecordSize;
char TracepointName[EVENTHEADER_NAME_MAX];
} header;
header.LittleEndianRecordSize = htole32((uint32_t)size);
assert(tpi.Name.size() < sizeof(header.TracepointName)); // Was checked in tracepoint_connect.
memcpy(header.TracepointName, tpi.Name.c_str(), tpi.Name.size() + 1);
assert(s_eventsFile == providerState->data_file);
/*
On-disk record format:
uint32_t LittleEndianRecordSize;
char[] NulTerminatedTracepointName;
char[] EventData;
*/
dataVecs[0].iov_base = &header;
dataVecs[0].iov_len = headerSize;
return 0 <= writev(providerState->data_file, dataVecs, (int)dataCount)
? 0
: errno;
}

View file

@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include <eventheader/TraceLoggingProvider.h>
#include <stdio.h>
#include <errno.h>
#include <inttypes.h>
TRACELOGGING_DEFINE_PROVIDER(
LongProvider,
"Long_Provider_Name_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX0123456789",
// {7a442600-4333-5126-6401-08ff132396f0}
(0x7a442600, 0x4333, 0x5126, 0x64, 0x01, 0x08, 0xff, 0x13, 0x23, 0x96, 0xf0),
TraceLoggingOptionGroupName("asdf"));
extern "C" {
bool TestC(void);
bool TestCpp(void);
} // extern "C"
int main()
{
bool allOk = true;
bool oneOk;
int result;
char str[EVENTHEADER_COMMAND_MAX];
result = EVENTHEADER_FORMAT_COMMAND(str, EVENTHEADER_COMMAND_MAX,
TraceLoggingProviderName(LongProvider), -1, -1, TraceLoggingProviderOptions(LongProvider));
printf("%d %s\n", result, str);
result = EVENTHEADER_FORMAT_TRACEPOINT_NAME(str, EVENTHEADER_NAME_MAX,
TraceLoggingProviderName(LongProvider), -1, -1, TraceLoggingProviderOptions(LongProvider));
printf("%d %s\n", result, str);
TraceLoggingRegister(LongProvider);
TraceLoggingWrite(LongProvider, "LongProviderEvent");
TraceLoggingUnregister(LongProvider);
oneOk = TestC();
printf("TestProvider: %s\n", oneOk ? "ok" : "ERROR");
allOk &= oneOk;
oneOk = TestCpp();
printf("TestProvider: %s\n", oneOk ? "ok" : "ERROR");
allOk &= oneOk;
return !allOk;
}

View file

@ -0,0 +1,23 @@
# eventheader-tracepoint = libeventheader-tracepoint, EVENTHEADER_HEADERS
add_library(eventheader-tracepoint
eventheader-tracepoint.c)
target_link_libraries(eventheader-tracepoint
PUBLIC eventheader-headers tracepoint-headers)
install(TARGETS eventheader-tracepoint
EXPORT eventheader-tracepointTargets)
install(EXPORT eventheader-tracepointTargets
FILE "eventheader-tracepointTargets.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/eventheader-tracepoint")
configure_package_config_file(
"${CMAKE_CURRENT_SOURCE_DIR}/eventheader-tracepointConfig.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/eventheader-tracepointConfig.cmake"
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/eventheader-tracepoint"
NO_SET_AND_CHECK_MACRO
NO_CHECK_REQUIRED_COMPONENTS_MACRO)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/eventheader-tracepointConfigVersion.cmake"
COMPATIBILITY SameMinorVersion)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/eventheader-tracepointConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/eventheader-tracepointConfigVersion.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/eventheader-tracepoint")

View file

@ -0,0 +1,193 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include <eventheader/eventheader-tracepoint.h>
#include <tracepoint/tracepoint.h>
#include <tracepoint/tracepoint-impl.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#ifndef _uehp_FUNC_ATTRIBUTES
#define _uehp_FUNC_ATTRIBUTES //__attribute__((weak, visibility("hidden")))
#endif // _uehp_FUNC_ATTRIBUTES
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
int
eventheader_open_provider(
eventheader_provider const* pProvider) _uehp_FUNC_ATTRIBUTES;
int
eventheader_open_provider(
eventheader_provider const* pProvider)
{
assert(pProvider->state);
assert(pProvider->name);
assert(NULL == strchr(pProvider->name, ' '));
assert(NULL == strchr(pProvider->name, ':'));
if (pProvider->options != NULL)
{
assert(NULL == strchr(pProvider->options, ' '));
assert(NULL == strchr(pProvider->options, ':'));
assert(NULL == strchr(pProvider->options, '_'));
}
return tracepoint_open_provider(pProvider->state);
}
int
eventheader_open_provider_with_events(
eventheader_provider const* pProvider,
eventheader_tracepoint const** pEventsStart,
eventheader_tracepoint const** pEventsStop) _uehp_FUNC_ATTRIBUTES;
int
eventheader_open_provider_with_events(
eventheader_provider const* pProvider,
eventheader_tracepoint const** pEventsStart,
eventheader_tracepoint const** pEventsStop)
{
int err = eventheader_open_provider(pProvider);
if (err != 0)
{
return err;
}
eventheader_tracepoint const** adjustedEventPtrsStop =
tracepoint_fix_array((void const**)pEventsStart, (void const**)pEventsStop);
int const eventCount = (int)(adjustedEventPtrsStop - pEventsStart);
for (int i = 0; i < eventCount; i += 1)
{
eventheader_tracepoint const* const pEvent = pEventsStart[i];
assert(0 == __atomic_load_n(&pEvent->state->status_word, __ATOMIC_RELAXED));
assert(-1 == __atomic_load_n(&pEvent->state->write_index, __ATOMIC_RELAXED));
assert(NULL == __atomic_load_n(&pEvent->state->provider_state, __ATOMIC_RELAXED));
(void)eventheader_connect(pEvent, pProvider);
}
return 0;
}
void
eventheader_close_provider(
eventheader_provider const* pProvider) _uehp_FUNC_ATTRIBUTES;
void
eventheader_close_provider(
eventheader_provider const* pProvider)
{
tracepoint_close_provider(pProvider->state);
}
int
eventheader_connect(
eventheader_tracepoint const* pEvent,
eventheader_provider const* pProvider) _uehp_FUNC_ATTRIBUTES;
int
eventheader_connect(
eventheader_tracepoint const* pEvent,
eventheader_provider const* pProvider)
{
int err;
char command[EVENTHEADER_COMMAND_MAX];
if (pProvider == NULL)
{
err = tracepoint_connect(pEvent->state, NULL, NULL);
}
else if (EVENTHEADER_COMMAND_MAX <= (unsigned)EVENTHEADER_FORMAT_COMMAND(
command, sizeof(command),
pProvider->name, pEvent->header.level, pEvent->keyword, pProvider->options))
{
assert(!"Full name too long");
err = E2BIG;
}
else
{
err = tracepoint_connect(pEvent->state, pProvider->state, command);
}
return err;
}
int
eventheader_write(
eventheader_tracepoint const* pEvent,
void const* pActivityId,
void const* pRelatedActivityId,
uint32_t dataCount,
struct iovec* dataVecs) _uehp_FUNC_ATTRIBUTES;
int
eventheader_write(
eventheader_tracepoint const* pEvent,
void const* pActivityId,
void const* pRelatedActivityId,
uint32_t dataCount,
struct iovec* dataVecs)
{
uint8_t headers[0
+ sizeof(eventheader)
+ sizeof(eventheader_extension) + 32]; // ActivityId + RelatedActivityId
size_t iHeaders = 0;
eventheader* pHeader = (eventheader*)&headers[iHeaders];
iHeaders += sizeof(eventheader);
*pHeader = pEvent->header;
if (pActivityId == NULL)
{
assert(pRelatedActivityId == NULL);
}
else
{
pHeader->flags |= eventheader_flag_extension;
eventheader_extension* pExt = (eventheader_extension*)&headers[iHeaders];
iHeaders += sizeof(eventheader_extension);
pExt->kind = pEvent->metadata || (pEvent->header.flags & eventheader_flag_extension)
? (eventheader_extension_kind_activity_id | eventheader_extension_kind_chain_flag)
: (eventheader_extension_kind_activity_id);
pExt->size = 16;
memcpy(&headers[iHeaders], pActivityId, 16);
iHeaders += 16;
if (pRelatedActivityId != NULL)
{
pExt->size = 32;
memcpy(&headers[iHeaders], pRelatedActivityId, 16);
iHeaders += 16;
}
}
assert(iHeaders <= sizeof(headers));
assert(dataVecs != NULL);
assert((int)dataCount >= EVENTHEADER_PREFIX_DATAVEC_COUNT_NO_METADATA);
dataVecs[0].iov_len = 0;
dataVecs[1].iov_base = headers;
dataVecs[1].iov_len = iHeaders;
if (pEvent->metadata != NULL)
{
pHeader->flags |= eventheader_flag_extension;
assert((int)dataCount >= EVENTHEADER_PREFIX_DATAVEC_COUNT);
dataVecs[2].iov_base = (void*)pEvent->metadata;
dataVecs[2].iov_len = pEvent->metadata->size + sizeof(eventheader_extension);
}
return tracepoint_write(pEvent->state, dataCount, dataVecs);
}
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus

View file

@ -0,0 +1,2 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/eventheader-tracepointTargets.cmake")

View file

@ -0,0 +1,41 @@
cmake_minimum_required(VERSION 3.10)
include(../version.cmake)
project(tracepoint-control-cpp
VERSION ${LINUXTRACEPOINTS_VERSION}
DESCRIPTION "Linux tracepoint collection for C++"
HOMEPAGE_URL "https://github.com/microsoft/LinuxTracepoints"
LANGUAGES CXX)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
set(BUILD_SAMPLES ON CACHE BOOL "Build sample code")
set(BUILD_TOOLS ON CACHE BOOL "Build tool code")
if(NOT WIN32)
if(NOT TARGET tracepoint-decode)
find_package(tracepoint-decode ${TRACEPOINT_DECODE_MINVER} REQUIRED)
endif()
add_compile_options(
-Wall
-Wextra
-Wformat
-Wformat-security
-Werror=format-security
-Wstack-protector
-Werror=stack-protector)
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
add_compile_options(-D_FORTIFY_SOURCE=2)
endif()
add_subdirectory(src)
if(BUILD_SAMPLES)
add_subdirectory(samples)
endif()
if(BUILD_TOOLS)
add_subdirectory(tools)
endif()
endif()

View file

@ -0,0 +1,11 @@
# libtracepoint-control-cpp
- `TracepointSession.h` implements an event collection session that can
collect tracepoint events and enumerate the events that the session has
collected.
- `TracepointPath.h` has functions for finding the `/sys/kernel/tracing`
mount point and reading `format` files.
- `TracepointName.h` represents a tracepoint name (system name + event
name); for instance, `user_events/eventName`.
- `TracepointCache.h` implements a cache for tracking parsed `format` files
and locating cached data by `TracepointName` or by `common_type` id.

View file

@ -0,0 +1,241 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
/*
TracepointCache: class that loads, parses, and caches the metadata (format)
information for tracepoints.
The TracepointSession class uses TracepointCache to manage format information
for its tracepoints.
*/
#pragma once
#ifndef _included_TracepointCache_h
#define _included_TracepointCache_h 1
#include "TracepointName.h"
#include <tracepoint/PerfEventMetadata.h>
#include <unordered_map>
#include <string_view>
#include <memory>
#include <vector>
#ifndef _Success_
#define _Success_(condition)
#endif
#ifndef _In_z_
#define _In_z_
#endif
#ifndef _Out_
#define _Out_
#endif
namespace tracepoint_control
{
class TracepointSpec; // Forward declaration
/*
Loads, parses, and caches the metadata (format) information for tracepoints.
*/
class TracepointCache
{
public:
TracepointCache(TracepointCache const&) = delete;
void operator=(TracepointCache const&) = delete;
~TracepointCache();
/*
May throw std::bad_alloc.
*/
TracepointCache() noexcept(false);
/*
If no events are present in cache, returns -1.
Otherwise, returns the offset of the common_type field (usually 0).
*/
int8_t
CommonTypeOffset() const noexcept;
/*
If no events are present in cache, returns 0.
Otherwise, returns the size of the common_type field (1, 2, or 4; usually 2).
*/
uint8_t
CommonTypeSize() const noexcept;
/*
If metadata for an event with the specified ID is cached, return it.
Otherwise, return NULL. Note that ID is from the event's common_type field
and is not the PERF_SAMPLE_ID or PERF_SAMPLE_IDENTIFIER value.
*/
tracepoint_decode::PerfEventMetadata const*
FindById(uint32_t id) const noexcept;
/*
If metadata for an event with the specified name is cached, return it.
Otherwise, return NULL.
*/
tracepoint_decode::PerfEventMetadata const*
FindByName(TracepointName const& name) const noexcept;
/*
If metadata for an event with the specified data is cached,
return it. Otherwise, return NULL.
Implementation:
- Assume that rawData is host-endian.
- Use CommonTypeOffset() and CommonTypeSize() to extract the common_type
field value from the rawData.
- Use FindById() to find the matching metadata.
*/
tracepoint_decode::PerfEventMetadata const*
FindByRawData(std::string_view rawData) const noexcept;
/*
Parse the formatFileContents to get the metadata. If systemName or
formatFileContents is invalid, return EINVAL. If metadata for an
event with the same name or ID is already cached, return EEXIST.
Otherwise, add the metadata to the cache.
*/
_Success_(return == 0) int
AddFromFormat(
std::string_view systemName,
std::string_view formatFileContents,
bool longSize64 = sizeof(long) == 8) noexcept;
/*
Load and parse the "/sys/.../tracing/events/systemName/eventName/format"
file. If name or the format data is invalid, return EINVAL. If metadata
for an event with the same name or ID is already cached, return EEXIST.
Otherwise, add the metadata to the cache.
*/
_Success_(return == 0) int
AddFromSystem(TracepointName const& name) noexcept;
/*
If metadata for an event with the specified name is cached, return it.
Otherwise, return AddFromSystem(name).
*/
_Success_(return == 0) int
FindOrAddFromSystem(
TracepointName const& name,
_Out_ tracepoint_decode::PerfEventMetadata const** ppMetadata) noexcept;
/*
Given the name of a user_events EventHeader tracepoint, pre-register and
cache the specified event.
Example eventName: "user_events:MyProvider_L1Kff"
Details:
- If the specified name is not a valid user_events EventHeader name, return EINVAL.
- If metadata for "user_events:eventName" is already cached, return EEXIST.
- Try to register an EventHeader tracepoint with the given tracepoint name. If
this fails, return the error.
- Return AddFromSystem("user_events:eventName").
If this operation succeeds, the event will remain registered as long as this cache
object exists.
*/
_Success_(return == 0) int
PreregisterEventHeaderTracepoint(TracepointName const& name) noexcept;
/*
Given a tracepoint definition, pre-register and cache the specified event.
Details:
- If spec.Kind is not Definition or EventHeaderDefinition, return EINVAL.
- If spec.SystemName is not "user_events", return EINVAL.
- If spec.EventName, spec.Flags, or spec.Fields is invalid, return EINVAL.
- If metadata for "user_events:eventName" is already cached, return EEXIST.
- Try to register a tracepoint with the given tracepoint name, flags, and
fields. If this fails, return the error.
- Return AddFromSystem("user_events:eventName").
If this operation succeeds, the event will remain registered as long as this cache
object exists.
*/
_Success_(return == 0) int
PreregisterTracepointDefinition(TracepointSpec const& spec) noexcept;
/*
Given the registration command for a user_events tracepoint, pre-register and
cache the specified event.
Example registerCommand: "MyEventName __rel_loc u8[] MyField1; int MyField2"
Details:
- Parse the command to determine the eventName. If invalid, return EINVAL.
- If metadata for "user_events:eventName" is already cached, return EEXIST.
- Try to register a user_events tracepoint using the specified command string. If
this fails, return the error.
- Return AddFromSystem("user_events:eventName").
If this operation succeeds, the event will remain registered as long as this cache
object exists.
*/
_Success_(return == 0) int
PreregisterTracepoint(_In_z_ char const* registerCommand) noexcept;
private:
struct TracepointRegistration
{
int DataFile;
int WriteIndex;
unsigned StatusWord;
TracepointRegistration(TracepointRegistration const&) = delete;
void operator=(TracepointRegistration const&) = delete;
~TracepointRegistration();
TracepointRegistration() noexcept;
};
struct CacheVal
{
std::vector<char> SystemAndFormat; // = "SystemName\nFormatFileContents"
tracepoint_decode::PerfEventMetadata Metadata; // Points into SystemAndFormat
std::unique_ptr<TracepointRegistration> Registration;
CacheVal(CacheVal const&) = delete;
void operator=(CacheVal const&) = delete;
~CacheVal();
CacheVal(
std::vector<char>&& systemAndFormat,
tracepoint_decode::PerfEventMetadata&& metadata,
std::unique_ptr<TracepointRegistration> registration) noexcept;
};
struct NameHashOps
{
size_t operator()(TracepointName const&) const noexcept; // Hash
size_t operator()(TracepointName const&, TracepointName const&) const noexcept; // Equal
};
_Success_(return == 0) int
PreregisterTracepointImpl(_In_z_ char const* registerCommand, unsigned eventNameSize) noexcept;
/*
systemAndFormat = "SystemName\nFormatFileContents".
*/
_Success_(return == 0) int
Add(std::vector<char>&& systemAndFormat,
size_t systemNameSize,
bool longSize64,
std::unique_ptr<TracepointRegistration> registration) noexcept;
std::unordered_map<uint32_t, CacheVal> m_byId;
std::unordered_map<TracepointName, CacheVal const&, NameHashOps, NameHashOps> m_byName;
int8_t m_commonTypeOffset; // -1 = unset
uint8_t m_commonTypeSize; // 0 = unset
};
}
// namespace tracepoint_control
#endif // _included_TracepointCache_h

View file

@ -0,0 +1,259 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
/*
TracepointName is a view of a SystemName and an EventName.
*/
#pragma once
#ifndef _included_TracepointName_h
#define _included_TracepointName_h 1
#include <string_view>
namespace tracepoint_control
{
/*
The name of the "user_events" system.
*/
static constexpr std::string_view UserEventsSystemName = std::string_view("user_events", 11);
/*
Maximum length of a SystemName = 255. (Does not count nul-termination.)
*/
static constexpr unsigned SystemNameMaxSize = 255;
/*
Maximum length of an EventName = 255. (Does not count nul-termination.)
*/
static constexpr unsigned EventNameMaxSize = 255;
/*
Returns true if the specified string is a valid tracepoint system name.
At present, this returns true if:
- systemName is not empty.
- systemName.size() <= SystemNameMaxSize.
- systemName does not contain nul, space, slash, or colon.
*/
constexpr bool
SystemNameIsValid(std::string_view systemName) noexcept
{
return systemName.size() > 0
&& systemName.size() <= SystemNameMaxSize
&& systemName.find('\0') == std::string_view::npos
&& systemName.find(' ') == std::string_view::npos
&& systemName.find('/') == std::string_view::npos
&& systemName.find(':') == std::string_view::npos;
}
/*
Returns true if the specified string is a valid tracepoint event name.
At present, this returns true if:
- eventName is not empty.
- eventName.size() <= EventNameMaxSize.
- eventName does not contain nul, space, slash, or colon.
*/
constexpr bool
EventNameIsValid(std::string_view eventName) noexcept
{
return eventName.size() > 0
&& eventName.size() <= EventNameMaxSize
&& eventName.find('\0') == std::string_view::npos
&& eventName.find(' ') == std::string_view::npos
&& eventName.find('/') == std::string_view::npos
&& eventName.find(':') == std::string_view::npos;
}
/*
Returns true if the specified string is a valid EventHeader tracepoint name,
e.g. "MyComponent_MyProvider_L1K2e" or "MyComponent_MyProv_L5K1Gmyprovider".
A valid EventHeader tracepoint name is a valid tracepoint event name that ends
with a "_LxKx..." suffix, where "x" is 1 or more lowercase hex digits and "..."
is 0 or more ASCII letters or digits.
*/
static constexpr bool
EventHeaderEventNameIsValid(std::string_view eventName) noexcept
{
auto const eventNameSize = eventName.size();
if (eventNameSize < 5 || // 5 = "_L1K1".size()
!EventNameIsValid(eventName))
{
return false;
}
auto i = eventName.rfind('_');
if (i > eventNameSize - 5 || // 5 = "_L1K1".size()
eventName[i + 1] != 'L')
{
// Does not end with "_L...".
return false;
}
i += 2; // Skip "_L".
// Skip level value (lowercase hex digits).
auto const levelStart = i;
for (; i != eventNameSize; i += 1)
{
auto const ch = eventName[i];
if ((ch < '0' || '9' < ch) && (ch < 'a' || 'f' < ch))
{
break;
}
}
if (levelStart == i)
{
// Does not end with "_Lx...".
return false;
}
if (i == eventNameSize || eventName[i] != 'K')
{
// Does not end with "_LxK...".
return false;
}
i += 1; // Skip "K"
// Skip keyword value (lowercase hex digits).
auto const keywordStart = i;
for (; i != eventNameSize; i += 1)
{
auto const ch = eventName[i];
if ((ch < '0' || '9' < ch) && (ch < 'a' || 'f' < ch))
{
break;
}
}
if (keywordStart == i)
{
// Does not end with "_LxKx...".
return false;
}
// If there are attributes, validate them.
if (i != eventNameSize)
{
if (eventName[i] < 'A' || 'Z' < eventName[i])
{
// Invalid attribute lead char.
return false;
}
// Skip attributes and their values.
for (; i != eventNameSize; i += 1)
{
auto const ch = eventName[i];
if ((ch < '0' || '9' < ch) &&
(ch < 'A' || 'Z' < ch) &&
(ch < 'a' || 'z' < ch))
{
// Invalid attribute character.
return false;
}
}
}
return true;
}
/*
A TracepointName is a string identifier for a tracepoint on a system.
It contains two parts: SystemName and EventName.
Construct a TracepointName by one of the following:
- TracepointName("SystemName", "EventName")
- TracepointName("SystemName:EventName")
- TracepointName("SystemName/EventName")
- TracepointName("EventName") // Uses SystemName = "user_events"
*/
struct TracepointName
{
/*
SystemName is the name of a subdirectory of
"/sys/kernel/tracing/events" such as "user_events" or "ftrace".
*/
std::string_view SystemName;
/*
EventName is the name of a subdirectory of
"/sys/kernel/tracing/events/SystemName" such as "MyEvent" or "function".
*/
std::string_view EventName;
/*
Create a TracepointName from systemName and eventName, e.g.
TracepointName("user_events", "MyEvent_L1K1").
- systemName is the name of a subdirectory of
"/sys/kernel/tracing/events" such as "user_events" or "ftrace".
- eventName is the name of a subdirectory of
"/sys/kernel/tracing/events/systemName", e.g. "MyEvent" or
"function".
*/
constexpr
TracepointName(std::string_view systemName, std::string_view eventName) noexcept
: SystemName(systemName)
, EventName(eventName)
{
return;
}
/*
Create a TracepointName from a combined "systemName:eventName" or
"systemName/eventName" string. If the string does not contain ':' or '/',
the SystemName is assumed to be "user_events".
*/
explicit constexpr
TracepointName(std::string_view systemAndEventName) noexcept
: SystemName()
, EventName()
{
auto const splitPos = systemAndEventName.find_first_of(":/", 0, 2);
if (splitPos == systemAndEventName.npos)
{
SystemName = UserEventsSystemName;
EventName = systemAndEventName;
}
else
{
SystemName = systemAndEventName.substr(0, splitPos);
EventName = systemAndEventName.substr(splitPos + 1);
}
}
/*
Require SystemName and EventName to always be specified.
*/
TracepointName() = delete;
/*
Returns true if SystemName is a valid tracepoint system name and EventName
is a valid tracepoint event name.
*/
constexpr bool
IsValid() const noexcept
{
return SystemNameIsValid(SystemName) && EventNameIsValid(EventName);
}
/*
Returns true if SystemName is a valid tracepoint system name and EventName
is a valid EventHeader tracepoint event name.
*/
constexpr bool
IsValidEventHeader() const noexcept
{
return SystemNameIsValid(SystemName) && EventHeaderEventNameIsValid(EventName);
}
};
}
// namespace tracepoint_control
#endif // _included_TracepointName_h

View file

@ -0,0 +1,91 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
/*
Helpers for locating the "/sys/.../tracing" directory and loading "format"
files from it.
The TracepointCache class uses these functions to locate and load format
information.
*/
#pragma once
#ifndef _included_TracepointPath_h
#define _included_TracepointPath_h 1
#include <string_view>
#include <vector>
#ifndef _In_z_
#define _In_z_
#endif
#ifndef _Ret_z_
#define _Ret_z_
#endif
#ifndef _Success_
#define _Success_(condition)
#endif
namespace tracepoint_control
{
/*
Returns the path to the "/sys/.../tracing" directory, usually either
"/sys/kernel/tracing" or "/sys/kernel/debug/tracing".
Returns "" if no tracing directory could be found (e.g. tracefs not mounted).
Implementation: The first time this is called, it checks for the existence
of "/sys/kernel/tracing/events" and if that is a directory, uses
"/sys/kernel/tracing"; otherwise, it parses "/proc/mounts" to find the
tracefs or debugfs mount point and uses the corresponding ".../tracing"
directory if a mount point is listed; otherwise, returns "".
Subsequent calls return the cached result. This function is thread-safe.
*/
_Ret_z_ char const*
GetTracingDirectory() noexcept;
/*
Returns a file descriptor for the user_events_data file. Result will be
non-negative on success or negative (-errno) on error.
Do not close the returned descriptor. Use it only for ioctl and writev.
Implementation: The first time this is called, it calls GetTracingDirectory()
to find the tracefs or debugfs mount point, then opens
"TracingDirectory/user_events_data" and caches the result. Subsequent calls
return the cached result. This function is thread-safe.
*/
_Success_(return >= 0) int
GetUserEventsDataFile() noexcept;
/*
Given full path to a file, appends the file's contents to the specified
dest string.
Returns 0 for success, errno for error.
*/
_Success_(return == 0) int
AppendTracingFile(
std::vector<char>& dest,
_In_z_ char const* fileName) noexcept;
/*
Given systemName and eventName, appends the corresponding event's format
data to the specified dest string (i.e. appends the contents of format file
"$(tracingDirectory)/events/$(systemName)/$(eventName)/format").
For example, AppendTracingFormatFile("user_events", "MyEventName", format) would
append the contents of "/sys/.../tracing/events/user_events/MyEventName/format"
(using the "/sys/.../tracing" directory as returned by GetTracingDirectory()).
Returns 0 for success, errno for error.
*/
_Success_(return == 0) int
AppendTracingFormatFile(
std::vector<char>& dest,
std::string_view systemName,
std::string_view eventName) noexcept;
}
// namespace tracepoint_control
#endif // _included_TracepointPath_h

View file

@ -0,0 +1,106 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
/*
A TracepointSpec stores a view of the information needed to add a tracepoint to
a trace collection session. It may or may not have the information needed to
pre-register the tracepoint.
*/
#pragma once
#ifndef _included_TracepointSpec_h
#define _included_TracepointSpec_h 1
#include <string_view>
namespace tracepoint_control
{
/*
Value indicating whether the TracepointSpec is empty, an identifier, a
definition, an EventHeader definition, or an error.
*/
enum class TracepointSpecKind : unsigned char
{
Empty, // Empty spec, all whitespace, or started with "#" (comment).
Identifier, // Name only, event cannot be pre-registered.
Definition, // Name plus field information, event can be pre-registered.
EventHeaderDefinition, // EventHeader name, event can be pre-registered.
ErrorIdentifierCannotHaveFields,
ErrorIdentifierCannotHaveFlags,
ErrorDefinitionCannotHaveColonAfterFlags,
ErrorIdentifierEventNameEmpty,
ErrorDefinitionEventNameEmpty,
ErrorIdentifierEventNameInvalid,
ErrorDefinitionEventNameInvalid,
ErrorEventHeaderDefinitionEventNameInvalid,
ErrorIdentifierSystemNameEmpty,
ErrorDefinitionSystemNameEmpty, // Unreachable via specString.
ErrorIdentifierSystemNameInvalid,
ErrorDefinitionSystemNameInvalid,
};
/*
A TracepointSpec stores the information needed to add a tracepoint to a
trace collection session.
The TracepointSpec is either a tracepoint identifier (name only, not enough
information to pre-register the tracepoint) or a tracepoint definition
(enough information to pre-register the tracepoint if not already
registered). A tracepoint definition can be either a normal tracepoint
definition (explicitly-specified fields) or an EventHeader tracepoint
definition (implicit well-known fields).
*/
struct TracepointSpec
{
std::string_view Trimmed = {}; // Input with leading/trailing whitespace removed = Trim(specString).
std::string_view SystemName = {}; // e.g. "user_events".
std::string_view EventName = {}; // e.g. "MyEvent" or "MyProvider_L2K1Gmygroup".
std::string_view Flags = {}; // e.g. "" or "flag1,flag2".
std::string_view Fields = {}; // e.g. "" or "u32 Field1; u16 Field2".
TracepointSpecKind Kind = {}; // Empty, Identifier, Definition, EventHeaderDefinition, or Error.
/*
Initializes an empty TracepointSpec.
*/
constexpr
TracepointSpec() noexcept = default;
/*
Initializes a TracepointSpec from the specified string.
Leading and trailing whitespace is always ignored.
If SystemName is not specified, "user_events" is assumed.
Accepted inputs:
* Empty string, or string starting with "#" --> Kind = Empty.
* Leading colon --> Kind = Identifier.
Examples: ":EventName", ":SystemName:EventName".
* No leading colon, fields present --> Kind = Definition.
Use a trailing space + semicolon to indicate no fields. The space is
required because semicolons are valid in event names.
Examples: "EventName ;", "SystemName:EventName:Flags Field1; Field2".
* No leading colon, no fields present --> Kind = EventHeaderDefinition.
Examples: "ProviderName_L1K1", or "SystemName:ProviderName_L1KffGgroup:Flags".
*/
explicit
TracepointSpec(std::string_view const specString) noexcept;
/*
Returns specString with all leading and trailing whitespace removed.
Uses the same definition of whitespace as the TracepointSpec constructor.
*/
static std::string_view
Trim(std::string_view const specString) noexcept;
};
}
// namespace tracepoint_control
#endif // _included_TracepointSpec_h

View file

@ -0,0 +1,20 @@
add_executable(control-lookup
control-lookup.cpp)
target_link_libraries(control-lookup
PUBLIC tracepoint-control)
target_compile_features(control-lookup
PRIVATE cxx_std_17)
add_executable(control-session
control-session.cpp)
target_link_libraries(control-session
PUBLIC tracepoint-control tracepoint-decode)
target_compile_features(control-session
PRIVATE cxx_std_17)
add_executable(save-session
save-session.cpp)
target_link_libraries(save-session
PUBLIC tracepoint-control tracepoint-decode)
target_compile_features(save-session
PRIVATE cxx_std_17)

View file

@ -0,0 +1,53 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
/*
Demonstrates use of TracepointCache to look up tracefs metadata.
*/
#include <tracepoint/TracepointCache.h>
#include <stdio.h>
using namespace std::string_view_literals;
using namespace tracepoint_control;
int
main(int argc, char* argv[])
{
TracepointCache cache;
for (int argi = 1; argi < argc; argi += 1)
{
int error;
auto const name = TracepointName(argv[argi]);
error = cache.AddFromSystem(name);
fprintf(stdout, "AddFromSystem(%.*s:%.*s)=%u\n",
(unsigned)name.SystemName.size(), name.SystemName.data(),
(unsigned)name.EventName.size(), name.EventName.data(),
error);
unsigned id = 0;
if (auto const meta = cache.FindByName(name); meta)
{
fprintf(stdout, "- FindByName=%u\n", meta->Id());
fprintf(stdout, " Sys = %.*s\n", (unsigned)meta->SystemName().size(), meta->SystemName().data());
fprintf(stdout, " Name= %.*s\n", (unsigned)meta->Name().size(), meta->Name().data());
fprintf(stdout, " Fmt = %.*s\n", (unsigned)meta->PrintFmt().size(), meta->PrintFmt().data());
fprintf(stdout, " Flds= %u\n", (unsigned)meta->Fields().size());
fprintf(stdout, " Id = %u\n", (unsigned)meta->Id());
fprintf(stdout, " CmnC= %u\n", (unsigned)meta->CommonFieldCount());
fprintf(stdout, " Kind= %u\n", (unsigned)meta->Kind());
id = meta->Id();
}
if (auto const meta = cache.FindById(id); meta)
{
fprintf(stdout, "- FindById(%u)=%u\n", id, meta->Id());
}
}
fprintf(stdout, "CommonTypeOffset=%d\n", cache.CommonTypeOffset());
fprintf(stdout, "CommonTypeSize =%u\n", cache.CommonTypeSize());
return 0;
}

View file

@ -0,0 +1,135 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include <tracepoint/TracepointSession.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
#include <tracepoint/PerfEventAbi.h>
using namespace std::string_view_literals;
using namespace tracepoint_control;
using namespace tracepoint_decode;
int
main(int argc, char* argv[])
{
int error = 0;
if (argc < 2 ||
(0 != strcmp(argv[1], "0") && 0 != strcmp(argv[1], "1")))
{
fprintf(stderr,
"Usage: control-session [0|1] systemName:eventName ...\n"
"0 = circular, 1 = realtime\n");
return 1;
}
auto const mode = 0 == strcmp(argv[1], "0")
? TracepointSessionMode::Circular
: TracepointSessionMode::RealTime;
TracepointCache cache;
TracepointSession session(
cache,
TracepointSessionOptions(mode, 0) // 0 should round up to a 1-page buffer.
.WakeupWatermark(100)); // WaitForWakeup waits for a buffer to have >= 100 bytes of data.
fprintf(stderr, "Session: BC=%u BS=%x RT=%u MODE=%u\n",
session.BufferCount(), session.BufferSize(), session.IsRealtime(), (unsigned)session.Mode());
fprintf(stderr, "\n");
for (int argi = 2; argi < argc; argi += 1)
{
TracepointName name(argv[argi]);
error = cache.AddFromSystem(name);
if (error != ENOENT || name.SystemName != UserEventsSystemName ||
!name.IsValidEventHeader())
{
fprintf(stderr, "AddFromSystem(%s) = %u\n", argv[argi], error);
}
else
{
// User-specified EventHeader event is not registered.
// Pre-register it and try to collect it anyway.
error = cache.PreregisterEventHeaderTracepoint(name);
fprintf(stderr, "PreregisterEventHeaderTracepoint(%s) = %u\n", argv[argi], error);
}
}
fprintf(stderr, "\n");
unsigned enabled = 0;
for (int argi = 2; argi < argc; argi += 1)
{
error = session.EnableTracepoint(TracepointName(argv[argi]));
fprintf(stderr, "EnableTracepoint(%s) = %u\n", argv[argi], error);
enabled += error == 0;
}
if (enabled == 0)
{
return error;
}
for (;;)
{
fprintf(stderr, "\n");
if (mode == TracepointSessionMode::Circular)
{
sleep(5);
}
else
{
int activeCount;
error = session.WaitForWakeup(nullptr, nullptr, &activeCount);
fprintf(stderr, "WaitForWakeup() = %u, active = %d\n", error, activeCount);
if (error != 0)
{
sleep(5);
}
}
error = session.EnumerateSampleEventsUnordered(
[](PerfSampleEventInfo const& event) -> int
{
auto ts = event.session_info->TimeToRealTime(event.time);
time_t const secs = (time_t)ts.tv_sec;
tm t = {};
gmtime_r(&secs, &t);
fprintf(stdout, "CPU%u: tid=%x time=%04u-%02u-%02uT%02u:%02u:%02u.%09uZ raw=0x%lx n=%s\n",
event.cpu,
event.tid,
1900 + t.tm_year, 1 + t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, ts.tv_nsec,
(long unsigned)event.raw_data_size,
event.Name());
return 0;
});
fprintf(stderr, "Enum: %u, Count=%llu, Lost=%llu, Bad=%llu, BadBuf=%llu\n",
error,
(long long unsigned)session.SampleEventCount(),
(long long unsigned)session.LostEventCount(),
(long long unsigned)session.CorruptEventCount(),
(long long unsigned)session.CorruptBufferCount());
for (auto& info : session.TracepointInfos())
{
auto& metadata = info.Metadata();
auto name = metadata.Name();
uint64_t count = 0;
error = info.GetEventCount(&count);
fprintf(stderr, " %.*s EnableState=%u Count=%llu Err=%u\n",
(int)name.size(), name.data(),
(int)info.EnableState(),
(long long unsigned)count,
error);
}
}
return 0;
}

View file

@ -0,0 +1,107 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
/*
Demonstrates the use of SavePerfDataFile to save session data to a perf file.
*/
#include <tracepoint/TracepointSession.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
using namespace std::string_view_literals;
using namespace tracepoint_control;
using namespace tracepoint_decode;
int
main(int argc, char* argv[])
{
int error = 0;
TracepointTimestampRange writtenRange;
if (argc < 3 ||
(0 != strcmp(argv[1], "0") && 0 != strcmp(argv[1], "1")))
{
fprintf(stderr,
"Usage: save-session [0|1] outFileName systemName:eventName ...\n"
"0 = circular, 1 = realtime\n");
return 1;
}
auto const mode = 0 == strcmp(argv[1], "0")
? TracepointSessionMode::Circular
: TracepointSessionMode::RealTime;
TracepointCache cache;
TracepointSession session(cache, mode, 0); // 0 should round up to a 1-page buffer.
fprintf(stderr, "Session: BC=%u BS=%x RT=%u MODE=%u\n",
session.BufferCount(), session.BufferSize(), session.IsRealtime(), (unsigned)session.Mode());
fprintf(stderr, "\n");
for (int argi = 3; argi < argc; argi += 1)
{
TracepointName name(argv[argi]);
error = cache.AddFromSystem(name);
if (error != ENOENT || name.SystemName != UserEventsSystemName ||
!name.IsValidEventHeader())
{
fprintf(stderr, "AddFromSystem(%s) = %u\n", argv[argi], error);
}
else
{
// User-specified EventHeader event is not registered.
// Pre-register it and try to collect it anyway.
error = cache.PreregisterEventHeaderTracepoint(name);
fprintf(stderr, "PreregisterEventHeaderTracepoint(%s) = %u\n", argv[argi], error);
}
}
fprintf(stderr, "\n");
unsigned enabled = 0;
for (int argi = 3; argi < argc; argi += 1)
{
error = session.EnableTracepoint(TracepointName(argv[argi]));
fprintf(stderr, "EnableTracepoint(%s) = %u\n", argv[argi], error);
enabled += error == 0;
}
if (enabled == 0)
{
return error;
}
for (unsigned i = 0;; i += 1)
{
printf("\nPress enter to iterate, x + enter to exit...\n");
char ch = (char)getchar();
if (ch == 'x' || ch == 'X')
{
break;
}
while (ch != '\n')
{
ch = (char)getchar();
}
char outFileName[256];
snprintf(outFileName, sizeof(outFileName), "%s.%u", argv[2], i);
// CodeQL [SM01937] This is a sample/tool. Using externally-supplied path is intended behavior.
error = session.SavePerfDataFile(
outFileName,
TracepointSavePerfDataFileOptions()
.TimestampFilter(writtenRange.Last) // For circular, filter out old events.
.TimestampWrittenRange(&writtenRange));
printf("SavePerfDataFile(%s) = %u\n", outFileName, error);
}
return 0;
}

View file

@ -0,0 +1,42 @@
# tracepoint-control = libtracepoint-control, CONTROL_HEADERS
add_library(tracepoint-control
"TracepointCache.cpp"
"TracepointPath.cpp"
"TracepointSession.cpp"
"TracepointSpec.cpp"
"UniqueHandles.cpp")
target_include_directories(tracepoint-control
PUBLIC
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include/>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
target_link_libraries(tracepoint-control
PUBLIC tracepoint-decode atomic)
set(CONTROL_HEADERS
"${PROJECT_SOURCE_DIR}/include/tracepoint/TracepointCache.h"
"${PROJECT_SOURCE_DIR}/include/tracepoint/TracepointName.h"
"${PROJECT_SOURCE_DIR}/include/tracepoint/TracepointPath.h"
"${PROJECT_SOURCE_DIR}/include/tracepoint/TracepointSession.h"
"${PROJECT_SOURCE_DIR}/include/tracepoint/TracepointSpec.h")
set_target_properties(tracepoint-control PROPERTIES
PUBLIC_HEADER "${CONTROL_HEADERS}")
target_compile_features(tracepoint-control
PRIVATE cxx_std_17)
install(TARGETS tracepoint-control
EXPORT tracepoint-controlTargets
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/tracepoint)
install(EXPORT tracepoint-controlTargets
FILE "tracepoint-controlTargets.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/tracepoint-control")
configure_package_config_file(
"${CMAKE_CURRENT_SOURCE_DIR}/tracepoint-controlConfig.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/tracepoint-controlConfig.cmake"
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/tracepoint-control"
NO_SET_AND_CHECK_MACRO
NO_CHECK_REQUIRED_COMPONENTS_MACRO)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/tracepoint-controlConfigVersion.cmake"
COMPATIBILITY SameMinorVersion)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/tracepoint-controlConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/tracepoint-controlConfigVersion.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/tracepoint-control")

View file

@ -0,0 +1,606 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include <tracepoint/TracepointCache.h>
#include <tracepoint/TracepointSpec.h>
#include <tracepoint/TracepointPath.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <linux/types.h>
#include <sys/ioctl.h>
//#include <linux/user_events.h>
struct user_reg63 {
/* Input: Size of the user_reg structure being used */
__u32 size;
/* Input: Bit in enable address to use */
__u8 enable_bit;
/* Input: Enable size in bytes at address */
__u8 enable_size;
/* Input: Flags for future use, set to 0 */
__u16 flags;
/* Input: Address to update when enabled */
__u64 enable_addr;
/* Input: Pointer to string with event name, description and flags */
__u64 name_args;
/* Output: Index of the event to use when writing data */
__u32 write_index;
} __attribute__((__packed__));
/*
* Describes an event unregister, callers must set the size, address and bit.
* This structure is passed to the DIAG_IOCSUNREG ioctl to disable bit updates.
*/
struct user_unreg63 {
/* Input: Size of the user_unreg structure being used */
__u32 size;
/* Input: Bit to unregister */
__u8 disable_bit;
/* Input: Reserved, set to 0 */
__u8 __reserved;
/* Input: Reserved, set to 0 */
__u16 __reserved2;
/* Input: Address to unregister */
__u64 disable_addr;
} __attribute__((__packed__));
#define DIAG_IOC_MAGIC '*'
#define DIAG_IOCSREG _IOWR(DIAG_IOC_MAGIC, 0, struct user_reg63*)
#define DIAG_IOCSDEL _IOW(DIAG_IOC_MAGIC, 1, char*)
#define DIAG_IOCSUNREG _IOW(DIAG_IOC_MAGIC, 2, struct user_unreg63*)
//#include <eventheader.h>
#define EVENTHEADER_COMMAND_TYPES "u8 eventheader_flags; u8 version; u16 id; u16 tag; u8 opcode; u8 level"
enum {
// Maximum length of a Tracepoint name "ProviderName_Attributes", including nul termination.
EVENTHEADER_NAME_MAX = 256,
// Maximum length needed for a DIAG_IOCSREG command "ProviderName_Attributes CommandTypes".
EVENTHEADER_COMMAND_MAX = EVENTHEADER_NAME_MAX + sizeof(EVENTHEADER_COMMAND_TYPES)
};
using namespace std::string_view_literals;
using namespace tracepoint_control;
using namespace tracepoint_decode;
static constexpr int8_t CommonTypeOffsetInit = -1;
static constexpr uint8_t CommonTypeSizeInit = 0;
TracepointCache::TracepointRegistration::~TracepointRegistration()
{
if (WriteIndex >= 0)
{
user_unreg63 unreg = {};
unreg.size = sizeof(user_unreg63);
unreg.disable_bit = 0;
unreg.disable_addr = (uintptr_t)&StatusWord;
ioctl(DataFile, DIAG_IOCSUNREG, &unreg);
}
}
TracepointCache::TracepointRegistration::TracepointRegistration() noexcept
: DataFile(-1)
, WriteIndex(-1)
, StatusWord(0)
{
return;
}
TracepointCache::CacheVal::~CacheVal()
{
return;
}
TracepointCache::CacheVal::CacheVal(
std::vector<char>&& systemAndFormat,
PerfEventMetadata&& metadata,
std::unique_ptr<TracepointRegistration> registration) noexcept
: SystemAndFormat(std::move(systemAndFormat))
, Metadata(metadata)
, Registration(std::move(registration))
{
return;
}
size_t
TracepointCache::NameHashOps::operator()(
TracepointName const& a) const noexcept
{
std::hash<std::string_view> const hasher;
return hasher(a.EventName) ^ hasher(a.SystemName);
}
size_t
TracepointCache::NameHashOps::operator()(
TracepointName const& a,
TracepointName const& b) const noexcept
{
return a.EventName == b.EventName && a.SystemName == b.SystemName;
}
TracepointCache::~TracepointCache() noexcept
{
return;
}
TracepointCache::TracepointCache() noexcept(false)
: m_byId() // may throw bad_alloc (but probably doesn't).
, m_byName() // may throw bad_alloc (but probably doesn't).
, m_commonTypeOffset(CommonTypeOffsetInit)
, m_commonTypeSize(CommonTypeSizeInit)
{
return;
}
int8_t
TracepointCache::CommonTypeOffset() const noexcept
{
return m_commonTypeOffset;
}
uint8_t
TracepointCache::CommonTypeSize() const noexcept
{
return m_commonTypeSize;
}
PerfEventMetadata const*
TracepointCache::FindById(uint32_t id) const noexcept
{
auto it = m_byId.find(id);
return it == m_byId.end()
? nullptr
: &it->second.Metadata;
}
PerfEventMetadata const*
TracepointCache::FindByName(TracepointName const& name) const noexcept
{
auto it = m_byName.find(name);
return it == m_byName.end()
? nullptr
: &it->second.Metadata;
}
PerfEventMetadata const*
TracepointCache::FindByRawData(std::string_view rawData) const noexcept
{
PerfEventMetadata const* metadata;
auto const offset = static_cast<size_t>(m_commonTypeOffset);
auto const commonTypeSize = m_commonTypeSize;
auto const rawDataSize = rawData.size();
if (rawDataSize <= offset ||
rawDataSize - offset <= commonTypeSize)
{
metadata = nullptr;
}
else if (commonTypeSize == sizeof(uint16_t))
{
uint16_t commonType;
memcpy(&commonType, rawData.data() + offset, sizeof(commonType));
metadata = FindById(commonType);
}
else if (commonTypeSize == sizeof(uint32_t))
{
uint32_t commonType;
memcpy(&commonType, rawData.data() + offset, sizeof(commonType));
metadata = FindById(commonType);
}
else
{
assert(commonTypeSize == 1);
uint8_t commonType;
memcpy(&commonType, rawData.data() + offset, sizeof(commonType));
metadata = FindById(commonType);
}
return metadata;
}
_Success_(return == 0) int
TracepointCache::AddFromFormat(
std::string_view systemName,
std::string_view formatFileContents,
bool longSize64) noexcept
{
int error;
try
{
std::vector<char> systemAndFormat;
systemAndFormat.reserve(systemName.size() + 1 + formatFileContents.size()); // may throw
systemAndFormat.assign(systemName.begin(), systemName.end());
systemAndFormat.push_back('\n'); // For readability when debugging.
systemAndFormat.insert(systemAndFormat.end(), formatFileContents.begin(), formatFileContents.end());
error = Add(std::move(systemAndFormat), systemName.size(), longSize64, nullptr);
}
catch (...)
{
error = ENOMEM;
}
return error;
}
_Success_(return == 0) int
TracepointCache::AddFromSystem(TracepointName const& name) noexcept
{
int error;
if (!name.IsValid())
{
error = EINVAL;
}
else try
{
std::vector<char> systemAndFormat;
systemAndFormat.reserve(name.SystemName.size() + 512); // may throw
systemAndFormat.assign(name.SystemName.begin(), name.SystemName.end());
systemAndFormat.push_back('\n'); // For readability when debugging.
error = AppendTracingFormatFile(systemAndFormat, name.SystemName, name.EventName);
if (error == 0)
{
error = Add(std::move(systemAndFormat), name.SystemName.size(), sizeof(long) == 8, nullptr);
}
}
catch (...)
{
error = ENOMEM;
}
return error;
}
_Success_(return == 0) int
TracepointCache::FindOrAddFromSystem(
TracepointName const& name,
_Out_ PerfEventMetadata const** ppMetadata) noexcept
{
int error;
PerfEventMetadata const* metadata;
auto it = m_byName.find(name);
if (it != m_byName.end())
{
error = 0;
metadata = &it->second.Metadata;
}
else
{
error = AddFromSystem(name);
metadata = error ? nullptr : FindByName(name);
}
*ppMetadata = metadata;
return error;
}
_Success_(return == 0) int
TracepointCache::PreregisterEventHeaderTracepoint(TracepointName const& name) noexcept
{
if (name.SystemName != UserEventsSystemName ||
!EventHeaderEventNameIsValid(name.EventName))
{
return EINVAL;
}
auto const eventNameSize = (unsigned)name.EventName.size();
char command[EVENTHEADER_COMMAND_MAX];
snprintf(command, sizeof(command), "%.*s %s",
eventNameSize, name.EventName.data(),
EVENTHEADER_COMMAND_TYPES);
auto error = PreregisterTracepointImpl(command, eventNameSize);
return error;
}
_Success_(return == 0) int
TracepointCache::PreregisterTracepointDefinition(TracepointSpec const& spec) noexcept
{
int error;
if (spec.SystemName != UserEventsSystemName ||
spec.Flags.size() > 65536 ||
spec.Fields.size() > 65536)
{
error = EINVAL;
}
else try
{
auto const eventNameSize = (unsigned)spec.EventName.size();
char commandStack[EVENTHEADER_COMMAND_MAX];
std::vector<char> commandHeap;
char* command;
if (spec.Kind == TracepointSpecKind::Definition)
{
if (!EventNameIsValid(spec.EventName))
{
error = EINVAL;
goto Done;
}
size_t const commandSize =
eventNameSize
+ 1 + spec.Flags.size()
+ 1 + spec.Fields.size()
+ 1;
if (commandSize <= sizeof(commandStack))
{
command = commandStack;
}
else
{
commandHeap.reserve(commandSize); // may throw
command = commandHeap.data();
}
snprintf(command, commandSize, "%.*s%s%.*s%s%.*s",
eventNameSize, spec.EventName.data(),
spec.Flags.empty() ? "" : ":",
(unsigned)spec.Flags.size(), spec.Flags.data(),
spec.Fields.empty() ? "" : " ",
(unsigned)spec.Fields.size(), spec.Fields.data());
}
else if (spec.Kind == TracepointSpecKind::EventHeaderDefinition)
{
if (!EventHeaderEventNameIsValid(spec.EventName))
{
error = EINVAL;
goto Done;
}
size_t const commandSize =
eventNameSize
+ 1 + spec.Flags.size()
+ sizeof(EVENTHEADER_COMMAND_TYPES)
+ 1;
if (commandSize <= sizeof(commandStack))
{
command = commandStack;
}
else
{
commandHeap.reserve(commandSize); // may throw
command = commandHeap.data();
}
snprintf(command, commandSize, "%.*s%s%.*s %s",
eventNameSize, spec.EventName.data(),
spec.Flags.empty() ? "" : ":",
(unsigned)spec.Flags.size(), spec.Flags.data(),
EVENTHEADER_COMMAND_TYPES);
}
else
{
error = EINVAL; // Unexpected spec.Kind.
goto Done;
}
error = PreregisterTracepointImpl(command, eventNameSize);
}
catch (...)
{
error = ENOMEM;
}
Done:
return error;
}
_Success_(return == 0) int
TracepointCache::PreregisterTracepoint(_In_z_ char const* registerCommand) noexcept
{
int error;
unsigned eventNameSize;
for (eventNameSize = 0;; eventNameSize += 1)
{
auto ch = registerCommand[eventNameSize];
if (ch == 0 || ch == ' ' || ch == ':')
{
break;
}
if (eventNameSize > EventNameMaxSize)
{
error = EINVAL;
goto Done;
}
}
if (!EventNameIsValid({ registerCommand, eventNameSize }))
{
error = EINVAL;
goto Done;
}
error = PreregisterTracepointImpl(registerCommand, eventNameSize);
Done:
return error;
}
_Success_(return == 0) int
TracepointCache::PreregisterTracepointImpl(_In_z_ char const* registerCommand, unsigned eventNameSize) noexcept
{
int error;
auto const name = TracepointName(UserEventsSystemName, { registerCommand, eventNameSize });
assert(EventNameIsValid(name.EventName)); // Precondition ensured by caller.
if (m_byName.find(name) != m_byName.end())
{
error = EALREADY;
goto Done;
}
try
{
auto registration = std::make_unique<TracepointRegistration>();
auto const dataFile = GetUserEventsDataFile();
if (dataFile < 0)
{
error = -dataFile;
goto Done;
}
user_reg63 reg = {};
reg.size = sizeof(reg);
reg.enable_bit = 0;
reg.enable_size = sizeof(registration->StatusWord);
reg.enable_addr = (uintptr_t)&registration->StatusWord;
reg.name_args = (uintptr_t)registerCommand;
if (0 > ioctl(dataFile, DIAG_IOCSREG, &reg))
{
error = errno;
goto Done;
}
assert(reg.write_index <= 0x7fffffff);
registration->DataFile = dataFile;
registration->WriteIndex = static_cast<int>(reg.write_index);
std::vector<char> systemAndFormat;
systemAndFormat.reserve(name.SystemName.size() + 512); // may throw
systemAndFormat.assign(name.SystemName.begin(), name.SystemName.end());
systemAndFormat.push_back('\n'); // For readability when debugging.
error = AppendTracingFormatFile(systemAndFormat, name.SystemName, name.EventName);
if (error == 0)
{
error = Add(
std::move(systemAndFormat),
name.SystemName.size(),
sizeof(long) == 8,
std::move(registration));
}
}
catch (...)
{
error = ENOMEM;
}
Done:
return error;
}
_Success_(return == 0) int
TracepointCache::Add(
std::vector<char>&& systemAndFormat,
size_t systemNameSize,
bool longSize64,
std::unique_ptr<TracepointRegistration> registration) noexcept
{
int error;
uint32_t id = 0;
bool idAdded = false;
try
{
assert(systemNameSize < systemAndFormat.size());
auto systemName = std::string_view(systemAndFormat.data(), systemNameSize);
auto formatFile = std::string_view(
systemAndFormat.data() + systemNameSize + 1,
systemAndFormat.size() - systemNameSize - 1);
PerfEventMetadata metadata;
if (!metadata.Parse(longSize64, systemName, formatFile))
{
error = EINVAL;
}
else if (auto name = TracepointName(metadata.SystemName(), metadata.Name());
!name.IsValid())
{
error = EINVAL;
}
else if (
m_byId.end() != m_byId.find(metadata.Id()) ||
m_byName.end() != m_byName.find(name))
{
error = EEXIST;
}
else
{
int8_t commonTypeOffset = CommonTypeOffsetInit;
uint8_t commonTypeSize = CommonTypeSizeInit;
for (unsigned i = 0; i != metadata.CommonFieldCount(); i += 1)
{
auto const& field = metadata.Fields()[i];
if (field.Name() == "common_type"sv)
{
if (field.Offset() < 128 &&
(field.Size() == 1 || field.Size() == 2 || field.Size() == 4) &&
field.Array() == PerfFieldArrayNone)
{
commonTypeOffset = static_cast<int8_t>(field.Offset());
commonTypeSize = static_cast<uint8_t>(field.Size());
if (m_commonTypeOffset == CommonTypeOffsetInit)
{
// First event to be parsed. Use its "common_type" field.
assert(m_commonTypeSize == CommonTypeSizeInit);
m_commonTypeOffset = commonTypeOffset;
m_commonTypeSize = commonTypeSize;
}
}
break;
}
}
if (commonTypeOffset == CommonTypeOffsetInit)
{
// Did not find a usable "common_type" field.
error = EINVAL;
}
else if (
m_commonTypeOffset != commonTypeOffset ||
m_commonTypeSize != commonTypeSize)
{
// Unexpected: found a different "common_type" field.
error = EINVAL;
}
else
{
id = metadata.Id();
auto er = m_byId.try_emplace(
id,
std::move(systemAndFormat),
std::move(metadata),
std::move(registration));
assert(er.second);
idAdded = er.second;
m_byName.try_emplace(name, er.first->second);
error = 0;
}
}
}
catch (...)
{
if (idAdded)
{
m_byId.erase(id);
}
error = ENOMEM;
}
return error;
}

View file

@ -0,0 +1,349 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include <tracepoint/TracepointPath.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/stat.h>
using namespace std::string_view_literals;
static constexpr unsigned TRACING_DIR_MAX = 256;
static int
IsSpaceChar(char ch)
{
return ch == ' ' || ch == '\t';
}
static int
IsNonSpaceChar(char ch)
{
return ch != '\0' && !IsSpaceChar(ch);
}
static int
GetFailureErrno(void)
{
int err = errno;
assert(err > 0);
if (err <= 0)
{
err = ENOENT;
}
return err;
}
static _Ret_z_ char const*
UpdateTracingDirectory(char const** pStaticTracingDir) noexcept
{
static pthread_mutex_t staticTracingDirMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&staticTracingDirMutex);
auto tracingDir = __atomic_load_n(pStaticTracingDir, __ATOMIC_RELAXED); // CONSUME semantics.
if (tracingDir == nullptr)
{
static char staticTracingDirBuffer[TRACING_DIR_MAX + 1];
#define SYS_KERNEL_TRACING "/sys/kernel/tracing"
struct stat eventsStat = {};
if (!stat(SYS_KERNEL_TRACING "/events", &eventsStat) && S_ISDIR(eventsStat.st_mode))
{
memcpy(staticTracingDirBuffer, SYS_KERNEL_TRACING, sizeof(SYS_KERNEL_TRACING));
}
else
{
auto const mountsFile = fopen("/proc/mounts", "r");
if (mountsFile != nullptr)
{
for (;;)
{
char line[4097];
if (!fgets(line, sizeof(line), mountsFile))
{
break;
}
// line is "deviceName mountPoint fileSystem otherStuff..."
size_t linePos = 0;
// deviceName
while (IsNonSpaceChar(line[linePos]))
{
linePos += 1;
}
// whitespace
while (IsSpaceChar(line[linePos]))
{
linePos += 1;
}
// mountPoint
auto const mountPointBegin = linePos;
while (IsNonSpaceChar(line[linePos]))
{
linePos += 1;
}
auto const mountPointEnd = linePos;
// whitespace
while (IsSpaceChar(line[linePos]))
{
linePos += 1;
}
// fileSystem
auto const fileSystemBegin = linePos;
while (IsNonSpaceChar(line[linePos]))
{
linePos += 1;
}
auto const fileSystemEnd = linePos;
if (!IsSpaceChar(line[linePos]))
{
// Ignore line if no whitespace after fileSystem.
continue;
}
std::string_view const fileSystem(line + fileSystemBegin, fileSystemEnd - fileSystemBegin);
std::string_view pathSuffix;
bool keepLooking;
if (fileSystem == "tracefs"sv)
{
// "tracefsMountPoint"
pathSuffix = ""sv;
keepLooking = false; // prefer "tracefs" over "debugfs".
}
else if (staticTracingDirBuffer[0] == 0 &&
fileSystem == "debugfs"sv)
{
// "debugfsMountPoint/tracing"
pathSuffix = "/tracing"sv;
keepLooking = true; // prefer "tracefs" over "debugfs".
}
else
{
continue;
}
auto const mountPointLen = mountPointEnd - mountPointBegin;
auto const pathLen = mountPointLen + pathSuffix.size() + 1; // includes NUL
if (pathLen > sizeof(staticTracingDirBuffer))
{
continue;
}
// path = mountpoint + suffix, e.g. "/sys/kernel/tracing\0"
memcpy(staticTracingDirBuffer, line + mountPointBegin, mountPointLen);
memcpy(staticTracingDirBuffer + mountPointLen, pathSuffix.data(), pathSuffix.size() + 1); // includes NUL
if (!keepLooking)
{
break;
}
}
fclose(mountsFile);
}
}
tracingDir = staticTracingDirBuffer;
__atomic_store_n(pStaticTracingDir, tracingDir, __ATOMIC_RELEASE); // CONSUME semantics.
}
pthread_mutex_unlock(&staticTracingDirMutex);
return tracingDir;
}
_Ret_z_ char const*
tracepoint_control::GetTracingDirectory() noexcept
{
static char const* staticTracingDir = nullptr;
auto tracingDir = __atomic_load_n(&staticTracingDir, __ATOMIC_RELAXED); // CONSUME semantics.
if (tracingDir == nullptr)
{
tracingDir = UpdateTracingDirectory(&staticTracingDir);
}
return tracingDir;
}
static _Success_(return >= 0) int
UpdateUserEventsDataFile(int* pStaticFile) noexcept
{
int newFileOrError;
if (auto const tracingDir = tracepoint_control::GetTracingDirectory();
tracingDir[0] == 0)
{
// Unable to find the "/.../tracing" directory.
newFileOrError = -ENOTSUP;
}
else
{
#define USER_EVENTS_DATA "/user_events_data"
char fileName[TRACING_DIR_MAX + sizeof(USER_EVENTS_DATA)];
auto const cchTracingDir = strlen(tracingDir);
assert(cchTracingDir <= TRACING_DIR_MAX);
memcpy(fileName, tracingDir, cchTracingDir);
memcpy(fileName + cchTracingDir, USER_EVENTS_DATA, sizeof(USER_EVENTS_DATA));
newFileOrError = open(fileName, O_WRONLY);
if (0 > newFileOrError)
{
newFileOrError = -GetFailureErrno();
}
}
int oldFileOrError = -EAGAIN;
for (;;)
{
if (__atomic_compare_exchange_n(
pStaticFile,
&oldFileOrError,
newFileOrError,
0,
__ATOMIC_RELAXED,
__ATOMIC_RELAXED))
{
// The cmpxchg set *pStaticFile = newFileOrError.
return newFileOrError;
}
// The cmpxchg set oldFileOrError = *pStaticFile.
if (oldFileOrError >= 0 || newFileOrError < 0)
{
// Prefer the existing contents of pStaticFile.
if (newFileOrError >= 0)
{
close(newFileOrError);
}
return oldFileOrError;
}
}
}
_Success_(return >= 0) int
tracepoint_control::GetUserEventsDataFile() noexcept
{
static int staticFileOrError = -EAGAIN; // Intentionally leaked.
int fileOrError = __atomic_load_n(&staticFileOrError, __ATOMIC_RELAXED);
return fileOrError != -EAGAIN
? fileOrError
: UpdateUserEventsDataFile(&staticFileOrError);
}
_Success_(return == 0) int
tracepoint_control::AppendTracingFile(
std::vector<char>& dest,
_In_z_ char const* fileName) noexcept
{
int error;
auto const destOldSize = dest.size();
if (auto const file = fopen(fileName, "r");
file == nullptr)
{
error = errno;
}
else
{
// Usually a special file, so don't try to seek or stat to get file size.
try
{
auto pos = destOldSize;
for (;;)
{
dest.resize(dest.size() + 512);
auto const readSize = fread(dest.data() + pos, 1, dest.size() - pos, file);
pos += readSize;
if (pos != dest.size())
{
if (feof(file))
{
dest.resize(pos);
error = 0;
break;
}
else if (ferror(file))
{
dest.resize(destOldSize);
error = EIO;
break;
}
}
}
}
catch (...)
{
dest.resize(destOldSize);
error = ENOMEM;
}
fclose(file);
}
return error;
}
_Success_(return == 0) int
tracepoint_control::AppendTracingFormatFile(
std::vector<char>& dest,
std::string_view systemName,
std::string_view eventName) noexcept
{
int error;
if (systemName.empty() ||
systemName.find_first_of("/.\0"sv) != systemName.npos ||
eventName.empty() ||
eventName.find_first_of("/.\0"sv) != eventName.npos)
{
// Invalid systemName or eventName parameter.
error = EINVAL;
}
else if (
auto const tracingDir = GetTracingDirectory();
tracingDir[0] == 0)
{
// Unable to find the "/.../tracing" directory.
error = ENOTSUP;
}
else
{
char fileName[TRACING_DIR_MAX + 256];
unsigned const pathLen = snprintf(fileName, sizeof(fileName), "%s/events/%.*s/%.*s/format",
tracingDir,
(unsigned)systemName.size(), systemName.data(),
(unsigned)eventName.size(), eventName.data());
if (pathLen >= sizeof(fileName))
{
// tracingDirectory + systemName + eventName too long.
error = E2BIG;
}
else
{
error = AppendTracingFile(dest, fileName);
}
}
return error;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,306 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include <tracepoint/TracepointSpec.h>
#include <tracepoint/TracepointName.h>
#include <assert.h>
using namespace tracepoint_control;
static bool
AsciiIsSpace(char ch)
{
return ch == ' ' || ('\t' <= ch && ch <= '\r');
}
static size_t
CountLeadingWhitespace(std::string_view str)
{
size_t pos;
for (pos = 0; pos != str.size(); pos += 1)
{
if (!AsciiIsSpace(str[pos]))
{
break;
}
}
return pos;
}
TracepointSpec::TracepointSpec(std::string_view const specString) noexcept
{
bool identifier;
bool hasFields = false;
/*
Cases:
1. Empty
2. '#' ANY*
3. ':' WS* EventName
4. ':' WS* SystemName ':' EventName
5. EventName (WS Fields*)?
6. SystemName ':' EventName (':' Flags)? (WS Fields*)?
*/
auto const trimmed = Trim(specString);
Trimmed = trimmed;
size_t pos = 0;
if (pos == trimmed.size())
{
Kind = TracepointSpecKind::Empty;
return; // Case 1
}
else if (trimmed[pos] == '#')
{
Kind = TracepointSpecKind::Empty;
return; // Case 2
}
else if (trimmed[pos] == ':')
{
size_t startPos;
// Skip ':'
pos += 1;
// Skip WS*
pos += CountLeadingWhitespace(trimmed.substr(pos));
// Skip Name
for (startPos = pos;; pos += 1)
{
if (pos == trimmed.size())
{
SystemName = UserEventsSystemName;
EventName = trimmed.substr(startPos, pos - startPos); // Might be empty.
identifier = true;
goto Done; // Case 3.
}
if (AsciiIsSpace(trimmed[pos]))
{
SystemName = UserEventsSystemName;
EventName = trimmed.substr(startPos, pos - startPos);
Kind = TracepointSpecKind::ErrorIdentifierCannotHaveFields;
return;
}
if (trimmed[pos] == ':')
{
break;
}
}
// End of name - ':'.
SystemName = trimmed.substr(startPos, pos - startPos); // Might be empty.
// Skip ':'
pos += 1;
// Skip Name
for (startPos = pos; pos != trimmed.size(); pos += 1)
{
if (AsciiIsSpace(trimmed[pos]))
{
EventName = trimmed.substr(startPos, pos - startPos);
Kind = TracepointSpecKind::ErrorIdentifierCannotHaveFields;
return;
}
if (trimmed[pos] == ':')
{
EventName = trimmed.substr(startPos, pos - startPos);
Kind = TracepointSpecKind::ErrorIdentifierCannotHaveFlags;
return;
}
}
EventName = trimmed.substr(startPos, pos - startPos); // Might be empty.
identifier = true;
goto Done; // Case 4.
}
else
{
size_t startPos;
assert(pos != trimmed.size());
assert(!AsciiIsSpace(trimmed[pos]));
assert(trimmed[pos] != ':');
startPos = pos;
pos += 1;
// Skip Name
for (;; pos += 1)
{
if (pos == trimmed.size())
{
SystemName = UserEventsSystemName;
EventName = trimmed.substr(startPos, pos - startPos);
identifier = false;
goto Done; // Case 5, no fields.
}
if (AsciiIsSpace(trimmed[pos]))
{
SystemName = UserEventsSystemName;
EventName = trimmed.substr(startPos, pos - startPos);
goto DefinitionFields; // Case 5, fields.
}
if (trimmed[pos] == ':')
{
break;
}
}
// End of name - ':'.
SystemName = trimmed.substr(startPos, pos - startPos);
// Skip ':'
pos += 1;
// Skip Name
for (startPos = pos;; pos += 1)
{
if (pos == trimmed.size())
{
EventName = trimmed.substr(startPos, pos - startPos); // Might be empty.
identifier = false;
goto Done; // Case 6, no fields.
}
if (AsciiIsSpace(trimmed[pos]))
{
EventName = trimmed.substr(startPos, pos - startPos); // Might be empty.
goto DefinitionFields; // Case 6, fields.
}
if (trimmed[pos] == ':')
{
break;
}
}
EventName = trimmed.substr(startPos, pos - startPos); // Might be empty.
// Skip ':'
pos += 1;
// Skip Name
for (startPos = pos;; pos += 1)
{
if (pos == trimmed.size())
{
Flags = trimmed.substr(startPos, pos - startPos); // Might be empty.
identifier = false;
goto Done; // Case 6, no fields.
}
if (AsciiIsSpace(trimmed[pos]))
{
Flags = trimmed.substr(startPos, pos - startPos); // Might be empty.
goto DefinitionFields; // Case 6, fields.
}
if (trimmed[pos] == ':')
{
Flags = trimmed.substr(startPos, pos - startPos);
Kind = TracepointSpecKind::ErrorDefinitionCannotHaveColonAfterFlags;
return;
}
}
DefinitionFields:
// Skip WS*
assert(AsciiIsSpace(trimmed[pos]));
pos += CountLeadingWhitespace(trimmed.substr(pos));
Fields = trimmed.substr(pos); // Might have trailing semicolon.
while (!Fields.empty() &&
(Fields.back() == ';' || AsciiIsSpace(Fields.back())))
{
Fields.remove_suffix(1);
}
hasFields = true;
identifier = false;
goto Done;
}
Done:
if (!EventNameIsValid(EventName))
{
if (EventName.empty())
{
Kind = identifier
? TracepointSpecKind::ErrorIdentifierEventNameEmpty
: TracepointSpecKind::ErrorDefinitionEventNameEmpty;
}
else
{
Kind = identifier
? TracepointSpecKind::ErrorIdentifierEventNameInvalid
: TracepointSpecKind::ErrorDefinitionEventNameInvalid;
}
}
else if (!SystemNameIsValid(SystemName))
{
if (SystemName.empty())
{
Kind = identifier
? TracepointSpecKind::ErrorIdentifierSystemNameEmpty
: TracepointSpecKind::ErrorDefinitionSystemNameEmpty;
}
else
{
Kind = identifier
? TracepointSpecKind::ErrorIdentifierSystemNameInvalid
: TracepointSpecKind::ErrorDefinitionSystemNameInvalid;
}
}
else if (identifier)
{
Kind = TracepointSpecKind::Identifier;
}
else if (hasFields)
{
Kind = TracepointSpecKind::Definition;
}
else if (!EventHeaderEventNameIsValid(EventName))
{
Kind = TracepointSpecKind::ErrorEventHeaderDefinitionEventNameInvalid;
}
else
{
Kind = TracepointSpecKind::EventHeaderDefinition;
}
}
std::string_view
TracepointSpec::Trim(std::string_view const str) noexcept
{
size_t startPos;
size_t endPos;
for (startPos = 0; startPos != str.size(); startPos += 1)
{
if (!AsciiIsSpace(str[startPos]))
{
break;
}
}
for (endPos = str.size(); endPos != startPos; endPos -= 1)
{
if (!AsciiIsSpace(str[endPos - 1]))
{
break;
}
}
return str.substr(startPos, endPos - startPos);
}

View file

@ -0,0 +1,140 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include <tracepoint/TracepointSession.h>
#include <unistd.h>
#include <sys/mman.h>
using namespace tracepoint_control;
TracepointSession::unique_fd::~unique_fd()
{
reset(-1);
}
TracepointSession::unique_fd::unique_fd() noexcept
: m_fd(-1)
{
return;
}
TracepointSession::unique_fd::unique_fd(int fd) noexcept
: m_fd(fd)
{
return;
}
TracepointSession::unique_fd::unique_fd(unique_fd&& other) noexcept
: m_fd(other.m_fd)
{
other.m_fd = -1;
}
TracepointSession::unique_fd&
TracepointSession::unique_fd::operator=(unique_fd&& other) noexcept
{
int fd = other.m_fd;
other.m_fd = -1;
reset(fd);
return *this;
}
TracepointSession::unique_fd::operator bool() const noexcept
{
return m_fd != -1;
}
void
TracepointSession::unique_fd::reset() noexcept
{
reset(-1);
}
void
TracepointSession::unique_fd::reset(int fd) noexcept
{
if (m_fd != -1)
{
close(m_fd);
}
m_fd = fd;
}
int
TracepointSession::unique_fd::get() const noexcept
{
return m_fd;
}
TracepointSession::unique_mmap::~unique_mmap()
{
reset(MAP_FAILED, 0);
}
TracepointSession::unique_mmap::unique_mmap() noexcept
: m_addr(MAP_FAILED)
, m_size(0)
{
return;
}
TracepointSession::unique_mmap::unique_mmap(void* addr, size_t size) noexcept
: m_addr(addr)
, m_size(size)
{
return;
}
TracepointSession::unique_mmap::unique_mmap(unique_mmap&& other) noexcept
: m_addr(other.m_addr)
, m_size(other.m_size)
{
other.m_addr = MAP_FAILED;
other.m_size = 0;
}
TracepointSession::unique_mmap&
TracepointSession::unique_mmap::operator=(unique_mmap&& other) noexcept
{
void* addr = other.m_addr;
size_t size = other.m_size;
other.m_addr = MAP_FAILED;
other.m_size = 0;
reset(addr, size);
return *this;
}
TracepointSession::unique_mmap::operator bool() const noexcept
{
return m_addr != MAP_FAILED;
}
void
TracepointSession::unique_mmap::reset() noexcept
{
reset(MAP_FAILED, 0);
}
void
TracepointSession::unique_mmap::reset(void* addr, size_t size) noexcept
{
if (m_addr != MAP_FAILED)
{
munmap(m_addr, m_size);
}
m_addr = addr;
m_size = size;
}
void*
TracepointSession::unique_mmap::get() const noexcept
{
return m_addr;
}
size_t
TracepointSession::unique_mmap::get_size() const noexcept
{
return m_size;
}

View file

@ -0,0 +1,2 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/tracepoint-controlTargets.cmake")

View file

@ -0,0 +1,7 @@
add_executable(tracepoint-collect
tracepoint-collect.cpp)
target_link_libraries(tracepoint-collect
tracepoint-control tracepoint-decode)
target_compile_features(tracepoint-collect
PRIVATE cxx_std_17)
install(TARGETS tracepoint-collect)

View file

@ -0,0 +1,942 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
/*
Simple tool for collecting tracepoints into perf.data files.
*/
#include <tracepoint/TracepointSession.h>
#include <tracepoint/TracepointSpec.h>
#include <tracepoint/PerfDataFileWriter.h>
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <vector>
#define PROGRAM_NAME "tracepoint-collect"
#define EXIT_SIGNALS SIGTERM, SIGINT
#define EXIT_SIGNALS_STR "SIGTERM or SIGINT"
static constexpr int ExitSigs[] = { EXIT_SIGNALS };
static constexpr unsigned ExitSigsCount = sizeof(ExitSigs) / sizeof(ExitSigs[0]);
static char const* const UsageCommon = R"(
Usage: )" PROGRAM_NAME R"( [options...] TracepointSpecs...
)";
// Usage error: stderr += UsageCommon + UsageShort.
static char const* const UsageShort = R"(
Try ")" PROGRAM_NAME R"( --help" for more information.
)";
// -h or --help: stdout += UsageCommon + UsageLong.
static char const* const UsageLong = R"(
Collects tracepoint events and saves them to a perf.data file. Collection
runs until )" EXIT_SIGNALS_STR R"( is received.
Requires privileges, typically the CAP_PERFMON capability plus read access to
/sys/kernel/tracing. Pre-registration of a tracepoint requires write access to
/sys/kernel/tracing/user_events_data.
Options:
-b, --buffersize <size>
Set the size of each buffer, in kilobytes. There will be
one buffer per CPU. The default is 128, max is 2GB.
-c, --circular Use circular trace mode. Events will be collected in
circular buffers (new events overwrite old) until the
signal is received, at which point the output file will be
created, buffer contents will be written to the file, and
the tool will exit.
-C, --realtime Use realtime trace mode (default). File will be created
immediately and events will be written to the file as they
are received until the signal is received, at which point
the tool will finalize the file and exit.
-i, --input <file> Read additional TracepointSpecs from <file>. Each line in
the file is treated as a TracepointSpec. Empty lines and
lines starting with '#' are ignored.
-o, --output <file> Set the output filename. The default is "./perf.data".
-w, --wakeup <size> Set the wakeup watermark size for realtime trace mode, in
kilobytes. The default is 2. The tool will wait for a
buffer to have at least this much data before waking to
flush the buffer to the output file.
-v, --verbose Show diagnostic output.
-h, --help Show this help message and exit.
A TracepointSpec is one of the following:
* If the tracepoint is a normal (non-EventHeader) user_event that may not
already exist, use the full user_event definition,
"SystemName:EventName Fields...". If the tracepoint does not already exist,
it will be pre-registered so that it can be added to the trace session. For
example:
user_events:MyEvent u32 MyField1; struct MyStruct2 MyField2 20
" Fields..." is required. For an event with no fields, use " ;", e.g.
user_events:MySimpleEvent ;
See https://docs.kernel.org/trace/user_events.html#command-format for
details on the user_events definition syntax.
* If the tracepoint is an EventHeader user_event that may not already exist,
use the EventHeader identity, "SystemName:ProviderName_Suffix". If the
tracepoint does not already exist, it will be pre-registered so that it can
be added to the trace session. For example:
user_events:MyProvider_L5K1
* If the tracepoint is known to already be registered (e.g. a kernel event),
use the tracepoint identity with a leading colon, ":SystemName:EventName".
If the tracepoint does not already exist, it will not be added to the trace
session. For example:
:sched:sched_switch
In all cases, you may omit "SystemName:" if it is "user_events:", e.g.
MyEvent u32 MyField1;
MyProvider_L5K1Gmygroup
:MyUserEventThatAlreadyExists
For TracepointSpecs provided on the command line, use quotation marks to
ensure correct handling of spaces and semicolons in each TracepointSpec, e.g.
"user_events:MyEvent u32 MyField1; struct MyStruct2 MyField2 20"
)";
using namespace std::string_view_literals;
using namespace tracepoint_control;
using namespace tracepoint_decode;
class Tracepoint
{
std::vector<char> storage;
public:
TracepointSpec spec;
Tracepoint(Tracepoint&&) = default;
Tracepoint& operator=(Tracepoint&&) = default;
explicit
Tracepoint(std::string_view line)
{
auto const trimmedLine = TracepointSpec::Trim(line);
if (!trimmedLine.empty())
{
storage.assign(trimmedLine.begin(), trimmedLine.end());
}
spec = TracepointSpec({ storage.data(), storage.size() });
}
};
struct Options
{
char const* output = "./perf.data";
bool verbose = false;
};
// fprintf(stderr, "PROGRAM_NAME: " + format, args...).
static void
PrintStderr(const char* format, ...)
{
va_list args;
va_start(args, format);
fputs(PROGRAM_NAME ": ", stderr);
vfprintf(stderr, format, args);
va_end(args);
}
// if (condition) fprintf(stderr, "PROGRAM_NAME: " + format, args...).
static void
PrintStderrIf(bool condition, const char* format, ...)
{
if (condition)
{
va_list args;
va_start(args, format);
fputs(PROGRAM_NAME ": ", stderr);
vfprintf(stderr, format, args);
va_end(args);
}
}
static void
PushFrontDef(Options const& o, std::vector<Tracepoint>& tracepoints, Tracepoint&& tp)
{
auto const& spec = tp.spec;
auto const trimmed = spec.Trimmed;
switch (tp.spec.Kind)
{
case TracepointSpecKind::Empty:
break;
case TracepointSpecKind::Identifier:
PrintStderrIf(o.verbose, "verbose: identifier \"%.*s:%.*s\".\n",
(unsigned)spec.SystemName.size(), spec.SystemName.data(),
(unsigned)spec.EventName.size(), spec.EventName.data());
tracepoints.push_back(std::move(tp));
break;
case TracepointSpecKind::Definition:
if (spec.SystemName != UserEventsSystemName)
{
PrintStderr("error: definition system name \"%.*s\" must be 'user_events' (from \"%.*s\").\n",
(unsigned)spec.SystemName.size(), spec.SystemName.data(),
(unsigned)trimmed.size(), trimmed.data());
}
else
{
PrintStderrIf(o.verbose, "verbose: definition \"%.*s:%.*s%s%.*s%s%.*s\"\n",
(unsigned)spec.SystemName.size(), spec.SystemName.data(),
(unsigned)spec.EventName.size(), spec.EventName.data(),
spec.Flags.empty() ? "" : ":",
(unsigned)spec.Flags.size(), spec.Flags.data(),
spec.Fields.empty() ? "" : " ",
(unsigned)spec.Fields.size(), spec.Fields.data());
tracepoints.push_back(std::move(tp));
}
break;
case TracepointSpecKind::EventHeaderDefinition:
if (spec.SystemName != UserEventsSystemName)
{
PrintStderr("error: eventheader system name \"%.*s\" must be 'user_events' (from \"%.*s\").\n",
(unsigned)spec.SystemName.size(), spec.SystemName.data(),
(unsigned)trimmed.size(), trimmed.data());
}
else
{
PrintStderrIf(o.verbose, "verbose: eventheader \"%.*s:%.*s%s%.*s\".\n",
(unsigned)spec.SystemName.size(), spec.SystemName.data(),
(unsigned)spec.EventName.size(), spec.EventName.data(),
spec.Flags.empty() ? "" : ":",
(unsigned)spec.Flags.size(), spec.Flags.data());
tracepoints.push_back(std::move(tp));
}
break;
case TracepointSpecKind::ErrorIdentifierCannotHaveFields:
PrintStderr("error: identifier cannot have fields (from \"%.*s\").\n",
(unsigned)trimmed.size(), trimmed.data());
break;
case TracepointSpecKind::ErrorIdentifierCannotHaveFlags:
PrintStderr("error: identifier cannot have flags (from \"%.*s\").\n",
(unsigned)trimmed.size(), trimmed.data());
break;
case TracepointSpecKind::ErrorDefinitionCannotHaveColonAfterFlags:
PrintStderr("error: definition cannot have colon after flags (from \"%.*s\").\n",
(unsigned)trimmed.size(), trimmed.data());
break;
case TracepointSpecKind::ErrorIdentifierEventNameEmpty:
PrintStderr("error: identifier event name is empty (from \"%.*s\").\n",
(unsigned)trimmed.size(), trimmed.data());
break;
case TracepointSpecKind::ErrorDefinitionEventNameEmpty:
PrintStderr("error: definition event name is empty (from \"%.*s\").\n",
(unsigned)trimmed.size(), trimmed.data());
break;
case TracepointSpecKind::ErrorIdentifierEventNameInvalid:
PrintStderr("error: identifier event name \"%.*s\" is invalid (from \"%.*s\").\n",
(unsigned)spec.EventName.size(), spec.EventName.data(),
(unsigned)trimmed.size(), trimmed.data());
break;
case TracepointSpecKind::ErrorDefinitionEventNameInvalid:
PrintStderr("error: definition event name \"%.*s\" is invalid (from \"%.*s\").\n",
(unsigned)spec.EventName.size(), spec.EventName.data(),
(unsigned)trimmed.size(), trimmed.data());
break;
case TracepointSpecKind::ErrorEventHeaderDefinitionEventNameInvalid:
PrintStderr("error: eventheader event name \"%.*s\" is invalid (from \"%.*s\").\n",
(unsigned)spec.EventName.size(), spec.EventName.data(),
(unsigned)trimmed.size(), trimmed.data());
PrintStderr("(error) If this was meant to be the name of an existing non-eventheader event, add a leading ':'.\n");
PrintStderr("(error) If this was meant to be the definition of a non-eventheader event, a Fields... section must be provided.\n");
PrintStderr("(error) If a non-eventheader event has no fields, use \" ;\" for Fields..., e.g. \"MyEvent ;\".\n");
break;
case TracepointSpecKind::ErrorIdentifierSystemNameEmpty:
PrintStderr("error: identifier system name is empty (from \"%.*s\").\n",
(unsigned)trimmed.size(), trimmed.data());
break;
case TracepointSpecKind::ErrorDefinitionSystemNameEmpty:
PrintStderr("error: definition system name is empty (from \"%.*s\").\n",
(unsigned)trimmed.size(), trimmed.data());
break;
case TracepointSpecKind::ErrorIdentifierSystemNameInvalid:
PrintStderr("error: identifier system name \"%.*s\" is invalid (from \"%.*s\").\n",
(unsigned)spec.SystemName.size(), spec.SystemName.data(),
(unsigned)trimmed.size(), trimmed.data());
break;
case TracepointSpecKind::ErrorDefinitionSystemNameInvalid:
PrintStderr("error: definition system name \"%.*s\" is invalid (from \"%.*s\").\n",
(unsigned)spec.SystemName.size(), spec.SystemName.data(),
(unsigned)trimmed.size(), trimmed.data());
break;
}
}
static bool
PushFrontDefsFromFile(Options const& o, std::vector<Tracepoint>& tracepoints, char const* filename)
{
// CodeQL [SM01937] This is a sample/tool. Using externally-supplied path is intended behavior.
FILE* file = fopen(filename, "r");
if (file == nullptr)
{
PrintStderr("error: cannot open file \"%s\", error %u.\n",
filename, errno);
return false;
}
std::vector<char> line;
char buf[128];
while (fgets(buf, sizeof(buf), file))
{
line.insert(line.end(), buf, buf + strlen(buf));
if (line.back() == '\n')
{
PushFrontDef(o, tracepoints, Tracepoint({ line.data(), line.size() }));
line.clear();
}
}
auto const error = errno;
bool const ok = 0 == ferror(file);
fclose(file);
if (!ok)
{
fprintf(stderr, "error: failed reading file \"%s\", error %u.\n",
filename, error);
}
else
{
// Flush last line.
PushFrontDef(o, tracepoints, Tracepoint({ line.data(), line.size() }));
}
return ok;
}
static void
ArgSize(
_In_z_ char const* flagName,
unsigned maxValue,
int argi,
int argc,
_In_reads_(argc) char* argv[],
_Inout_ bool* usageError,
_Inout_ unsigned* value)
{
if (argi >= argc)
{
PrintStderr("error: missing value for flag %s.\n",
flagName);
*usageError = true;
}
else
{
auto const* const arg = argv[argi];
auto argValue = strtoul(arg, nullptr, 0);
if (argValue == 0)
{
PrintStderr("error: expected positive integer for flag %s \"%s\".\n",
flagName, arg);
*usageError = true;
}
else if (argValue > maxValue)
{
PrintStderr("error: value %lu too large (max %u) for flag %s \"%s\".\n",
argValue, maxValue, flagName, arg);
*usageError = true;
}
else
{
*value = static_cast<unsigned>(argValue);
}
}
}
static void
InitExitSigSet(sigset_t* exitSigSet)
{
sigemptyset(exitSigSet);
for (auto exitSig : ExitSigs)
{
sigaddset(exitSigSet, exitSig);
}
}
static int SignalHandled = 0;
class SignalMask
{
int m_initError;
bool m_masked;
unsigned short m_sigsInstalled;
sigset_t m_oldSigSet;
struct sigaction m_oldActs[ExitSigsCount];
public:
SignalMask(SignalMask const&) = delete;
SignalMask& operator=(SignalMask const&) = delete;
~SignalMask()
{
Restore();
}
SignalMask() noexcept
: m_initError(0)
, m_masked(false)
, m_sigsInstalled(0)
{
struct sigaction newAct = {};
newAct.sa_handler = [](int sig)
{
SignalHandled = sig;
};
InitExitSigSet(&newAct.sa_mask);
if (sigprocmask(SIG_BLOCK, &newAct.sa_mask, &m_oldSigSet))
{
m_initError = errno;
PrintStderr("error: sigprocmask error %u.\n",
m_initError);
if (m_initError == 0)
{
m_initError = EINTR;
}
return;
}
m_masked = true;
for (; m_sigsInstalled != ExitSigsCount; m_sigsInstalled += 1)
{
if (sigaction(ExitSigs[m_sigsInstalled], &newAct, &m_oldActs[m_sigsInstalled]))
{
m_initError = errno;
PrintStderr("error: sigaction error %u.\n",
m_initError);
if (m_initError == 0)
{
m_initError = EINTR;
}
return;
}
}
}
int
InitError() const noexcept
{
return m_initError;
}
sigset_t const*
OldSigSet() const noexcept
{
return &m_oldSigSet;
}
void
Restore() noexcept
{
for (; m_sigsInstalled != 0; m_sigsInstalled -= 1)
{
sigaction(ExitSigs[m_sigsInstalled - 1], &m_oldActs[m_sigsInstalled - 1], nullptr);
}
if (m_masked)
{
m_masked = false;
sigprocmask(SIG_SETMASK, &m_oldSigSet, nullptr);
if (m_initError == 0)
{
fputc('\n', stderr);
}
}
}
};
static unsigned
EnableTracepoints(
Options const& o,
std::vector<Tracepoint> const& tracepoints,
TracepointCache& cache,
TracepointSession& session)
{
unsigned enabledCount = 0;
for (auto const& tp : tracepoints)
{
int error;
if (tp.spec.Kind == TracepointSpecKind::Identifier)
{
error = cache.AddFromSystem(TracepointName(tp.spec.SystemName, tp.spec.EventName));
switch (error)
{
default:
PrintStderr("warning: Cannot load format for \"%.*s:%.*s\", error %u.\n",
(unsigned)tp.spec.SystemName.size(), tp.spec.SystemName.data(),
(unsigned)tp.spec.EventName.size(), tp.spec.EventName.data(),
error);
continue;
case 0:
PrintStderrIf(o.verbose, "verbose: Loaded format for \"%.*s:%.*s\".\n",
(unsigned)tp.spec.SystemName.size(), tp.spec.SystemName.data(),
(unsigned)tp.spec.EventName.size(), tp.spec.EventName.data());
break;
case EEXIST:
PrintStderrIf(o.verbose, "verbose: Format already loaded for \"%.*s:%.*s\".\n",
(unsigned)tp.spec.SystemName.size(), tp.spec.SystemName.data(),
(unsigned)tp.spec.EventName.size(), tp.spec.EventName.data());
break;
}
}
else
{
error = cache.PreregisterTracepointDefinition(tp.spec);
switch (error)
{
default:
PrintStderr("warning: Cannot pre-register \"%.*s:%.*s\", error %u.\n",
(unsigned)tp.spec.SystemName.size(), tp.spec.SystemName.data(),
(unsigned)tp.spec.EventName.size(), tp.spec.EventName.data(),
error);
continue;
case 0:
PrintStderrIf(o.verbose, "verbose: Pre-registered \"%.*s:%.*s\".\n",
(unsigned)tp.spec.SystemName.size(), tp.spec.SystemName.data(),
(unsigned)tp.spec.EventName.size(), tp.spec.EventName.data());
break;
case EEXIST:
PrintStderrIf(o.verbose, "verbose: Did not pre-register \"%.*s:%.*s\" (already cached).\n",
(unsigned)tp.spec.SystemName.size(), tp.spec.SystemName.data(),
(unsigned)tp.spec.EventName.size(), tp.spec.EventName.data());
break;
}
}
error = session.EnableTracepoint(TracepointName(tp.spec.SystemName, tp.spec.EventName));
if (error != 0)
{
PrintStderr("warning: Cannot enable \"%.*s:%.*s\", error %u.\n",
(unsigned)tp.spec.SystemName.size(), tp.spec.SystemName.data(),
(unsigned)tp.spec.EventName.size(), tp.spec.EventName.data(),
error);
}
else
{
enabledCount += 1;
PrintStderrIf(o.verbose, "verbose: Enabled \"%.*s:%.*s\".\n",
(unsigned)tp.spec.SystemName.size(), tp.spec.SystemName.data(),
(unsigned)tp.spec.EventName.size(), tp.spec.EventName.data());
}
}
return enabledCount;
}
static int
CollectCircular(Options const& o, TracepointSession& session)
{
int error;
int sig = 0;
sigset_t exitSigSet;
InitExitSigSet(&exitSigSet);
// Scope for signalMask.
PrintStderr("info: collecting until " EXIT_SIGNALS_STR ".\n");
{
SignalMask signalMask;
error = signalMask.InitError();
if (error != 0)
{
return error;
}
sigwait(&exitSigSet, &sig);
}
PrintStderr("info: stopping session (signal %u).\n",
sig);
error = session.SavePerfDataFile(o.output);
if (error == 0)
{
PrintStderr("info: saved buffer contents to \"%s\".\n",
o.output);
}
else
{
PrintStderr("error: failed saving to \"%s\", error %u.\n",
o.output, error);
}
return error;
}
static int
CollectRealtime(Options const& o, TracepointSession& session)
{
int error;
unsigned wakeupCount = 0;
uint64_t eventBytes = 0;
PerfDataFileWriter writer;
error = writer.Create(o.output);
if (error != 0)
{
PrintStderr("error: failed creating file \"%s\", error %u.\n",
o.output, error);
goto Done;
}
error = writer.WriteFinishedInit();
if (error != 0)
{
PrintStderr("error: failed writing FinishedInit to \"%s\", error %u.\n",
o.output, error);
unlink(o.output); // Nothing useful in the file.
goto Done;
}
{
auto const writerSessionStartPos = writer.FilePos();
auto writerRoundStartPos = writerSessionStartPos;
TracepointTimestampRange writtenRange;
assert(SignalHandled == 0);
// Scope for signalMask.
PrintStderr("info: created \"%s\", collecting until " EXIT_SIGNALS_STR ".\n",
o.output);
{
SignalMask signalMask;
error = signalMask.InitError();
if (error != 0)
{
unlink(o.output); // Nothing useful in the file.
goto Done;
}
while (SignalHandled == 0) // Not sure whether this can ever be false.
{
error = session.WaitForWakeup(nullptr, signalMask.OldSigSet());
if (error != 0)
{
signalMask.Restore();
if (error != EINTR)
{
PrintStderr("error: ppoll failed, error %u.\n",
error);
}
else
{
PrintStderrIf(o.verbose, "verbose: ppoll EINTR.\n");
}
break;
}
wakeupCount += 1;
error = session.FlushToWriter(writer, &writtenRange);
if (error != 0)
{
signalMask.Restore();
PrintStderr("error: failed flushing \"%s\", error %u.\n",
o.output, error);
goto Finalize;
}
auto const writerRoundEndPos = writer.FilePos();
eventBytes = writerRoundEndPos - writerSessionStartPos;
PrintStderrIf(o.verbose, "verbose: flushed %lu bytes.\n",
static_cast<unsigned long>(writerRoundEndPos - writerRoundStartPos));
if (writerRoundStartPos != writerRoundEndPos)
{
error = writer.WriteFinishedRound();
if (error != 0)
{
signalMask.Restore();
PrintStderr("error: failed writing FinishedRound to \"%s\", error %u.\n",
o.output, error);
goto Finalize;
}
writerRoundStartPos = writer.FilePos();
}
}
}
PrintStderr("info: stopping session (signal %u).\n",
SignalHandled);
error = session.FlushToWriter(writer, &writtenRange);
if (error != 0)
{
PrintStderr("error: failed flushing \"%s\", error %u.\n",
o.output, error);
goto Finalize;
}
auto const writerSessionEndPos = writer.FilePos();
eventBytes = writerSessionEndPos - writerSessionStartPos;
PrintStderrIf(o.verbose, "verbose: flushed %lu bytes.\n",
static_cast<unsigned long>(writerSessionEndPos - writerRoundStartPos));
error = session.SetWriterHeaders(writer, &writtenRange);
if (error != 0)
{
PrintStderr("error: failed collecting system info for \"%s\", error %u.\n",
o.output, error);
goto Finalize;
}
}
Finalize:
{
auto newError = writer.FinalizeAndClose();
if (newError == 0)
{
PrintStderr("info: woke %u times, wrote 0x%lX bytes to \"%s\".\n",
wakeupCount, static_cast<unsigned long>(eventBytes), o.output);
}
else if (error == 0)
{
error = newError;
PrintStderr("error: failed finalizing \"%s\", error %u.\n",
o.output, error);
}
}
Done:
return error;
}
int
main(int argc, char* argv[])
{
int error;
try
{
std::vector<Tracepoint> tracepoints;
Options o;
unsigned const buffersizeMax = 0x80000000 / 1024;
unsigned buffersize = 128u;
unsigned const wakeupMax = 0x80000000 / 1024;
unsigned wakeup = 2u;
bool realtime = true;
bool showHelp = false;
bool usageError = false;
for (int argi = 1; argi < argc; argi += 1)
{
auto const* const arg = argv[argi];
if (arg[0] != '-')
{
PushFrontDef(o, tracepoints, Tracepoint(arg));
}
else if (arg[1] != '-')
{
auto const flags = &arg[1];
for (unsigned flagsPos = 0; flags[flagsPos] != '\0'; flagsPos += 1)
{
auto const flag = flags[flagsPos];
switch (flag)
{
case 'b':
argi += 1;
ArgSize("-b", buffersizeMax, argi, argc, argv, &usageError, &buffersize);
break;
case 'c':
realtime = false;
break;
case 'C':
realtime = true;
break;
case 'i':
argi += 1;
if (argi < argc)
{
PushFrontDefsFromFile(o, tracepoints, argv[argi]);
}
else
{
PrintStderr("error: missing filename for flag -i.\n");
usageError = true;
}
break;
case 'o':
argi += 1;
if (argi < argc)
{
o.output = argv[argi];
}
else
{
PrintStderr("error: missing filename for flag -o.\n");
usageError = true;
}
break;
case 'w':
argi += 1;
ArgSize("-w", wakeupMax, argi, argc, argv, &usageError, &wakeup);
break;
case 'v':
o.verbose = true;
break;
case 'h':
showHelp = true;
break;
default:
PrintStderr("error: invalid flag -%c.\n",
flag);
usageError = true;
break;
}
}
}
else
{
auto const flag = &arg[2];
if (0 == strcmp(flag, "buffersize"))
{
argi += 1;
ArgSize("--buffersize", buffersizeMax, argi, argc, argv, &usageError, &buffersize);
}
else if (0 == strcmp(flag, "circular"))
{
realtime = false;
}
else if (0 == strcmp(flag, "realtime"))
{
realtime = true;
}
else if (0 == strcmp(flag, "input"))
{
argi += 1;
if (argi < argc)
{
PushFrontDefsFromFile(o, tracepoints, argv[argi]);
}
else
{
PrintStderr("error: missing filename for flag --input.\n");
usageError = true;
}
}
else if (0 == strcmp(flag, "output"))
{
argi += 1;
if (argi < argc)
{
o.output = argv[argi];
}
else
{
PrintStderr("error: missing filename for flag --output.\n");
usageError = true;
}
}
else if (0 == strcmp(flag, "wakeup"))
{
argi += 1;
ArgSize("--wakeup", wakeupMax, argi, argc, argv, &usageError, &wakeup);
}
else if (0 == strcmp(flag, "verbose"))
{
o.verbose = true;
}
else if (0 == strcmp(flag, "help"))
{
showHelp = true;
}
else
{
PrintStderr("error: invalid flag \"--%s\".\n",
flag);
usageError = true;
}
}
}
if (showHelp || usageError)
{
fputs(UsageCommon, stdout);
fputs(showHelp ? UsageLong : UsageShort, stdout);
error = EINVAL;
goto Done;
}
else if (tracepoints.empty())
{
PrintStderr("error: no tracepoints specified, exiting.\n");
error = EINVAL;
goto Done;
}
else if (realtime && wakeup >= buffersize)
{
PrintStderr("error: wakeup size %u must be less than buffersize %u.\n",
wakeup, buffersize);
error = EINVAL;
goto Done;
}
auto const mode = realtime
? TracepointSessionMode::RealTime
: TracepointSessionMode::Circular;
TracepointCache cache;
TracepointSession session(
cache,
TracepointSessionOptions(mode, buffersize * 1024)
.WakeupWatermark(wakeup * 1024));
unsigned const enabledCount = EnableTracepoints(o, tracepoints, cache, session);
if (enabledCount == 0)
{
PrintStderr("error: No tracepoints enabled, exiting.\n");
error = ENOENT;
goto Done;
}
switch (mode)
{
case TracepointSessionMode::Circular:
error = CollectCircular(o, session);
break;
case TracepointSessionMode::RealTime:
error = CollectRealtime(o, session);
break;
default:
assert(false);
break;
}
}
catch (std::exception const& ex)
{
PrintStderr("fatal error: %s.\n",
ex.what());
error = ENOMEM;
}
Done:
return error;
}

View file

@ -0,0 +1,28 @@
cmake_minimum_required(VERSION 3.10)
include(../version.cmake)
project(tracepoint-decode-cpp
VERSION ${LINUXTRACEPOINTS_VERSION}
DESCRIPTION "Tracepoint decoding for C/C++"
HOMEPAGE_URL "https://github.com/microsoft/LinuxTracepoints"
LANGUAGES CXX)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
if(WIN32)
add_compile_options(/W4 /WX /permissive-)
else()
add_compile_options(
-Wall
-Wextra
-Wformat
-Wformat-security
-Werror=format-security
-Wstack-protector
-Werror=stack-protector)
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
add_compile_options(-D_FORTIFY_SOURCE=2)
endif()
endif()
add_subdirectory(src)
add_subdirectory(samples)

View file

@ -0,0 +1,11 @@
# libtracepoint-decode-cpp
C++ library for decoding tracepoints and perf.data files.
Works on Linux or Windows.
- **[PerfDataFile.h](include/tracepoint/PerfDataFile.h):**
Splits a `perf.data` file into events.
- **[PerfEventInfo.h](include/tracepoint/PerfEventInfo.h):**
Structures for sample and non-sample events.
- **[PerfEventMetadata.h](include/tracepoint/PerfEventMetadata.h):**
Metadata parsing for ftrace-style tracepoint decoding information.

View file

@ -0,0 +1,139 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#ifndef _included_PerfByteReader_h
#define _included_PerfByteReader_h
#include <stdint.h>
#include <string.h> // memcpy
#ifdef _WIN32
#include <sal.h>
#endif
#ifndef _In_
#define _In_
#endif
#ifndef _In_reads_bytes_
#define _In_reads_bytes_(cb)
#endif
namespace tracepoint_decode
{
// Loads values from the perf event data buffer, handling misaligned
// and byte-swapped values.
class PerfByteReader
{
bool m_bigEndian;
public:
// Initializes a data reader that treats input as host-endian.
//
// Postcondition: this->ByteSwapNeeded() == false.
PerfByteReader() noexcept;
// If bigEndian is true, initializes a data reader that assumes input is
// big-endian. Otherwise, assumes input is little-endian.
//
// Postcondition: this->BigEndian() == bigEndian.
explicit
PerfByteReader(bool bigEndian) noexcept;
// Returns true if this reader is treating its input data as big-endian.
// Returns false if this reader is treating its input data as little-endian.
bool
BigEndian() const noexcept;
// Returns BigEndian() != HOST_IS_BIG_ENDIAN.
bool
ByteSwapNeeded() const noexcept;
// Returns *pSrc as uint8_t.
uint8_t
ReadAsU8(_In_reads_bytes_(1) void const* pSrc) const noexcept;
// Reads 2 bytes from pSrc, performs byteswap if appropriate,
// then returns the result as uint16_t.
uint16_t
ReadAsU16(_In_reads_bytes_(2) void const* pSrc) const noexcept;
// Reads 4 bytes from pSrc, performs byteswap if appropriate,
// then returns the result as uint32_t.
uint32_t
ReadAsU32(_In_reads_bytes_(4) void const* pSrc) const noexcept;
// Reads 8 bytes from pSrc, performs byteswap if appropriate,
// then returns the result as uint64_t.
uint64_t
ReadAsU64(_In_reads_bytes_(8) void const* pSrc) const noexcept;
// Requires: cbSrc is 1, 2, or 4.
// Reads cbSrc bytes from pSrc, performs byteswap if appropriate,
// then returns the result cast to uint32_t.
uint32_t
ReadAsDynU32(_In_reads_bytes_(cbSrc) void const* pSrc, uint8_t cbSrc) const noexcept;
// Requires: cbSrc is 1, 2, 4, or 8.
// Reads cbSrc bytes from pSrc, performs byteswap if appropriate,
// then returns the result cast to uint64_t.
uint64_t
ReadAsDynU64(_In_reads_bytes_(cbSrc) void const* pSrc, uint8_t cbSrc) const noexcept;
// Requires: sizeof(ValType) is 1, 2, 4, or 8.
// Reads sizeof(ValType) bytes from pSrc, performs byteswap if appropriate,
// then returns the result cast to ValType.
// ValType should be a trivial type, e.g. uint32_t, long, or double.
template<class ValType>
ValType
ReadAs(_In_reads_bytes_(sizeof(ValType)) void const* pSrc) const noexcept
{
if constexpr (sizeof(ValType) == sizeof(uint8_t))
{
ValType v;
memcpy(&v, pSrc, sizeof(v));
return v;
}
else if constexpr (sizeof(ValType) == sizeof(uint16_t))
{
auto const uintVal = ReadAsU16(pSrc);
ValType v;
memcpy(&v, &uintVal, sizeof(v));
return v;
}
else if constexpr (sizeof(ValType) == sizeof(uint32_t))
{
auto const uintVal = ReadAsU32(pSrc);
ValType v;
memcpy(&v, &uintVal, sizeof(v));
return v;
}
else if constexpr (sizeof(ValType) == sizeof(uint64_t))
{
auto const uintVal = ReadAsU64(pSrc);
ValType v;
memcpy(&v, &uintVal, sizeof(v));
return v;
}
else
{
static_assert(sizeof(ValType) == 0,
"ReadAs supports values of size 1, 2, 4, and 8.");
}
}
// Requires: sizeof(ValType) is 1, 2, 4, or 8.
// Reads sizeof(ValType) bytes from pSrc, performs byteswap if appropriate,
// then returns the result cast to ValType.
// ValType should be a trivial type, e.g. uint32_t, long, or double.
template<class ValType>
ValType
Read(_In_ ValType const* pSrc) const noexcept
{
return ReadAs<ValType>(pSrc);
}
};
}
// namespace tracepoint_decode
#endif // _included_PerfByteReader_h

View file

@ -0,0 +1,331 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
/*
PerfDataFile class - Reads perf.data files.
*/
#pragma once
#ifndef _included_PerfDataFile_h
#define _included_PerfDataFile_h
#include "PerfByteReader.h"
#include "PerfDataFileDefs.h"
#include "PerfEventSessionInfo.h"
#include <stdint.h>
#include <stdio.h> // FILE
#include <map>
#include <memory>
#include <string_view>
#include <vector>
#ifdef _WIN32
#include <sal.h>
#endif
#ifndef _In_
#define _In_
#endif
#ifndef _In_z_
#define _In_z_
#endif
#ifndef _In_reads_bytes_
#define _In_reads_bytes_(cb)
#endif
#ifndef _Out_
#define _Out_
#endif
#ifndef _Outptr_result_maybenull_
#define _Outptr_result_maybenull_
#endif
#ifndef _Out_writes_bytes_all_
#define _Out_writes_bytes_all_(size)
#endif
#ifndef _Success_
#define _Success_(condition)
#endif
#ifndef _Ret_opt_
#define _Ret_opt_
#endif
namespace tracepoint_decode
{
// Forward declarations from PerfEventInfo.h:
struct PerfSampleEventInfo;
struct PerfNonSampleEventInfo;
// Forward declaration from PerfEventMetadata.h:
class PerfEventMetadata;
/*
PerfDataFile class - Reads perf.data files.
*/
class PerfDataFile
{
struct perf_file_section;
struct perf_pipe_header;
struct perf_file_header;
struct EventDesc : PerfEventDesc
{
std::unique_ptr<perf_event_attr> attrStorage;
std::unique_ptr<uint64_t[]> idsStorage;
};
uint64_t m_filePos;
uint64_t m_fileLen;
uint64_t m_dataBeginFilePos;
uint64_t m_dataEndFilePos;
FILE* m_file;
std::vector<uint8_t> m_eventData;
std::vector<char> m_headers[PERF_HEADER_LAST_FEATURE]; // Stored file-endian.
std::vector<EventDesc> m_eventDescList; // Stored host-endian. Name points into m_headers.
std::map<uint64_t, size_t> m_eventDescById; // Index into m_eventDescList.
PerfEventSessionInfo m_sessionInfo;
PerfByteReader m_byteReader;
int8_t m_sampleIdOffset; // -1 = unset, -2 = no id.
int8_t m_nonSampleIdOffset; // -1 = unset, -2 = no id.
int8_t m_commonTypeOffset; // -1 = unset, -2 = not available.
uint8_t m_commonTypeSize;
bool m_parsedHeaderEventDesc;
// HEADER_TRACING_DATA
bool m_parsedTracingData;
uint8_t m_tracingDataLongSize;
uint32_t m_tracingDataPageSize;
std::string_view m_headerPage; // Points into m_headers.
std::string_view m_headerEvent; // Points into m_headers.
std::vector<std::string_view> m_ftraces; // Points into m_headers.
std::map<uint32_t, PerfEventMetadata> m_metadataById; // Points into m_headers.
std::string_view m_kallsyms; // Points into m_headers.
std::string_view m_printk; // Points into m_headers.
std::string_view m_cmdline; // Points into m_headers.
public:
PerfDataFile(PerfDataFile const&) = delete;
void operator=(PerfDataFile const&) = delete;
~PerfDataFile() noexcept;
PerfDataFile() noexcept;
// Returns true if the currently-opened file is big-endian.
bool
FileBigEndian() const noexcept;
// Returns PerfByteReader(FileBigEndian()).
PerfByteReader
ByteReader() const noexcept;
// Returns the position within the input file of the event that will be
// read by the next call to ReadEvent().
// Returns UINT64_MAX after end-of-file or file error.
uint64_t
FilePos() const noexcept;
// Returns the position within the input file of the first event.
uint64_t
DataBeginFilePos() const noexcept;
// If the input file was recorded in pipe mode, returns UINT64_MAX.
// Otherwise, returns the position within the input file immediately after
// the last event.
uint64_t
DataEndFilePos() const noexcept;
// Returns the number of attribute records available from EventDesc().
uintptr_t
EventDescCount() const noexcept;
// Combined data from perf_file_header::attrs and PERF_RECORD_HEADER_ATTR.
// Requires: eventDescIndex < EventDescCount().
PerfEventDesc const&
EventDesc(uintptr_t eventDescIndex) const noexcept;
// Combined data from perf_file_header::attrs, PERF_RECORD_HEADER_ATTR,
// and HEADER_EVENT_DESC. Returns NULL if sampleId is not known.
_Ret_opt_ PerfEventDesc const*
FindEventDescById(uint64_t sampleId) const noexcept;
// Returns the raw data from the specified header (file-endian, use ByteReader()
// to do byte-swapping as appropriate).
// Returns empty if the requested header was not loaded from the file.
std::string_view
Header(PerfHeaderIndex headerIndex) const noexcept;
// Returns the LongSize parsed from a PERF_HEADER_TRACING_DATA header,
// or 0 if no PERF_HEADER_TRACING_DATA has been parsed.
uint8_t
TracingDataLongSize() const noexcept;
// Returns the PageSize parsed from a PERF_HEADER_TRACING_DATA header,
// or 0 if no PERF_HEADER_TRACING_DATA has been parsed.
uint32_t
TracingDataPageSize() const noexcept;
// Returns the header_page parsed from a PERF_HEADER_TRACING_DATA header,
// or {} if no PERF_HEADER_TRACING_DATA has been parsed.
std::string_view
TracingDataHeaderPage() const noexcept;
// Returns the header_event parsed from a PERF_HEADER_TRACING_DATA header,
// or {} if no PERF_HEADER_TRACING_DATA has been parsed.
std::string_view
TracingDataHeaderEvent() const noexcept;
// Returns the ftraces parsed from a PERF_HEADER_TRACING_DATA header,
// or NULL if no PERF_HEADER_TRACING_DATA has been parsed.
std::string_view const*
TracingDataFtraces() const noexcept;
// Returns the count of ftraces parsed from a PERF_HEADER_TRACING_DATA header,
// or 0 if no PERF_HEADER_TRACING_DATA has been parsed.
uint32_t
TracingDataFtraceCount() const noexcept;
// Returns the kallsyms parsed from a PERF_HEADER_TRACING_DATA header,
// or {} if no PERF_HEADER_TRACING_DATA has been parsed.
std::string_view
TracingDataKallsyms() const noexcept;
// Returns the printk parsed from a PERF_HEADER_TRACING_DATA header,
// or {} if no PERF_HEADER_TRACING_DATA has been parsed.
std::string_view
TracingDataPrintk() const noexcept;
// Returns the saved_cmdline parsed from a PERF_HEADER_TRACING_DATA header,
// or {} if no PERF_HEADER_TRACING_DATA has been parsed.
std::string_view
TracingDataSavedCmdLine() const noexcept;
// Closes the input file, if any.
void
Close() noexcept;
// Closes the current input file (if any), then opens the specified
// perf.data file using fopen and reads the file header.
// If not a pipe-mode file, loads metadata. If a pipe-mode file, metadata
// will be loaded as the metadata events are encountered by ReadEvent.
// On successful return, the file will be positioned before the first event.
_Success_(return == 0) int
Open(_In_z_ char const* filePath) noexcept;
// Closes the current input file (if any), then switches stdin to binary
// mode (Windows-only), then reads the file header from stdin. If stdin is
// not a pipe-mode file, returns an error. Metadata will be loaded as the
// metadata events are encountered by ReadEvent.
// On successful return, the file will be positioned before the first event.
_Success_(return == 0) int
OpenStdin() noexcept;
// Returns the event header (host-endian) followed by the raw data from the
// file (file-endian, use ByteReader() to do byte-swapping as appropriate).
//
// On success, sets *ppEventHeader to the event and returns 0.
// The returned pointer is valid until the next call to ReadEvent.
//
// On end-of-file, sets *ppEventHeader to NULL and returns 0.
//
// On error, sets *ppEventHeader to NULL and returns errno.
//
// Note that for PERF_RECORD_HEADER_TRACING_DATA and PERF_RECORD_AUXTRACE,
// there will be extra data immediately after the event. Use EventDataSize to
// get the actual event size.
_Success_(return == 0) int
ReadEvent(_Outptr_result_maybenull_ perf_event_header const** ppEventHeader) noexcept;
// Given a pEventHeader that was returned from ReadEvent, returns the actual
// size of the specified event.
//
// For most event types, this returns pEventHeader->size.
//
// For PERF_RECORD_HEADER_TRACING_DATA and PERF_RECORD_AUXTRACE, there is
// extra data after the event, and this will return a size that includes
// that extra data.
uint32_t
EventDataSize(perf_event_header const* pEventHeader) noexcept;
// Tries to get event information from the event's prefix. The prefix is
// usually present only for sample events. If the event prefix is not
// present, this function may return an error or it may succeed but return
// incorrect information. In general, only use this on events where
// pEventHeader->type == PERF_RECORD_SAMPLE.
_Success_(return == 0) int
GetSampleEventInfo(
_In_ perf_event_header const* pEventHeader,
_Out_ PerfSampleEventInfo* pInfo) const noexcept;
// Tries to get event information from the event's suffix. The event suffix
// is usually present only for non-sample kernel-generated events.
// If the event suffix is not present, this function may return an error or
// it may succeed but return incorrect information. In general:
// - Only use this on events where pEventHeader->type != PERF_RECORD_SAMPLE
// and pEventHeader->type < PERF_RECORD_USER_TYPE_START.
// - Only use this on events that come after the PERF_RECORD_FINISHED_INIT
// event.
_Success_(return == 0) int
GetNonSampleEventInfo(
_In_ perf_event_header const* pEventHeader,
_Out_ PerfNonSampleEventInfo* pInfo) const noexcept;
private:
_Success_(return == 0) int
LoadAttrs(perf_file_section const& attrs, uint64_t cbAttrAndIdSection64) noexcept;
_Success_(return == 0) int
LoadHeaders(perf_file_section const& data, uint64_t flags) noexcept;
void
ParseTracingData() noexcept;
void
ParseHeaderClockid() noexcept;
void
ParseHeaderClockData() noexcept;
void
ParseHeaderEventDesc() noexcept;
_Success_(return == 0) int
GetSampleEventId(_In_ perf_event_header const* pEventHeader, _Out_ uint64_t* pId) const noexcept;
_Success_(return == 0) int
GetNonSampleEventId(_In_ perf_event_header const* pEventHeader, _Out_ uint64_t* pId) const noexcept;
_Success_(return == 0) int
AddAttr(
std::unique_ptr<perf_event_attr> pAttr,
uint32_t cbAttrCopied,
_In_z_ char const* pName,
_In_reads_bytes_(cbIdsFileEndian) void const* pbIdsFileEndian,
uintptr_t cbIdsFileEndian) noexcept(false);
template<class SizeType>
_Success_(return == 0) int
ReadPostEventData(uint16_t eventSizeFromHeader) noexcept;
bool
EnsureEventDataSize(uint32_t minSize) noexcept;
// Note: leaves filePos at EOF.
_Success_(return == 0) int
UpdateFileLen() noexcept;
bool
SectionValid(perf_file_section const& section) const noexcept;
// Returns 0 (success), EIO (fread error), or EPIPE (eof).
_Success_(return == 0) int
FileRead(_Out_writes_bytes_all_(cb) void* p, uintptr_t cb) noexcept;
_Success_(return == 0) int
FileSeek(uint64_t filePos) noexcept;
// Returns 0 (success), EIO (fread error), EPIPE (eof), or others.
_Success_(return == 0) int
FileSeekAndRead(uint64_t filePos, _Out_writes_bytes_all_(cb) void* p, uintptr_t cb) noexcept;
};
}
// namespace tracepoint_decode
#endif // _included_PerfDataFile_h

View file

@ -0,0 +1,79 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#ifndef _included_PerfDataFileDefs_h
#define _included_PerfDataFileDefs_h
#include <stdint.h>
#ifdef _WIN32
#include <sal.h>
#endif
#ifndef _Field_size_
#define _Field_size_(size)
#endif
#ifndef _Field_z_
#define _Field_z_
#endif
// Forward declarations from PerfEventAbi.h or linux/uapi/linux/perf_event.h:
struct perf_event_attr;
struct perf_event_header;
namespace tracepoint_decode
{
// Forward declaration from PerfEventMetadata.h:
class PerfEventMetadata;
// uint8 header index.
// From: perf.data-file-format.txt, perf/util/header.h.
enum PerfHeaderIndex : uint8_t {
PERF_HEADER_RESERVED = 0, // always cleared
PERF_HEADER_FIRST_FEATURE = 1,
PERF_HEADER_TRACING_DATA = 1,
PERF_HEADER_BUILD_ID,
PERF_HEADER_HOSTNAME,
PERF_HEADER_OSRELEASE,
PERF_HEADER_VERSION,
PERF_HEADER_ARCH,
PERF_HEADER_NRCPUS,
PERF_HEADER_CPUDESC,
PERF_HEADER_CPUID,
PERF_HEADER_TOTAL_MEM,
PERF_HEADER_CMDLINE,
PERF_HEADER_EVENT_DESC,
PERF_HEADER_CPU_TOPOLOGY,
PERF_HEADER_NUMA_TOPOLOGY,
PERF_HEADER_BRANCH_STACK,
PERF_HEADER_PMU_MAPPINGS,
PERF_HEADER_GROUP_DESC,
PERF_HEADER_AUXTRACE,
PERF_HEADER_STAT,
PERF_HEADER_CACHE,
PERF_HEADER_SAMPLE_TIME,
PERF_HEADER_MEM_TOPOLOGY,
PERF_HEADER_CLOCKID,
PERF_HEADER_DIR_FORMAT,
PERF_HEADER_BPF_PROG_INFO,
PERF_HEADER_BPF_BTF,
PERF_HEADER_COMPRESSED,
PERF_HEADER_CPU_PMU_CAPS,
PERF_HEADER_CLOCK_DATA,
PERF_HEADER_HYBRID_TOPOLOGY,
PERF_HEADER_PMU_CAPS,
PERF_HEADER_LAST_FEATURE,
};
struct PerfEventDesc
{
perf_event_attr const* attr; // NULL for unknown id.
_Field_z_ char const* name; // "" if no name available, e.g. if no PERF_HEADER_EVENT_DESC header.
PerfEventMetadata const* metadata; // NULL if no metadata available.
_Field_size_(ids_count) uint64_t const* ids; // The sample_ids that share this descriptor.
uint32_t ids_count;
};
}
// namespace tracepoint_decode
#endif // _included_PerfDataFileDefs_h

View file

@ -0,0 +1,347 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#ifndef _included_PerfDataFileWriter_h
#define _included_PerfDataFileWriter_h
#include "PerfDataFileDefs.h"
#include <stdint.h>
#include <map>
#include <memory>
#include <string_view>
#include <vector>
#ifdef _WIN32
#include <sal.h>
#endif
#ifndef _In_
#define _In_
#endif
#ifndef _In_z_
#define _In_z_
#endif
#ifndef _In_reads_
#define _In_reads_(count)
#endif
#ifndef _In_reads_bytes_
#define _In_reads_bytes_(cb)
#endif
#ifndef _Out_
#define _Out_
#endif
#ifndef _Outptr_result_maybenull_
#define _Outptr_result_maybenull_
#endif
#ifndef _Out_writes_bytes_all_
#define _Out_writes_bytes_all_(size)
#endif
#ifndef _Field_z_
#define _Field_z_
#endif
#ifndef _Field_size_bytes_
#define _Field_size_bytes_(size)
#endif
#ifndef _Success_
#define _Success_(condition)
#endif
#ifndef _WIN32
// Forward declaration from sys/uio.h:
struct iovec;
// Forward declaration from sys/utsname.h:
struct utsname;
#endif // !_WIN32
namespace tracepoint_decode
{
// Forward declaration from PerfEventSessionInfo.h:
class PerfEventSessionInfo;
/*
PerfDataFileWriter class - Writes perf.data files.
- Construct a writer: PerfDataFileWriter writer;
- Open the file: writer.Create(filename);
- This writes headers and positions the file pointer for event data.
- Do the following (in any order):
- Call WriteEventData to write event data to the file.
- Call AddTracepointEventDesc() to provide event information for events
with tracefs format information.
- Call AddEventDesc() to provide event information for events that don't
have tracefs format information.
- Call SetHeader() to provide data for other headers in the file.
- Close the file: writer.FinalizeAndClose();
- This writes the file footers, finalizes the headers, then closes the file.
*/
class PerfDataFileWriter
{
struct perf_file_section;
struct perf_file_header;
struct EventDesc;
struct TracepointInfo;
uint64_t m_filePos;
int m_file;
std::vector<EventDesc> m_eventDescs;
std::map<uint32_t, TracepointInfo> m_tracepointInfoByCommonType;
std::vector<char> m_headers[PERF_HEADER_LAST_FEATURE];
uint32_t m_tracingDataPageSize;
uint8_t m_tracingDataLongSize;
std::vector<char> m_tracingDataHeaderPage;
std::vector<char> m_tracingDataHeaderEvent;
std::vector<std::vector<char>> m_tracingDataFtraces;
std::vector<char> m_tracingDataKallsyms;
std::vector<char> m_tracingDataPrintk;
std::vector<char> m_tracingDataSavedCmdline;
public:
PerfDataFileWriter(PerfDataFileWriter const&) = delete;
void operator=(PerfDataFileWriter const&) = delete;
// Calls CloseNoFinalize.
~PerfDataFileWriter() noexcept;
// May throw bad_alloc.
PerfDataFileWriter() noexcept(false);
// Immediately closes the current output file (if any).
// Does not finalize headers - resulting file will not be usable.
void
CloseNoFinalize() noexcept;
// Writes footer, finalizes header, and closes the output file.
// On error, closes the output file and returns errno.
_Success_(return == 0) int
FinalizeAndClose() noexcept;
// Calls CloseNoFinalize() to close any previous output file, then creates a new
// file using open(filePath, O_CREAT|O_WRONLY|O_TRUNC|O_CLOEXEC, mode) and
// writes the file header. On error, closes the output file and returns errno.
_Success_(return == 0) int
Create(_In_z_ char const* filePath, int mode = -1) noexcept;
// Returns the file offset at which the next call to WriteEventData()
// will begin writing. Returns -1 if file is closed.
uint64_t
FilePos() const noexcept;
// Adds a block of event data to the output file.
// Data should be a sequence of perf_event_header blocks, i.e. a
// perf_event_header, then data, then another perf_event_header, etc.
//
// On success, returns 0. On error, returns errno, in which case file state is
// unspecified (may have written some but not all of the data to the file).
//
// Notes:
// - The content of the data is written directly to the event data section of
// the output file without any validation.
// - Every perf_event_header block's size should be a multiple of 8.
// - dataSize should almost always be the sum of hdr.size for all headers written,
// except for PERF_RECORD_HEADER_TRACING_DATA and PERF_RECORD_AUXTRACE which may
// have additional data in the block beyond the size indicated in the header.
// - The trace file will be invalid if any events are written with an id
// field that does not have a corresponding entry in the EventDesc table. You
// need to provide that information by calling AddTracepointEventDesc(...) or
// AddEventDesc(...).
_Success_(return == 0) int
WriteEventData(
_In_reads_bytes_(dataSize) void const* data,
size_t dataSize) noexcept;
#ifndef _WIN32
// Advanced: Adds blocks of event data to the output file.
// Similar to WriteEventData, but accepts multiple blocks of data and returns
// the number of bytes written instead of errno.
//
// On error, returns -1. Check errno for error code.
// On success, returns number of bytes written. In rare cases, may succeed with a
// result less than dataSize (see writev(2) documentation).
_Success_(return >= 0) ptrdiff_t
WriteEventDataIovecs(
_In_reads_(iovecsCount) struct iovec const* iovecs,
int iovecsCount) noexcept;
#endif // !_WIN32
// Adds a PERF_RECORD_FINISHED_INIT record to the output file. This should be
// called after all "initial system state" data has been written to the file,
// e.g. non-sample events like PERF_RECORD_MMAP, PERF_RECORD_COMM,
// PERF_RECORD_ID_INDEX, PERF_RECORD_THREAD_MAP, PERF_RECORD_CPU_MAP.
_Success_(return == 0) int
WriteFinishedInit() noexcept;
// Adds a PERF_RECORD_FINISHED_ROUND record to the output file. This should be
// called each time you completely flush all buffers. This indicates that no
// events older than this point will be written to the file after this point.
_Success_(return == 0) int
WriteFinishedRound() noexcept;
// Returns the number of bytes set for the specified header.
size_t
GetHeaderSize(PerfHeaderIndex index) const noexcept;
// Returns a pointer to the data set for the specified header.
void const*
GetHeaderData(PerfHeaderIndex index) const noexcept;
// Directly sets or resets the data for the specified header.
//
// Note that the PerfDataFileWriter class has special support for the
// following headers:
//
// - If no data has been set via SetHeader(PERF_HEADER_TRACING_DATA, ...) then
// CloseNoFinalize() will synthesize a PERF_HEADER_TRACING_DATA header using
// data supplied via AddTracepointEventDesc(...) and SetTracingData(...).
// - If no data has been set via SetHeader(PERF_HEADER_EVENT_DESC, ...) then
// CloseNoFinalize() will synthesize a PERF_HEADER_EVENT_DESC header using
// data supplied via AddTracepointEventDesc(...) and AddEventDesc(...).
_Success_(return == 0) int
SetHeader(
PerfHeaderIndex index,
_In_reads_bytes_(dataSize) void const* data,
size_t dataSize) noexcept;
// Sets or resets the data for the specified perf_header_string header.
// Use this for headers where the header value is a perf_header_string, e.g.
// HOSTNAME, OSRELEASE, VERSION, ARCH, CPUDESC, CPUID, CMDLINE.
_Success_(return == 0) int
SetStringHeader(
PerfHeaderIndex index,
_In_z_ char const* str) noexcept;
// Sets the data for the NRCPUS header.
_Success_(return == 0) int
SetNrCpusHeader(uint32_t available, uint32_t online) noexcept;
// Sets the data for the SAMPLE_TIME header.
_Success_(return == 0) int
SetSampleTimeHeader(uint64_t first, uint64_t last) noexcept;
// Sets the data for the CLOCKID header.
_Success_(return == 0) int
SetClockidHeader(uint32_t clockid) noexcept;
// Sets the data for the CLOCK_DATA header.
_Success_(return == 0) int
SetClockDataHeader(uint32_t clockid, uint64_t wallClockNS, uint64_t clockidTimeNS) noexcept;
// Sets or resets the data for headers available in the specified sessionInfo:
// - CLOCKID: Set based on Clockid(); cleared if Clockid() == 0xFFFFFFFF.
// - CLOCK_DATA: Set based on GetClockOffset(); cleared if !ClockOffsetKnown().
_Success_(return == 0) int
SetSessionInfoHeaders(PerfEventSessionInfo const& sessionInfo) noexcept;
#ifndef _WIN32
// Sets or resets the data for the HOSTNAME, OSRELEASE, and ARCH headers.
_Success_(return == 0) int
SetUtsNameHeaders(utsname const& uts) noexcept;
#endif // !_WIN32
// Configures information to be included in the synthesized
// PERF_HEADER_TRACING_DATA header. These settings are given default values
// when the PerfDataFileWriter is constructed. These settings are used by
// CloseNoFinalize() if no data was provided via
// SetHeader(PERF_HEADER_TRACING_DATA, ...).
//
// For all of the parameters, a 0 or {NULL, 0} value indicates "keep the
// existing value". To indicate "set the value to empty", use {non-null, 0}.
//
// - longSize: Default is sizeof(size_t).
// - pageSize: Default is sysconf(_SC_PAGESIZE).
// - headerPage: Default is timestamp64+commit64+overwrite8+data4080. Empty means use default.
// - headerEvent: Default is type_len:5, time_delta:27, array:32. Empty means use default.
// - ftraces: Default is "".
// - kallsyms: Default is "".
// - printk: Default is "".
// - savedCmdLine: Default is "".
_Success_(return == 0) int
SetTracingData(
uint8_t longSize,
uint32_t pageSize,
std::string_view headerPage,
std::string_view headerEvent,
_In_reads_(ftraceCount) std::string_view const* ftraces,
uint32_t ftraceCount,
std::string_view kallsyms,
std::string_view printk,
std::string_view savedCmdLine) noexcept;
// Adds perf_event_attr and name information for the specified event ids.
// Use this for events that do NOT have tracefs format information, i.e.
// when desc.metadata == NULL.
//
// Requires: desc.attr != NULL, desc.name != NULL.
//
// Returns 0 for success, errno for error.
// Returns E2BIG if desc.name is 64KB or longer.
//
// Note that each id used in the trace should map to exactly one attr provided
// by AddTracepointEventDesc or AddEventDesc, but this is not validated by
// PerfDataFileWriter. For example, if the same id is provided in two different
// calls to AddEventDesc, the resulting file may not decode properly.
_Success_(return == 0) int
AddEventDesc(PerfEventDesc const& desc) noexcept;
// Returns true if there has been a successful call to
// AddTracepointEventDesc(desc) where desc.metadata->Id() == common_type.
bool
HasTracepointEventDesc(uint32_t common_type) const noexcept;
// Adds perf_event_attr, name, and metadata for the specified event ids.
// Use this for events that DO have tracefs format information, i.e.
// when desc.metadata != NULL.
//
// Requires: desc.attr != NULL, desc.name != NULL, desc.metadata != NULL.
// Also, desc.metadata is copied by reference (shallow copy).
//
// Returns 0 for success, errno for error.
// Returns E2BIG if desc.name is 64KB or longer.
// Returns EEXIST if metadata has already been set for the common_type
// indicated in desc.metadata->Id().
//
// Note that each id used in the trace should map to exactly one attr provided
// by AddTracepointEventDesc or AddEventDesc, but this is not validated by
// PerfDataFileWriter. For example, if the same id is provided in two different
// calls to AddEventDesc, the resulting file may not decode properly.
_Success_(return == 0) int
AddTracepointEventDesc(PerfEventDesc const& desc) noexcept;
private:
bool
ValidFilePos() const noexcept;
_Success_(return == 0) int
WriteData(
_In_reads_bytes_(dataSize) void const* data,
size_t dataSize) noexcept;
void
SynthesizeTracingData();
void
SynthesizeEventDesc();
// Writes the perf_file_sections for m_headers,
// then writes the data from m_headers.
_Success_(return == 0) int
WriteHeaders(_Out_ uint64_t* pFlags0) noexcept;
// Writes the attr+idSection for each attr.
// Then writes the id data.
_Success_(return == 0) int
WriteAttrs(_Out_ perf_file_section* pAttrsSection) noexcept;
};
}
// namespace tracepoint_decode
#endif // _included_PerfDataFileWriter_h

View file

@ -0,0 +1,774 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// Adapted from linux/uapi/linux/perf_event.h.
#pragma once
#ifndef _included_PerfEventAbi_h
#define _included_PerfEventAbi_h
#include <stdint.h>
#ifdef _WIN32
#include <sal.h>
#endif
#ifndef _Ret_z_
#define _Ret_z_
#endif
#ifndef _Pre_cap_
#define _Pre_cap_(c)
#endif
#ifndef _ltpDecl
#ifdef _WIN32
#define _ltpDecl __cdecl
#else
#define _ltpDecl
#endif
#endif
// uint32 value for perf_event_attr::type.
enum perf_type_id : uint32_t {
PERF_TYPE_HARDWARE = 0,
PERF_TYPE_SOFTWARE = 1,
PERF_TYPE_TRACEPOINT = 2,
PERF_TYPE_HW_CACHE = 3,
PERF_TYPE_RAW = 4,
PERF_TYPE_BREAKPOINT = 5,
PERF_TYPE_MAX, // non-ABI
};
// uint32 value for perf_event_attr::size.
enum perf_event_attr_size : uint32_t
{
PERF_ATTR_SIZE_VER0 = 64, // first published struct
PERF_ATTR_SIZE_VER1 = 72, // add: config2
PERF_ATTR_SIZE_VER2 = 80, // add: branch_sample_type
PERF_ATTR_SIZE_VER3 = 96, // add: sample_regs_user, sample_stack_user
PERF_ATTR_SIZE_VER4 = 104, // add: sample_regs_intr
PERF_ATTR_SIZE_VER5 = 112, // add: aux_watermark
PERF_ATTR_SIZE_VER6 = 120, // add: aux_sample_size
PERF_ATTR_SIZE_VER7 = 128, // add: sig_data
};
// bits that can be set in perf_event_attr::sample_type.
enum perf_event_sample_format {
PERF_SAMPLE_IP = 1U << 0,
PERF_SAMPLE_TID = 1U << 1,
PERF_SAMPLE_TIME = 1U << 2,
PERF_SAMPLE_ADDR = 1U << 3,
PERF_SAMPLE_READ = 1U << 4,
PERF_SAMPLE_CALLCHAIN = 1U << 5,
PERF_SAMPLE_ID = 1U << 6,
PERF_SAMPLE_CPU = 1U << 7,
PERF_SAMPLE_PERIOD = 1U << 8,
PERF_SAMPLE_STREAM_ID = 1U << 9,
PERF_SAMPLE_RAW = 1U << 10,
PERF_SAMPLE_BRANCH_STACK = 1U << 11,
PERF_SAMPLE_REGS_USER = 1U << 12,
PERF_SAMPLE_STACK_USER = 1U << 13,
PERF_SAMPLE_WEIGHT = 1U << 14,
PERF_SAMPLE_DATA_SRC = 1U << 15,
PERF_SAMPLE_IDENTIFIER = 1U << 16,
PERF_SAMPLE_TRANSACTION = 1U << 17,
PERF_SAMPLE_REGS_INTR = 1U << 18,
PERF_SAMPLE_PHYS_ADDR = 1U << 19,
PERF_SAMPLE_AUX = 1U << 20,
PERF_SAMPLE_CGROUP = 1U << 21,
PERF_SAMPLE_DATA_PAGE_SIZE = 1U << 22,
PERF_SAMPLE_CODE_PAGE_SIZE = 1U << 23,
PERF_SAMPLE_WEIGHT_STRUCT = 1U << 24,
PERF_SAMPLE_MAX = 1U << 25, // non-ABI
PERF_SAMPLE_WEIGHT_TYPE = PERF_SAMPLE_WEIGHT | PERF_SAMPLE_WEIGHT_STRUCT,
};
// bits that can be set in perf_event_attr::read_format.
enum perf_event_read_format {
PERF_FORMAT_TOTAL_TIME_ENABLED = 1U << 0,
PERF_FORMAT_TOTAL_TIME_RUNNING = 1U << 1,
PERF_FORMAT_ID = 1U << 2,
PERF_FORMAT_GROUP = 1U << 3,
PERF_FORMAT_LOST = 1U << 4,
PERF_FORMAT_MAX = 1U << 5, // non-ABI
};
// Event's collection parameters.
struct perf_event_attr {
perf_type_id type; // Major type: hardware/software/tracepoint/etc.
perf_event_attr_size size; // Size of the attr structure, for fwd/bwd compat.
uint64_t config; // Type-specific configuration information.
union {
uint64_t sample_period;
uint64_t sample_freq;
};
uint64_t sample_type; // perf_event_sample_format
uint64_t read_format; // perf_event_read_format
uint64_t disabled : 1; // off by default
uint64_t inherit : 1; // children inherit it
uint64_t pinned : 1; // must always be on PMU
uint64_t exclusive : 1; // only group on PMU
uint64_t exclude_user : 1; // don't count user
uint64_t exclude_kernel : 1; // ditto kernel
uint64_t exclude_hv : 1; // ditto hypervisor
uint64_t exclude_idle : 1; // don't count when idle
uint64_t mmap : 1; // include mmap data
uint64_t comm : 1; // include comm data
uint64_t freq : 1; // use freq, not period
uint64_t inherit_stat : 1; // per task counts
uint64_t enable_on_exec : 1; // next exec enables
uint64_t task : 1; // trace fork/exit
uint64_t watermark : 1; // wakeup_watermark
// skid constraint:
// 0 - SAMPLE_IP can have arbitrary skid
// 1 - SAMPLE_IP must have constant skid
// 2 - SAMPLE_IP requested to have 0 skid
// 3 - SAMPLE_IP must have 0 skid
// See also PERF_RECORD_MISC_EXACT_IP
uint64_t precise_ip : 2;
uint64_t mmap_data : 1; // non-exec mmap data
uint64_t sample_id_all : 1; // sample_type all events
uint64_t exclude_host : 1; // don't count in host
uint64_t exclude_guest : 1; // don't count in guest
uint64_t exclude_callchain_kernel : 1; // exclude kernel callchains
uint64_t exclude_callchain_user : 1; // exclude user callchains
uint64_t mmap2 : 1; // include mmap with inode data
uint64_t comm_exec : 1; // flag comm events that are due to an exec
uint64_t use_clockid : 1; // use @clockid for time fields
uint64_t context_switch : 1; // context switch data
uint64_t write_backward : 1; // Write ring buffer from end to beginning
uint64_t namespaces : 1; // include namespaces data
uint64_t ksymbol : 1; // include ksymbol events
uint64_t bpf_event : 1; // include bpf events
uint64_t aux_output : 1; // generate AUX records instead of events
uint64_t cgroup : 1; // include cgroup events
uint64_t text_poke : 1; // include text poke events
uint64_t build_id : 1; // use build id in mmap2 events
uint64_t inherit_thread : 1; // children only inherit if cloned with CLONE_THREAD
uint64_t remove_on_exec : 1; // event is removed from task on exec
uint64_t sigtrap : 1; // send synchronous SIGTRAP on event
uint64_t reserved1 : 26;
union {
uint32_t wakeup_events; // wakeup every n events
uint32_t wakeup_watermark; // bytes before wakeup
};
uint32_t bp_type;
union {
uint64_t bp_addr;
uint64_t kprobe_func; // for perf_kprobe
uint64_t uprobe_path; // for perf_uprobe
uint64_t config1; // extension of config
};
union {
uint64_t bp_len;
uint64_t kprobe_addr; // when kprobe_func == NULL
uint64_t probe_offset; // for perf_[k,u]probe
uint64_t config2; // extension of config1
};
uint64_t branch_sample_type; // enum perf_branch_sample_type
// Defines set of user regs to dump on samples.
// See asm/perf_regs.h for details.
uint64_t sample_regs_user;
// Defines size of the user stack to dump on samples.
uint32_t sample_stack_user;
int32_t clockid;
// Defines set of regs to dump for each sample state captured on:
// - precise = 0: PMU interrupt
// - precise > 0: sampled instruction
// See asm/perf_regs.h for details.
uint64_t sample_regs_intr;
// Wakeup watermark for AUX area
uint32_t aux_watermark;
uint16_t sample_max_stack;
uint16_t reserved2;
uint32_t aux_sample_size;
uint32_t reserved3;
// User provided data if sigtrap=1, passed back to user via
// siginfo_t::si_perf_data, e.g. to permit user to identify the event.
// Note, siginfo_t::si_perf_data is long-sized, and sig_data will be
// truncated accordingly on 32 bit architectures.
uint64_t sig_data;
// Reverse the endian order of all fields in this struct.
void ByteSwap() noexcept;
};
static_assert(sizeof(perf_event_attr) == PERF_ATTR_SIZE_VER7, "Bad perf_event_attr");
// uint32 value for perf_event_header::type.
enum perf_event_type : uint32_t {
/*
* If perf_event_attr.sample_id_all is set then all event types will
* have the sample_type selected fields related to where/when
* (identity) an event took place (TID, TIME, ID, STREAM_ID, CPU,
* IDENTIFIER) described in PERF_RECORD_SAMPLE below, it will be stashed
* just after the perf_event_header and the fields already present for
* the existing fields, i.e. at the end of the payload. That way a newer
* perf.data file will be supported by older perf tools, with these new
* optional fields being ignored.
*
* struct sample_id {
* { u32 pid, tid; } && PERF_SAMPLE_TID
* { u64 time; } && PERF_SAMPLE_TIME
* { u64 id; } && PERF_SAMPLE_ID
* { u64 stream_id;} && PERF_SAMPLE_STREAM_ID
* { u32 cpu, res; } && PERF_SAMPLE_CPU
* { u64 id; } && PERF_SAMPLE_IDENTIFIER
* } && perf_event_attr::sample_id_all
*
* Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID. The
* advantage of PERF_SAMPLE_IDENTIFIER is that its position is fixed
* relative to header.size.
*/
/*
* The MMAP events record the PROT_EXEC mappings so that we can
* correlate userspace IPs to code. They have the following structure:
*
* struct {
* struct perf_event_header header;
*
* u32 pid, tid;
* u64 addr;
* u64 len;
* u64 pgoff;
* char filename[];
* struct sample_id sample_id;
* };
*/
PERF_RECORD_MMAP = 1,
/*
* struct {
* struct perf_event_header header;
* u64 id;
* u64 lost;
* struct sample_id sample_id;
* };
*/
PERF_RECORD_LOST = 2,
/*
* struct {
* struct perf_event_header header;
*
* u32 pid, tid;
* char comm[];
* struct sample_id sample_id;
* };
*/
PERF_RECORD_COMM = 3,
/*
* struct {
* struct perf_event_header header;
* u32 pid, ppid;
* u32 tid, ptid;
* u64 time;
* struct sample_id sample_id;
* };
*/
PERF_RECORD_EXIT = 4,
/*
* struct {
* struct perf_event_header header;
* u64 time;
* u64 id;
* u64 stream_id;
* struct sample_id sample_id;
* };
*/
PERF_RECORD_THROTTLE = 5,
PERF_RECORD_UNTHROTTLE = 6,
/*
* struct {
* struct perf_event_header header;
* u32 pid, ppid;
* u32 tid, ptid;
* u64 time;
* struct sample_id sample_id;
* };
*/
PERF_RECORD_FORK = 7,
/*
* struct {
* struct perf_event_header header;
* u32 pid, tid;
*
* struct read_format values;
* struct sample_id sample_id;
* };
*/
PERF_RECORD_READ = 8,
/*
* struct {
* struct perf_event_header header;
*
* #
* # Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID.
* # The advantage of PERF_SAMPLE_IDENTIFIER is that its position
* # is fixed relative to header.
* #
*
* { u64 id; } && PERF_SAMPLE_IDENTIFIER
* { u64 ip; } && PERF_SAMPLE_IP
* { u32 pid, tid; } && PERF_SAMPLE_TID
* { u64 time; } && PERF_SAMPLE_TIME
* { u64 addr; } && PERF_SAMPLE_ADDR
* { u64 id; } && PERF_SAMPLE_ID
* { u64 stream_id;} && PERF_SAMPLE_STREAM_ID
* { u32 cpu, res; } && PERF_SAMPLE_CPU
* { u64 period; } && PERF_SAMPLE_PERIOD
*
* { struct read_format values; } && PERF_SAMPLE_READ
*
* { u64 nr,
* u64 ips[nr]; } && PERF_SAMPLE_CALLCHAIN
*
* #
* # The RAW record below is opaque data wrt the ABI
* #
* # That is, the ABI doesn't make any promises wrt to
* # the stability of its content, it may vary depending
* # on event, hardware, kernel version and phase of
* # the moon.
* #
* # In other words, PERF_SAMPLE_RAW contents are not an ABI.
* #
*
* { u32 size;
* char data[size];}&& PERF_SAMPLE_RAW
*
* { u64 nr;
* { u64 hw_idx; } && PERF_SAMPLE_BRANCH_HW_INDEX
* { u64 from, to, flags } lbr[nr];
* } && PERF_SAMPLE_BRANCH_STACK
*
* { u64 abi; # enum perf_sample_regs_abi
* u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER
*
* { u64 size;
* char data[size];
* u64 dyn_size; } && PERF_SAMPLE_STACK_USER
*
* { union perf_sample_weight
* {
* u64 full; && PERF_SAMPLE_WEIGHT
* #if defined(__LITTLE_ENDIAN_BITFIELD)
* struct {
* u32 var1_dw;
* u16 var2_w;
* u16 var3_w;
* } && PERF_SAMPLE_WEIGHT_STRUCT
* #elif defined(__BIG_ENDIAN_BITFIELD)
* struct {
* u16 var3_w;
* u16 var2_w;
* u32 var1_dw;
* } && PERF_SAMPLE_WEIGHT_STRUCT
* #endif
* }
* }
* { u64 data_src; } && PERF_SAMPLE_DATA_SRC
* { u64 transaction; } && PERF_SAMPLE_TRANSACTION
* { u64 abi; # enum perf_sample_regs_abi
* u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_INTR
* { u64 phys_addr;} && PERF_SAMPLE_PHYS_ADDR
* { u64 size;
* char data[size]; } && PERF_SAMPLE_AUX
* { u64 data_page_size;} && PERF_SAMPLE_DATA_PAGE_SIZE
* { u64 code_page_size;} && PERF_SAMPLE_CODE_PAGE_SIZE
* };
*/
PERF_RECORD_SAMPLE = 9,
/*
* The MMAP2 records are an augmented version of MMAP, they add
* maj, min, ino numbers to be used to uniquely identify each mapping
*
* struct {
* struct perf_event_header header;
*
* u32 pid, tid;
* u64 addr;
* u64 len;
* u64 pgoff;
* union {
* struct {
* u32 maj;
* u32 min;
* u64 ino;
* u64 ino_generation;
* };
* struct {
* u8 build_id_size;
* u8 __reserved_1;
* u16 __reserved_2;
* u8 build_id[20];
* };
* };
* u32 prot, flags;
* char filename[];
* struct sample_id sample_id;
* };
*/
PERF_RECORD_MMAP2 = 10,
/*
* Records that new data landed in the AUX buffer part.
*
* struct {
* struct perf_event_header header;
*
* u64 aux_offset;
* u64 aux_size;
* u64 flags;
* struct sample_id sample_id;
* };
*/
PERF_RECORD_AUX = 11,
/*
* Indicates that instruction trace has started
*
* struct {
* struct perf_event_header header;
* u32 pid;
* u32 tid;
* struct sample_id sample_id;
* };
*/
PERF_RECORD_ITRACE_START = 12,
/*
* Records the dropped/lost sample number.
*
* struct {
* struct perf_event_header header;
*
* u64 lost;
* struct sample_id sample_id;
* };
*/
PERF_RECORD_LOST_SAMPLES = 13,
/*
* Records a context switch in or out (flagged by
* PERF_RECORD_MISC_SWITCH_OUT). See also
* PERF_RECORD_SWITCH_CPU_WIDE.
*
* struct {
* struct perf_event_header header;
* struct sample_id sample_id;
* };
*/
PERF_RECORD_SWITCH = 14,
/*
* CPU-wide version of PERF_RECORD_SWITCH with next_prev_pid and
* next_prev_tid that are the next (switching out) or previous
* (switching in) pid/tid.
*
* struct {
* struct perf_event_header header;
* u32 next_prev_pid;
* u32 next_prev_tid;
* struct sample_id sample_id;
* };
*/
PERF_RECORD_SWITCH_CPU_WIDE = 15,
/*
* struct {
* struct perf_event_header header;
* u32 pid;
* u32 tid;
* u64 nr_namespaces;
* { u64 dev, inode; } [nr_namespaces];
* struct sample_id sample_id;
* };
*/
PERF_RECORD_NAMESPACES = 16,
/*
* Record ksymbol register/unregister events:
*
* struct {
* struct perf_event_header header;
* u64 addr;
* u32 len;
* u16 ksym_type;
* u16 flags;
* char name[];
* struct sample_id sample_id;
* };
*/
PERF_RECORD_KSYMBOL = 17,
/*
* Record bpf events:
* enum perf_bpf_event_type {
* PERF_BPF_EVENT_UNKNOWN = 0,
* PERF_BPF_EVENT_PROG_LOAD = 1,
* PERF_BPF_EVENT_PROG_UNLOAD = 2,
* };
*
* struct {
* struct perf_event_header header;
* u16 type;
* u16 flags;
* u32 id;
* u8 tag[BPF_TAG_SIZE];
* struct sample_id sample_id;
* };
*/
PERF_RECORD_BPF_EVENT = 18,
/*
* struct {
* struct perf_event_header header;
* u64 id;
* char path[];
* struct sample_id sample_id;
* };
*/
PERF_RECORD_CGROUP = 19,
/*
* Records changes to kernel text i.e. self-modified code. 'old_len' is
* the number of old bytes, 'new_len' is the number of new bytes. Either
* 'old_len' or 'new_len' may be zero to indicate, for example, the
* addition or removal of a trampoline. 'bytes' contains the old bytes
* followed immediately by the new bytes.
*
* struct {
* struct perf_event_header header;
* u64 addr;
* u16 old_len;
* u16 new_len;
* u8 bytes[];
* struct sample_id sample_id;
* };
*/
PERF_RECORD_TEXT_POKE = 20,
/*
* Data written to the AUX area by hardware due to aux_output, may need
* to be matched to the event by an architecture-specific hardware ID.
* This records the hardware ID, but requires sample_id to provide the
* event ID. e.g. Intel PT uses this record to disambiguate PEBS-via-PT
* records from multiple events.
*
* struct {
* struct perf_event_header header;
* u64 hw_id;
* struct sample_id sample_id;
* };
*/
PERF_RECORD_AUX_OUTPUT_HW_ID = 21,
PERF_RECORD_MAX, // non-ABI
PERF_RECORD_USER_TYPE_START = 64,
/*
* struct attr_event {
* struct perf_event_header header;
* struct perf_event_attr attr;
* uint64_t id[];
* };
*/
PERF_RECORD_HEADER_ATTR = 64,
/* deprecated
*
* #define MAX_EVENT_NAME 64
*
* struct perf_trace_event_type {
* uint64_t event_id;
* char name[MAX_EVENT_NAME];
* };
*
* struct event_type_event {
* struct perf_event_header header;
* struct perf_trace_event_type event_type;
* };
*/
PERF_RECORD_HEADER_EVENT_TYPE = 65,
/* Describe me
*
* struct tracing_data_event {
* struct perf_event_header header;
* uint32_t size;
* };
*/
PERF_RECORD_HEADER_TRACING_DATA = 66,
/* Define a ELF build ID for a referenced executable. */
PERF_RECORD_HEADER_BUILD_ID = 67,
/* No event reordering over this header. No payload. */
PERF_RECORD_FINISHED_ROUND = 68,
/*
* Map event ids to CPUs and TIDs.
*
* struct id_index_entry {
* uint64_t id;
* uint64_t idx;
* uint64_t cpu;
* uint64_t tid;
* };
*
* struct id_index_event {
* struct perf_event_header header;
* uint64_t nr;
* struct id_index_entry entries[nr];
* };
*/
PERF_RECORD_ID_INDEX = 69,
/*
* Auxtrace type specific information. Describe me
*
* struct auxtrace_info_event {
* struct perf_event_header header;
* uint32_t type;
* uint32_t reserved__; // For alignment
* uint64_t priv[];
* };
*/
PERF_RECORD_AUXTRACE_INFO = 70,
/*
* Defines auxtrace data. Followed by the actual data. The contents of
* the auxtrace data is dependent on the event and the CPU. For example
* for Intel Processor Trace it contains Processor Trace data generated
* by the CPU.
*
* struct auxtrace_event {
* struct perf_event_header header;
* uint64_t size;
* uint64_t offset;
* uint64_t reference;
* uint32_t idx;
* uint32_t tid;
* uint32_t cpu;
* uint32_t reserved__; // For alignment
* };
*
* struct aux_event {
* struct perf_event_header header;
* uint64_t aux_offset;
* uint64_t aux_size;
* uint64_t flags;
* };
*/
PERF_RECORD_AUXTRACE = 71,
/*
* Describes an error in hardware tracing
*
* enum auxtrace_error_type {
* PERF_AUXTRACE_ERROR_ITRACE = 1,
* PERF_AUXTRACE_ERROR_MAX
* };
*
* #define MAX_AUXTRACE_ERROR_MSG 64
*
* struct auxtrace_error_event {
* struct perf_event_header header;
* uint32_t type;
* uint32_t code;
* uint32_t cpu;
* uint32_t pid;
* uint32_t tid;
* uint32_t reserved__; // For alignment
* uint64_t ip;
* char msg[MAX_AUXTRACE_ERROR_MSG];
* };
*/
PERF_RECORD_AUXTRACE_ERROR = 72,
PERF_RECORD_THREAD_MAP = 73,
PERF_RECORD_CPU_MAP = 74,
PERF_RECORD_STAT_CONFIG = 75,
PERF_RECORD_STAT = 76,
PERF_RECORD_STAT_ROUND = 77,
PERF_RECORD_EVENT_UPDATE = 78,
PERF_RECORD_TIME_CONV = 79,
/*
* Describes a header feature. These are records used in pipe-mode that
* contain information that otherwise would be in perf.data file's header.
*/
PERF_RECORD_HEADER_FEATURE = 80,
/*
* struct compressed_event {
* struct perf_event_header header;
* char data[];
* };
The header is followed by compressed data frame that can be decompressed
into array of perf trace records. The size of the entire compressed event
record including the header is limited by the max value of header.size.
*/
PERF_RECORD_COMPRESSED = 81,
/*
Marks the end of records for the system, pre-existing threads in system wide
sessions, etc. Those are the ones prefixed PERF_RECORD_USER_*.
This is used, for instance, to 'perf inject' events after init and before
regular events, those emitted by the kernel, to support combining guest and
host records.
*/
PERF_RECORD_FINISHED_INIT = 82,
};
// Information at the start of each event.
struct perf_event_header {
perf_event_type type;
uint16_t misc;
uint16_t size;
// Reverse the endian order of all fields in this struct.
void ByteSwap() noexcept;
};
namespace tracepoint_decode
{
// Returns a string for the PERF_TYPE_* enum value, e.g. "HARDWARE".
// If enum is not recognized, formats decimal value into and returns scratch.
_Ret_z_ char const* _ltpDecl
PerfEnumToString(perf_type_id value, _Pre_cap_(11) char* scratch) noexcept;
// Returns a string for the PERF_RECORD_* enum value, e.g. "SAMPLE".
// If enum is not recognized, formats decimal value into and returns scratch.
_Ret_z_ char const* _ltpDecl
PerfEnumToString(perf_event_type value, _Pre_cap_(11) char* scratch) noexcept;
}
// namespace tracepoint_decode
#endif // _included_PerfEventAbi_h

View file

@ -0,0 +1,109 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#ifndef _included_PerfEventInfo_h
#define _included_PerfEventInfo_h
#include <stdint.h>
#ifdef _WIN32
#include <sal.h>
#endif
#ifndef _Field_size_bytes_
#define _Field_size_bytes_(size)
#endif
#ifndef _Ret_z_
#define _Ret_z_
#endif
#ifndef _Ret_opt_
#define _Ret_opt_
#endif
// Forward declarations from PerfEventAbi.h or linux/uapi/linux/perf_event.h:
struct perf_event_attr;
struct perf_event_header;
namespace tracepoint_decode
{
// Forward declaration from PerfDataFileDefs.h:
struct PerfEventDesc;
// Forward declaration from PerfEventSessionInfo.h:
class PerfEventSessionInfo;
// Forward declaration from PerfEventMetadata.h:
class PerfEventMetadata;
struct PerfSampleEventInfo
{
PerfEventDesc const* event_desc; // Always valid if GetSampleEventInfo() succeeded.
PerfEventSessionInfo const* session_info;//Always valid if GetSampleEventInfo() succeeded.
perf_event_header const* header; // Always valid if GetSampleEventInfo() succeeded. Points into event.
uint64_t id; // Valid if SampleType() & (PERF_SAMPLE_IDENTIFIER | PERF_SAMPLE_ID).
uint32_t pid, tid; // Valid if SampleType() & PERF_SAMPLE_TID.
uint64_t time; // Valid if SampleType() & PERF_SAMPLE_TIME.
uint64_t stream_id; // Valid if SampleType() & PERF_SAMPLE_STREAM_ID.
uint32_t cpu, cpu_reserved; // Valid if SampleType() & PERF_SAMPLE_CPU.
uint64_t ip; // Valid if SampleType() & PERF_SAMPLE_IP.
uint64_t addr; // Valid if SampleType() & PERF_SAMPLE_ADDR.
uint64_t period; // Valid if SampleType() & PERF_SAMPLE_PERIOD.
uint64_t const* read_values; // Valid if SampleType() & PERF_SAMPLE_READ. Points into event.
uint64_t const* callchain; // Valid if SampleType() & PERF_SAMPLE_CALLCHAIN. Points into event.
_Field_size_bytes_(raw_data_size) void const* raw_data; // Valid if SampleType() & PERF_SAMPLE_RAW. Points into event.
uintptr_t raw_data_size; // Valid if SampleType() & PERF_SAMPLE_RAW. Size of raw_data.
// Requires: GetSampleEventInfo() succeeded.
// Returns: event_desc->attr->sample_type.
uint64_t
SampleType() const noexcept;
// Requires: GetSampleEventInfo() succeeded.
// Returns: event_desc->attr.
perf_event_attr const&
Attr() const noexcept;
// Requires: GetSampleEventInfo() succeeded.
// Returns: event_desc->name.
// May be "", e.g. if no PERF_HEADER_EVENT_DESC header. In that case,
// caller should check for a name in Metadata().
_Ret_z_ char const*
Name() const noexcept;
// Requires: GetSampleEventInfo() succeeded.
// Returns: event_desc->metadata (may be NULL).
// Valid if SampleType() & PERF_SAMPLE_RAW.
_Ret_opt_ PerfEventMetadata const*
Metadata() const noexcept;
};
struct PerfNonSampleEventInfo
{
PerfEventDesc const* event_desc; // Always valid if GetNonSampleEventInfo() succeeded.
PerfEventSessionInfo const* session_info;//Always valid if GetNonSampleEventInfo() succeeded.
perf_event_header const* header; // Always valid if GetNonSampleEventInfo() succeeded. Points into event.
uint64_t id; // Valid if SampleType() & (PERF_SAMPLE_IDENTIFIER | PERF_SAMPLE_ID).
uint32_t pid, tid; // Valid if SampleType() & PERF_SAMPLE_TID.
uint64_t time; // Valid if SampleType() & PERF_SAMPLE_TIME.
uint64_t stream_id; // Valid if SampleType() & PERF_SAMPLE_STREAM_ID.
uint32_t cpu, cpu_reserved; // Valid if SampleType() & PERF_SAMPLE_CPU.
// Requires: GetNonSampleEventInfo() succeeded.
// Returns: event_desc->attr->sample_type.
uint64_t
SampleType() const noexcept;
// Requires: GetNonSampleEventInfo() succeeded.
// Returns: event_desc->attr.
perf_event_attr const&
Attr() const noexcept;
// Requires: GetNonSampleEventInfo() succeeded.
// Returns: event_desc->name.
_Ret_z_ char const*
Name() const noexcept;
};
}
// namespace tracepoint_decode
#endif // _included_PerfEventInfo_h

View file

@ -0,0 +1,242 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#ifndef _included_PerfEventMetadata_h
#define _included_PerfEventMetadata_h
#include <stdint.h>
#include <string_view>
#include <vector>
#ifdef _WIN32
#include <sal.h>
#endif
#ifndef _In_reads_bytes_
#define _In_reads_bytes_(cb)
#endif
namespace tracepoint_decode
{
enum PerfFieldElementSize : uint8_t
{
PerfFieldElementSize8 = 0, // sizeof(uint8_t) == 1 << PerfFieldElementSize8
PerfFieldElementSize16 = 1, // sizeof(uint16_t) == 1 << PerfFieldElementSize16
PerfFieldElementSize32 = 2, // sizeof(uint32_t) == 1 << PerfFieldElementSize32
PerfFieldElementSize64 = 3, // sizeof(uint64_t) == 1 << PerfFieldElementSize64
};
enum PerfFieldFormat : uint8_t
{
PerfFieldFormatNone, // Type unknown (treat as binary blob)
PerfFieldFormatUnsigned,// u8, u16, u32, u64, etc.
PerfFieldFormatSigned, // s8, s16, s32, s64, etc.
PerfFieldFormatHex, // unsigned long, pointers
PerfFieldFormatString, // char, char[]
};
enum PerfFieldArray : uint8_t
{
PerfFieldArrayNone, // e.g. "char val"
PerfFieldArrayFixed, // e.g. "char val[12]"
PerfFieldArrayDynamic, // e.g. "__data_loc char val[]", value = (len << 16) | offset.
PerfFieldArrayRelDyn, // e.g. "__rel_loc char val[]", value = (len << 16) | relativeOffset.
};
class PerfFieldMetadata
{
static constexpr std::string_view noname = std::string_view("noname", 6);
std::string_view m_name; // deduced from field, e.g. "my_field".
std::string_view m_field; // value of "field:" property, e.g. "char my_field[8]".
uint16_t m_offset; // value of "offset:" property.
uint16_t m_size; // value of "size:" property.
uint16_t m_fixedArrayCount; // deduced from field, size.
PerfFieldElementSize m_elementSize; // deduced from field, size.
PerfFieldFormat m_format; // deduced from field, size, signed.
PerfFieldArray m_array; // deduced from field, size.
public:
// Parses a line of the "format:" section of an event's "format" file. The
// formatLine string will generally look like
// "[whitespace?]field:[declaration]; offset:[number]; size:[number]; ...".
//
// If "field:" is non-empty, "offset:" is a valid unsigned integer, and
// "size:" is a valid unsigned integer, this returns
// PerfFieldMetadata(field, offset, size, isSigned). Otherwise, this
// returns PerfFieldMetadata().
//
// Stored strings will point into formatLine, so the formatLine string must
// outlive this object.
static PerfFieldMetadata
Parse(
bool longSize64, // true if sizeof(long) == 8, false if sizeof(long) == 4.
std::string_view formatLine) noexcept;
// Same as PerfFieldMetadata(false, {}, 0, 0)
constexpr
PerfFieldMetadata() noexcept
: m_name(noname)
, m_field()
, m_offset()
, m_size()
, m_fixedArrayCount()
, m_elementSize()
, m_format()
, m_array() {}
// Initializes Field, Offset, and Size properties exactly as specified.
// Deduces the other properties. The isSigned parameter should be -1 if the
// "signed:" property is not present in the format line.
PerfFieldMetadata(
bool longSize64, // true if sizeof(long) == 8, false if sizeof(long) == 4.
std::string_view field,
uint16_t offset,
uint16_t size,
int8_t isSigned = -1) noexcept;
// Returns the field name, e.g. "my_field". Never empty. (Deduced from
// "field:".)
constexpr std::string_view
Name() const noexcept { return m_name; }
// Returns the field declaration, e.g. "char my_field[8]".
// (Parsed directly from "field:".)
constexpr std::string_view
Field() const noexcept { return m_field; }
// Returns the byte offset of the start of the field data from the start of
// the event raw data. (Parsed directly from "offset:".)
constexpr uint16_t
Offset() const noexcept { return m_offset; }
// Returns the byte size of the field data. (Parsed directly from "size:".)
constexpr uint16_t
Size() const noexcept { return m_size; }
// Returns the number of elements in this field. Meaningful only when
// Array() == Fixed. (Deduced from "field:" and "size:".)
constexpr uint16_t
FixedArrayCount() const noexcept { return m_fixedArrayCount; }
// Returns the size of each element in this field. (Deduced from "field:"
// and "size:".)
constexpr PerfFieldElementSize
ElementSize() const noexcept { return m_elementSize; }
// Returns the format of the field. (Deduced from "field:" and "signed:".)
constexpr PerfFieldFormat
Format() const noexcept { return m_format; }
// Returns whether this is an array, and if so, how the array length should
// be determined. (Deduced from "field:" and "size:".)
constexpr PerfFieldArray
Array() const noexcept { return m_array; }
// Given the event's raw data (e.g. PerfSampleEventInfo::raw_data), return
// this field's raw data. Returns empty for error (e.g. out of bounds).
//
// Does not do any byte-swapping. This method uses fileBigEndian to resolve
// data_loc and rel_loc references, not to fix up the field data.
//
// Note that in some cases, the size returned by GetFieldBytes may be
// different from the value returned by Size():
//
// - If eventRawDataSize < Offset() + Size(), returns {}.
// - If Size() == 0, returns all data from offset to the end of the event,
// i.e. it returns eventRawDataSize - Offset() bytes.
// - If Array() is Dynamic or RelDyn, the returned size depends on the
// event contents.
std::string_view
GetFieldBytes(
_In_reads_bytes_(eventRawDataSize) void const* eventRawData,
uintptr_t eventRawDataSize,
bool fileBigEndian) const noexcept;
};
enum class PerfEventKind : uint8_t
{
Normal, // No special handling detected.
EventHeader, // First user field is named "eventheader_flags".
};
class PerfEventMetadata
{
std::string_view m_systemName;
std::string_view m_formatFileContents;
std::string_view m_name;
std::string_view m_printFmt;
std::vector<PerfFieldMetadata> m_fields;
uint32_t m_id; // From common_type; not the same as the perf_event_attr::id or PerfSampleEventInfo::id.
uint16_t m_commonFieldCount; // fields[common_field_count] is the first user field.
uint16_t m_commonFieldsSize; // Offset of the end of the last common field
PerfEventKind m_kind;
public:
~PerfEventMetadata();
PerfEventMetadata() noexcept;
// Returns the value of the systemName parameter, e.g. "user_events".
constexpr std::string_view
SystemName() const noexcept { return m_systemName; }
// Returns the value of the formatFileContents parameter, e.g.
// "name: my_event\nID: 1234\nformat:...".
constexpr std::string_view
FormatFileContents() const noexcept { return m_formatFileContents; }
// Returns the value of the "name:" property, e.g. "my_event".
constexpr std::string_view
Name() const noexcept { return m_name; }
// Returns the value of the "print fmt:" property.
constexpr std::string_view
PrintFmt() const noexcept { return m_printFmt; }
// Returns the fields from the "format:" property.
constexpr std::vector<PerfFieldMetadata> const&
Fields() const noexcept { return m_fields; }
// Returns the value of the "ID:" property. Note that this value gets
// matched against the "common_type" field of an event, not the id field
// of perf_event_attr or PerfSampleEventInfo.
constexpr uint32_t
Id() const noexcept { return m_id; }
// Returns the number of "common_*" fields at the start of the event.
// User fields start at this index. At present, there are 4 common fields:
// common_type, common_flags, common_preempt_count, common_pid.
constexpr uint16_t
CommonFieldCount() const noexcept { return m_commonFieldCount; }
// Returns the offset of the end of the last "common_*" field.
// This is the start of the first user field.
constexpr uint16_t
CommonFieldsSize() const noexcept { return m_commonFieldsSize; }
// Returns the detected event decoding system - Normal or EventHeader.
constexpr PerfEventKind
Kind() const noexcept { return m_kind; }
// Sets all properties of this object to {} values.
void
Clear() noexcept;
// Parses an event's "format" file and sets the fields of this object based
// on the results. Returns true if "ID:" is a valid unsigned integer and
// "name:" is non-empty, returns true. Throws bad_alloc for out-of-memory.
//
// Stored strings will point into systemName and formatFileContents, so
// those strings must outlive this object.
bool
Parse(
bool longSize64, // true if sizeof(long) == 8, false if sizeof(long) == 4.
std::string_view systemName,
std::string_view formatFileContents) noexcept(false); // May throw bad_alloc.
};
}
// namespace tracepoint_decode
#endif // _included_PerfEventMetadata_h

View file

@ -0,0 +1,91 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#ifndef _included_PerfEventSessionInfo_h
#define _included_PerfEventSessionInfo_h
#include <stdint.h>
#ifdef _WIN32
#include <sal.h>
#endif
#ifndef _Out_
#define _Out_
#endif
namespace tracepoint_decode
{
// Semantics equivalent to struct timespec from <time.h>.
struct PerfEventTimeSpec
{
int64_t tv_sec; // Seconds since 1970.
uint32_t tv_nsec; // Nanoseconds.
};
class PerfEventSessionInfo
{
int64_t m_clockOffsetSec;
uint32_t m_clockOffsetNsec;
uint32_t m_clockId;
bool m_clockOffsetKnown;
public:
constexpr
PerfEventSessionInfo() noexcept
: m_clockOffsetSec(0)
, m_clockOffsetNsec(0)
, m_clockId(0xFFFFFFFF)
, m_clockOffsetKnown(false)
{
return;
}
// From HEADER_CLOCKID. If unknown, use SetClockid(0xFFFFFFFF).
void
SetClockid(uint32_t clockid) noexcept;
// From HEADER_CLOCK_DATA. If unknown, use SetClockData(0xFFFFFFFF, 0, 0).
void
SetClockData(uint32_t clockid, uint64_t wallClockNS, uint64_t clockidTimeNS) noexcept;
// Gets offset values suitable for use in HEADER_CLOCK_DATA.
// Note: The returned NS values may be normalized relative to the values provided
// to SetClockData, but the difference between them will be the same as the
// difference between the values provided to SetClockData.
void
GetClockData(
_Out_ uint64_t* wallClockNS,
_Out_ uint64_t* clockidTimeNS) const noexcept;
// Returns the clockid of the session timestamp, e.g. CLOCK_MONOTONIC.
// Returns 0xFFFFFFFF if the session timestamp clockid is unknown.
constexpr uint32_t
Clockid() const noexcept
{
return m_clockId;
}
// Returns the CLOCK_REALTIME value that corresponds to an event timestamp of 0
// for this session. Returns 1970 if the session timestamp offset is unknown.
PerfEventTimeSpec
ClockOffset() const noexcept;
// Returns true if session clock offset is known.
constexpr bool
ClockOffsetKnown() const noexcept
{
return m_clockOffsetKnown;
}
// Converts time from session timestamp to real-time (time since 1970):
// TimeToRealTime = ClockOffset() + time.
// If session clock offset is unknown, assume 1970.
PerfEventTimeSpec
TimeToRealTime(uint64_t time) const noexcept;
};
}
// namespace tracepoint_decode
#endif // _included_PerfEventSessionInfo_h

View file

@ -0,0 +1,6 @@
add_executable(perf-file-rewrite
perf-file-rewrite.cpp)
target_link_libraries(perf-file-rewrite
tracepoint-decode)
target_compile_features(perf-file-rewrite
PRIVATE cxx_std_17)

View file

@ -0,0 +1,347 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
/*
Sample tool that demonstrates reading a perf.data file with PerfDataFile and
writing a perf.data file with PerfDataFileWriter.
This is not intended to be useful (except perhaps for testing purposes). It is
intended to show how PerfDataFile can be used to take the perf.data file
apart and how PerfDataFileWriter can put it back together.
Note that the output file is not expected to be exactly the same as the input:
- Output is always a normal-mode file even if input was a pipe-mode file.
- Output file may store headers in a different order.
- Output file may use more/less padding.
- If the input file is semantically inconsistent, the output file may not
precisely match the input (the inconsistent data might be lost). For
example, there are usually two (or more) copies of each attr, one in a
v1 format and another in a v2 format. The rewrite process will typically
ignore the v1 copy of the data if a v2 copy is available, so if the v1 copy
is semantically different from the v2 copy, that detail might be lost during
rewrite.
*/
#include <tracepoint/PerfDataFile.h>
#include <tracepoint/PerfDataFileWriter.h>
#include <tracepoint/PerfEventAbi.h>
#include <tracepoint/PerfEventInfo.h>
#include <tracepoint/PerfEventMetadata.h>
#include <string>
#include <unordered_set>
#include <vector>
#ifdef _WIN32
#define strerror_r(errnum, buf, buflen) (strerror_s(buf, buflen, errnum), buf)
#define UNLINK(filename) _unlink(filename)
#else
#include <unistd.h>
#define UNLINK(filename) unlink(filename)
#endif // _WIN32
using namespace tracepoint_decode;
static void
WriteErrorMessage(char const* filename, int error, char const* context)
{
if (error == ENOMEM)
{
throw std::bad_alloc();
}
char errorBuf[80];
fprintf(stderr, "%s: error %u : %s (%s).\n",
filename,
error,
context,
strerror_r(error, errorBuf, sizeof(errorBuf)));
}
static void
WriteWarningMessage(char const* filename, int error, char const* context)
{
if (error == ENOMEM)
{
throw std::bad_alloc();
}
char errorBuf[80];
fprintf(stderr, "%s: warning %u : %s (%s).\n",
filename,
error,
context,
strerror_r(error, errorBuf, sizeof(errorBuf)));
}
static void
MergeEventDesc(
PerfDataFileWriter& output,
char const* outputPath,
std::unordered_set<uint64_t>& sampleIdsUsed,
std::vector<uint64_t>& sampleIdsBuffer,
PerfEventDesc const& desc)
{
sampleIdsBuffer.clear();
for (uint32_t iId = 0; iId != desc.ids_count; iId += 1)
{
auto const id = desc.ids[iId];
if (sampleIdsUsed.insert(desc.ids[iId]).second)
{
sampleIdsBuffer.push_back(id);
}
}
if (!sampleIdsBuffer.empty())
{
PerfEventDesc newDesc = desc;
newDesc.ids = sampleIdsBuffer.data();
newDesc.ids_count = static_cast<uint32_t>(sampleIdsBuffer.size());
auto err = output.AddEventDesc(newDesc);
if (err != 0)
{
WriteWarningMessage(outputPath, err, "output.AddEventDesc failed, metadata incomplete");
}
}
}
int
main(int argc, char* argv[])
{
bool mainSuccessful = false;
if (argc <= 1)
{
fprintf(stderr, "\nUsage: %s [perf.data] ... (will generate *.rewrite)\n",
argv[0]);
}
else try
{
PerfDataFile input;
PerfDataFileWriter output;
std::string outputPathBuffer;
std::vector<uint64_t> sampleIdsBuffer;
std::unordered_set<uint64_t> sampleIdsUsed;
for (int argi = 1; argi < argc; argi += 1)
{
int err;
auto const inputPath = argv[argi];
outputPathBuffer = inputPath;
outputPathBuffer += ".rewrite";
auto const outputPath = outputPathBuffer.c_str();
// CodeQL [SM01937] This is a sample/tool. Using externally-supplied path is intended behavior.
err = input.Open(inputPath);
if (err != 0)
{
WriteErrorMessage(inputPath, err, "input.Open failed, skipping file");
continue;
}
if (input.ByteReader().ByteSwapNeeded())
{
// PerfDataFileWriter only supports creating host-endian files, so we can't
// easily rewrite a byte-swapped input file.
err = ENOTSUP;
WriteErrorMessage(inputPath, err, "input is byte-swapped, skipping file");
continue;
}
// CodeQL [SM01937] This is a sample/tool. Using externally-supplied path is intended behavior.
err = output.Create(outputPath);
if (err != 0)
{
WriteErrorMessage(outputPath, err, "output.Create failed, skipping file");
continue;
}
sampleIdsUsed.clear();
for (;;)
{
perf_event_header const* pHeader;
err = input.ReadEvent(&pHeader);
if (!pHeader)
{
if (err != 0)
{
WriteWarningMessage(inputPath, err, "input.Read failed, ignoring rest of input");
}
break;
}
uint32_t eventDataSize;
switch (pHeader->type)
{
default:
eventDataSize = pHeader->size;
break;
case PERF_RECORD_AUXTRACE:
// Special-case. Event content size != pHeader->size.
eventDataSize = input.EventDataSize(pHeader);
break;
case PERF_RECORD_HEADER_ATTR:
// Pseudo-event, conflicts with AddEventDesc.
// PerfDataFile automatically merges data from this event into its own
// EventDesc table. We'll use AddEventDesc to generate the output file's
// attr headers based on the merged EventDesc table.
continue;
case PERF_RECORD_HEADER_EVENT_TYPE:
// Pseudo-event, conflicts with AddEventDesc.
// PerfDataFile could automatically merge data from this event into its
// own EventDesc table, but that is not implemented because this event
// type is deprecated. Instead, we'll just ignore this event type.
continue;
case PERF_RECORD_HEADER_TRACING_DATA:
// Pseudo-event, conflicts with SetTracingData.
// PerfDataFile automatically merges data from this event into its own
// metadata table. We'll use SetTracingData to generate the output file's
// metadata based on the metadata referenced by the input file's events.
continue;
case PERF_RECORD_HEADER_BUILD_ID:
case PERF_RECORD_HEADER_FEATURE:
// Pseudo-events, conflict with SetHeader.
// PerfDataFile automatically merges data from these events into its own
// header table. We'll use SetHeader to generate the output file's headers
// based on the merged header table.
continue;
}
if (pHeader->type == PERF_RECORD_SAMPLE)
{
// Populate the output file's metadata from the event's metadata.
PerfSampleEventInfo info;
err = input.GetSampleEventInfo(pHeader, &info);
if (err != 0)
{
WriteWarningMessage(inputPath, err, "input.GetSampleEventInfo failed, metadata may be incomplete");
}
else if (info.event_desc->metadata)
{
auto const& desc = *info.event_desc;
err = output.AddTracepointEventDesc(desc);
if (err == 0)
{
// We don't need to AddEventDesc for the IDs covered by this event_desc.
for (auto i = 0u; i != desc.ids_count; i += 1)
{
sampleIdsUsed.insert(desc.ids[i]);
}
}
else if (err == EEXIST)
{
// Already added metadata for this event.
}
else
{
WriteWarningMessage(outputPath, err, "output.AddTracepointEventDesc failed, metadata may be incomplete");
}
}
}
err = output.WriteEventData(pHeader, eventDataSize);
if (err != 0)
{
WriteErrorMessage(outputPath, err, "output.Write failed");
goto CloseAndUnlinkOutput;
}
}
// Populate the output file's EventDesc table from the input file's table.
// Some of this was already done by AddTracepointEventDesc.
// In addition, the input file's table usually has duplicate entries - one entry with
// names and one entry without names. Therefore, MergeEventDesc will skip ids that are
// already populated, and we merge all descriptors with names before merging any
// descriptors that don't have names.
for (size_t iDesc = 0; iDesc != input.EventDescCount(); iDesc += 1)
{
// First, merge data from descriptors that have names.
auto const& desc = input.EventDesc(iDesc);
if (desc.name[0] != '\0')
{
MergeEventDesc(output, outputPath, sampleIdsUsed, sampleIdsBuffer, desc);
}
}
for (size_t iDesc = 0; iDesc != input.EventDescCount(); iDesc += 1)
{
// Second, fill gaps (if any) using descriptors that don't have names.
auto const& desc = input.EventDesc(iDesc);
if (desc.name[0] == '\0')
{
MergeEventDesc(output, outputPath, sampleIdsUsed, sampleIdsBuffer, desc);
}
}
// Populate the output file's headers.
for (auto i = PERF_HEADER_FIRST_FEATURE; i != PERF_HEADER_LAST_FEATURE; i = static_cast<PerfHeaderIndex>(i + 1))
{
switch (i)
{
case PERF_HEADER_TRACING_DATA:
case PERF_HEADER_EVENT_DESC:
// Let the output file auto-populate these based on AddEventDesc and AddTracingData.
continue;
default:
break;
}
auto header = input.Header(i);
if (!header.empty())
{
// Copy the input file's merged header into the output file.
err = output.SetHeader(i, header.data(), static_cast<unsigned>(header.size()));
if (err != 0)
{
WriteErrorMessage(outputPath, err, "output.SetHeader failed, closing");
goto CloseAndUnlinkOutput;
}
}
}
err = output.SetTracingData(
input.TracingDataLongSize(),
input.TracingDataPageSize(),
input.TracingDataHeaderPage(),
input.TracingDataHeaderEvent(),
input.TracingDataFtraces(),
input.TracingDataFtraceCount(),
input.TracingDataKallsyms(),
input.TracingDataPrintk(),
input.TracingDataSavedCmdLine());
if (err != 0)
{
WriteErrorMessage(outputPath, err, "output.SetTracingData failed");
goto CloseAndUnlinkOutput;
}
err = output.FinalizeAndClose();
if (err != 0)
{
WriteErrorMessage(outputPath, err, "output.FinalizeAndClose failed");
goto CloseAndUnlinkOutput;
}
fprintf(stdout, "\"%s\" --> \"%s\"\n", inputPath, outputPath);
mainSuccessful = true; // One or more files completed.
continue;
CloseAndUnlinkOutput:
output.CloseNoFinalize();
UNLINK(outputPath);
}
}
catch (std::exception const& ex)
{
fprintf(stderr, "\nException: %s\n", ex.what());
mainSuccessful = false;
}
return mainSuccessful ? 0 : 1;
}

View file

@ -0,0 +1,45 @@
# tracepoint-decode = libtracepoint-decode, DECODE_HEADERS
add_library(tracepoint-decode
PerfByteReader.cpp
PerfDataFile.cpp
PerfDataFileWriter.cpp
PerfEventAbi.cpp
PerfEventInfo.cpp
PerfEventMetadata.cpp
PerfEventSessionInfo.cpp)
target_include_directories(tracepoint-decode
PUBLIC
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include/>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
set(DECODE_HEADERS
"${PROJECT_SOURCE_DIR}/include/tracepoint/PerfByteReader.h"
"${PROJECT_SOURCE_DIR}/include/tracepoint/PerfDataFile.h"
"${PROJECT_SOURCE_DIR}/include/tracepoint/PerfDataFileDefs.h"
"${PROJECT_SOURCE_DIR}/include/tracepoint/PerfDataFileWriter.h"
"${PROJECT_SOURCE_DIR}/include/tracepoint/PerfEventAbi.h"
"${PROJECT_SOURCE_DIR}/include/tracepoint/PerfEventInfo.h"
"${PROJECT_SOURCE_DIR}/include/tracepoint/PerfEventMetadata.h"
"${PROJECT_SOURCE_DIR}/include/tracepoint/PerfEventSessionInfo.h")
set_target_properties(tracepoint-decode PROPERTIES
PUBLIC_HEADER "${DECODE_HEADERS}")
target_compile_features(tracepoint-decode
PRIVATE cxx_std_17)
install(TARGETS tracepoint-decode
EXPORT tracepoint-decodeTargets
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/tracepoint)
install(EXPORT tracepoint-decodeTargets
FILE "tracepoint-decodeTargets.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/tracepoint-decode")
configure_package_config_file(
"${CMAKE_CURRENT_SOURCE_DIR}/tracepoint-decodeConfig.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/tracepoint-decodeConfig.cmake"
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/tracepoint-decode"
NO_SET_AND_CHECK_MACRO
NO_CHECK_REQUIRED_COMPONENTS_MACRO)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/tracepoint-decodeConfigVersion.cmake"
COMPATIBILITY SameMinorVersion)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/tracepoint-decodeConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/tracepoint-decodeConfigVersion.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/tracepoint-decode")

View file

@ -0,0 +1,121 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include <tracepoint/PerfByteReader.h>
#include <string.h>
#include <assert.h>
#ifdef _WIN32
#include <stdlib.h>
#define bswap_16(n) _byteswap_ushort(n)
#define bswap_32(n) _byteswap_ulong(n)
#define bswap_64(n) _byteswap_uint64(n)
static bool constexpr HostIsBigEndian = false;
#else // _WIN32
#include <byteswap.h>
#include <endian.h>
static bool constexpr HostIsBigEndian = __BYTE_ORDER == __BIG_ENDIAN;
#endif // _WIN32
using namespace tracepoint_decode;
PerfByteReader::PerfByteReader() noexcept
: m_bigEndian(HostIsBigEndian) {}
PerfByteReader::PerfByteReader(bool bigEndian) noexcept
: m_bigEndian(bigEndian) {}
bool
PerfByteReader::BigEndian() const noexcept
{
return m_bigEndian;
}
bool
PerfByteReader::ByteSwapNeeded() const noexcept
{
return HostIsBigEndian != m_bigEndian;
}
uint8_t
PerfByteReader::ReadAsU8(_In_reads_bytes_(1) void const* pSrc) const noexcept
{
return *static_cast<uint8_t const*>(pSrc);
}
uint16_t
PerfByteReader::ReadAsU16(_In_reads_bytes_(2) void const* pSrc) const noexcept
{
uint16_t fileBits;
memcpy(&fileBits, pSrc, sizeof(fileBits));
return HostIsBigEndian == m_bigEndian ? fileBits : bswap_16(fileBits);
}
uint32_t
PerfByteReader::ReadAsU32(_In_reads_bytes_(4) void const* pSrc) const noexcept
{
uint32_t fileBits;
memcpy(&fileBits, pSrc, sizeof(fileBits));
return HostIsBigEndian == m_bigEndian ? fileBits : bswap_32(fileBits);
}
uint64_t
PerfByteReader::ReadAsU64(_In_reads_bytes_(8) void const* pSrc) const noexcept
{
uint64_t fileBits;
memcpy(&fileBits, pSrc, sizeof(fileBits));
return HostIsBigEndian == m_bigEndian ? fileBits : bswap_64(fileBits);
}
uint32_t
PerfByteReader::ReadAsDynU32(
_In_reads_bytes_(cbSrc) void const* pSrc,
uint8_t cbSrc) const noexcept
{
uint32_t result;
switch (cbSrc)
{
default:
assert(false);
result = 0;
break;
case 1:
result = *static_cast<uint8_t const*>(pSrc);
break;
case 2:
result = ReadAsU16(pSrc);
break;
case 4:
result = ReadAsU32(pSrc);
break;
}
return result;
}
uint64_t
PerfByteReader::ReadAsDynU64(
_In_reads_bytes_(cbSrc) void const* pSrc,
uint8_t cbSrc) const noexcept
{
uint64_t result;
switch (cbSrc)
{
default:
assert(false);
result = 0;
break;
case 1:
result = *static_cast<uint8_t const*>(pSrc);
break;
case 2:
result = ReadAsU16(pSrc);
break;
case 4:
result = ReadAsU32(pSrc);
break;
case 8:
result = ReadAsU64(pSrc);
break;
}
return result;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,184 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include <tracepoint/PerfEventAbi.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#ifdef _WIN32
#define bswap_16(n) _byteswap_ushort(n)
#define bswap_32(n) _byteswap_ulong(n)
#define bswap_64(n) _byteswap_uint64(n)
#else // _WIN32
#include <byteswap.h>
#endif // _WIN32
using namespace tracepoint_decode;
template<class T, unsigned N>
static constexpr unsigned
ArrayCount(T(&)[N]) noexcept
{
return N;
}
template<class T>
static void
Bswap(T& value) noexcept
{
if constexpr (sizeof(value) == sizeof(uint16_t))
{
value = static_cast<T>(bswap_16(value));
}
else if constexpr (sizeof(value) == sizeof(uint32_t))
{
value = static_cast<T>(bswap_32(value));
}
else if constexpr (sizeof(value) == sizeof(uint64_t))
{
value = static_cast<T>(bswap_64(value));
}
else
{
static_assert(sizeof(T) == 0, "Bad Bswap");
}
}
void
perf_event_attr::ByteSwap() noexcept
{
Bswap(type);
Bswap(size);
Bswap(config);
Bswap(sample_period);
Bswap(sample_type);
Bswap(read_format);
// Bitfield hack: Reverse bits within each byte, don't reorder bytes.
unsigned constexpr offsetof_bitfield = offsetof(perf_event_attr, read_format) + sizeof(read_format);
static_assert(offsetof_bitfield + sizeof(uint64_t) == offsetof(perf_event_attr, wakeup_events));
for (uint8_t* pb = reinterpret_cast<uint8_t*>(this) + offsetof_bitfield;
pb != reinterpret_cast<uint8_t*>(this) + offsetof_bitfield + sizeof(uint64_t);
pb += 1)
{
uint8_t b = *pb;
b = ((b & 0xF0) >> 4) | ((b & 0x0F) << 4);
b = ((b & 0xCC) >> 2) | ((b & 0x33) << 2);
b = ((b & 0xAA) >> 1) | ((b & 0x55) << 1);
*pb = b;
}
Bswap(wakeup_events);
Bswap(bp_type);
Bswap(bp_addr);
Bswap(bp_len);
Bswap(branch_sample_type);
Bswap(sample_regs_user);
Bswap(sample_stack_user);
Bswap(aux_watermark);
Bswap(sample_max_stack);
Bswap(aux_sample_size);
}
void
perf_event_header::ByteSwap() noexcept
{
Bswap(type);
Bswap(misc);
Bswap(size);
}
_Ret_z_
static char const*
EnumToString(
char const* const names[],
unsigned nameCount,
uint32_t value,
_Pre_cap_(11) char* scratch) noexcept
{
char const* str;
if (value < nameCount)
{
str = names[value];
}
else
{
snprintf(scratch, 11, "%u", value);
str = scratch;
}
return str;
}
_Ret_z_ char const* _ltpDecl
PerfEnumToString(perf_type_id value, _Pre_cap_(11) char* scratch) noexcept
{
static char const* const names[] = {
"HARDWARE",
"SOFTWARE",
"TRACEPOINT",
"HW_CACHE",
"RAW",
"BREAKPOINT",
};
static_assert(ArrayCount(names) == PERF_TYPE_MAX);
return EnumToString(names, ArrayCount(names), value, scratch);
}
_Ret_z_ char const* _ltpDecl
PerfEnumToString(perf_event_type value, _Pre_cap_(11) char* scratch) noexcept
{
static char const* const names[] = {
"0",
"MMAP",
"LOST",
"COMM",
"EXIT",
"THROTTLE",
"UNTHROTTLE",
"FORK",
"READ",
"SAMPLE",
"MMAP2",
"AUX",
"ITRACE_START",
"LOST_SAMPLES",
"SWITCH",
"SWITCH_CPU_WIDE",
"NAMESPACES",
"KSYMBOL",
"BPF_EVENT",
"CGROUP",
"TEXT_POKE",
"AUX_OUTPUT_HW_ID",
};
static_assert(ArrayCount(names) == PERF_RECORD_MAX);
static char const* const moreNames[] = {
"HEADER_ATTR",
"HEADER_EVENT_TYPE",
"HEADER_TRACING_DATA",
"HEADER_BUILD_ID",
"FINISHED_ROUND",
"ID_INDEX",
"AUXTRACE_INFO",
"AUXTRACE",
"AUXTRACE_ERROR",
"THREAD_MAP",
"CPU_MAP",
"STAT_CONFIG",
"STAT",
"STAT_ROUND",
"EVENT_UPDATE",
"TIME_CONV",
"HEADER_FEATURE",
"COMPRESSED",
"FINISHED_INIT",
};
static_assert(ArrayCount(moreNames) == PERF_RECORD_FINISHED_INIT - PERF_RECORD_HEADER_ATTR + 1);
return PERF_RECORD_HEADER_ATTR <= value && value <= PERF_RECORD_FINISHED_INIT
? moreNames[value - PERF_RECORD_HEADER_ATTR]
: EnumToString(names, ArrayCount(names), value, scratch);
}

Some files were not shown because too many files have changed in this diff Show more