diff --git a/THIRD-PARTY-NOTICES.TXT b/THIRD-PARTY-NOTICES.TXT index 547e2bb6d7908..1f17cf6bc1106 100644 --- a/THIRD-PARTY-NOTICES.TXT +++ b/THIRD-PARTY-NOTICES.TXT @@ -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 ------------------------------- diff --git a/docs/design/features/user-events.md b/docs/design/features/user-events.md new file mode 100644 index 0000000000000..aa1a644608ae6 --- /dev/null +++ b/docs/design/features/user-events.md @@ -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 \ No newline at end of file diff --git a/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt b/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt index 7ba58d0297f9a..7e941521bd51b 100644 --- a/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt +++ b/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt @@ -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) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 07fa685484f02..c6c2903f5c383 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -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"), "") diff --git a/src/coreclr/inc/eventtracebase.h b/src/coreclr/inc/eventtracebase.h index 3fa3903b05811..316104f649a1d 100644 --- a/src/coreclr/inc/eventtracebase.h +++ b/src/coreclr/inc/eventtracebase.h @@ -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 @@ class EventPipeHelper 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 diff --git a/src/coreclr/scripts/genEventPipe.py b/src/coreclr/scripts/genEventPipe.py index 05bedd31e78e9..469852cfc63d8 100644 --- a/src/coreclr/scripts/genEventPipe.py +++ b/src/coreclr/scripts/genEventPipe.py @@ -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): diff --git a/src/coreclr/scripts/genEventing.py b/src/coreclr/scripts/genEventing.py index a69f155bbdfcc..e1ca570166e70 100644 --- a/src/coreclr/scripts/genEventing.py +++ b/src/coreclr/scripts/genEventing.py @@ -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 @@ def __init__(self, runtime): "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 @@ def __init__(self, runtime): "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 @@ -167,7 +224,7 @@ def includeProvider(providerName, runtimeFlavor): return False else: return True - + def includeEvent(inclusionList, providerName, eventName): if len(inclusionList) == 0: return True @@ -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,10 +592,17 @@ 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") - + if runtimeFlavor.nativeaot: if providerName == "Microsoft-Windows-DotNETRuntime" or providerName == "Microsoft-Windows-DotNETRuntimePrivate": fnbody.append("#ifndef TARGET_UNIX\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 \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') @@ -774,7 +901,7 @@ def updateclreventsfile(write_xplatheader, target_cpp, runtimeFlavor, eventpipe_ elif generatedFileType == "source-impl-noop": Clrallevents.write('#include \n') Clrallevents.write('#include \n\n') - Clrallevents.write('#include \n\n') + Clrallevents.write('#include \n\n') Clrallevents.write('#ifndef ERROR_SUCCESS\n') Clrallevents.write('#define ERROR_SUCCESS 0L\n') Clrallevents.write('#endif\n\n') @@ -786,6 +913,9 @@ def updateclreventsfile(write_xplatheader, target_cpp, runtimeFlavor, eventpipe_ Clrallevents.write(dotnet_trace_context_typedef_windows + "\n") 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 @@ -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 @@ def generatePlatformIndependentFiles(sClrEtwAllMan, incDir, etmDummyFile, extern #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 @@ def generatePlatformIndependentFiles(sClrEtwAllMan, incDir, etmDummyFile, extern { 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 @@ def generatePlatformIndependentFiles(sClrEtwAllMan, incDir, etmDummyFile, extern # 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 @@ def generatePlatformIndependentFiles(sClrEtwAllMan, incDir, etmDummyFile, extern 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 @@ def generatePlatformIndependentFiles(sClrEtwAllMan, incDir, etmDummyFile, extern #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:]) diff --git a/src/coreclr/scripts/genUserEvents.py b/src/coreclr/scripts/genUserEvents.py new file mode 100644 index 0000000000000..dae8edc1b9630 --- /dev/null +++ b/src/coreclr/scripts/genUserEvents.py @@ -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 /src/scripts/genUserEvent.py + +******************************************************************/ + +""" + +stdprolog_cmake = """# +# +#****************************************************************** + +#DO NOT MODIFY. AUTOGENERATED FILE. +#This file is generated using the logic from /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 +#include +#include +""" + +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) diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt index f60713ef587e1..47afaec73bb1d 100644 --- a/src/coreclr/vm/CMakeLists.txt +++ b/src/coreclr/vm/CMakeLists.txt @@ -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 diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index ba2db68bf64a6..1cf3192bfba74 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -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 diff --git a/src/coreclr/vm/eventing/CMakeLists.txt b/src/coreclr/vm/eventing/CMakeLists.txt index efa1fc160ebe3..861f50dc160c3 100644 --- a/src/coreclr/vm/eventing/CMakeLists.txt +++ b/src/coreclr/vm/eventing/CMakeLists.txt @@ -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() diff --git a/src/coreclr/vm/eventing/userevents/CMakeLists.txt b/src/coreclr/vm/eventing/userevents/CMakeLists.txt new file mode 100644 index 0000000000000..0f0f468a530cc --- /dev/null +++ b/src/coreclr/vm/eventing/userevents/CMakeLists.txt @@ -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) diff --git a/src/coreclr/vm/eventing/userevents/user_events.cpp b/src/coreclr/vm/eventing/userevents/user_events.cpp new file mode 100644 index 0000000000000..7abeaa3791c25 --- /dev/null +++ b/src/coreclr/vm/eventing/userevents/user_events.cpp @@ -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 +#include +#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; +} diff --git a/src/coreclr/vm/eventing/userevents/user_events.h b/src/coreclr/vm/eventing/userevents/user_events.h new file mode 100644 index 0000000000000..370fc8afbd7ce --- /dev/null +++ b/src/coreclr/vm/eventing/userevents/user_events.h @@ -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__ \ No newline at end of file diff --git a/src/coreclr/vm/eventtrace.cpp b/src/coreclr/vm/eventtrace.cpp index 86da0269b3794..5e9a73c871254 100644 --- a/src/coreclr/vm/eventtrace.cpp +++ b/src/coreclr/vm/eventtrace.cpp @@ -5800,6 +5800,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) diff --git a/src/native/external/LinuxTracepoints-version.txt b/src/native/external/LinuxTracepoints-version.txt new file mode 100644 index 0000000000000..6c73186a9d0c6 --- /dev/null +++ b/src/native/external/LinuxTracepoints-version.txt @@ -0,0 +1,2 @@ +1.3.3 +https://github.com/microsoft/LinuxTracepoints/tree/a817b91dfb08b2929ec6d48a211644e3394bf1c7 diff --git a/src/native/external/LinuxTracepoints.cmake b/src/native/external/LinuxTracepoints.cmake new file mode 100644 index 0000000000000..fedadc9304977 --- /dev/null +++ b/src/native/external/LinuxTracepoints.cmake @@ -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}") \ No newline at end of file diff --git a/src/native/external/LinuxTracepoints/.gitattributes b/src/native/external/LinuxTracepoints/.gitattributes new file mode 100644 index 0000000000000..7ade31e8bfaa1 --- /dev/null +++ b/src/native/external/LinuxTracepoints/.gitattributes @@ -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 diff --git a/src/native/external/LinuxTracepoints/.gitignore b/src/native/external/LinuxTracepoints/.gitignore new file mode 100644 index 0000000000000..baf208060b9bc --- /dev/null +++ b/src/native/external/LinuxTracepoints/.gitignore @@ -0,0 +1,10 @@ +*.suo +*.user +/bin*/ +.vs/ +obj/ +/out/ +.vscode +/rust/target/ +/rust/Cargo.lock +/build/ diff --git a/src/native/external/LinuxTracepoints/CHANGELOG.md b/src/native/external/LinuxTracepoints/CHANGELOG.md new file mode 100644 index 0000000000000..5041ecacb9e4e --- /dev/null +++ b/src/native/external/LinuxTracepoints/CHANGELOG.md @@ -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. diff --git a/src/native/external/LinuxTracepoints/CMakeLists.txt b/src/native/external/LinuxTracepoints/CMakeLists.txt new file mode 100644 index 0000000000000..fc9f4956f0a1c --- /dev/null +++ b/src/native/external/LinuxTracepoints/CMakeLists.txt @@ -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() diff --git a/src/native/external/LinuxTracepoints/CMakePresets.json b/src/native/external/LinuxTracepoints/CMakePresets.json new file mode 100644 index 0000000000000..4809c079b649c --- /dev/null +++ b/src/native/external/LinuxTracepoints/CMakePresets.json @@ -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"] + } + ] +} diff --git a/src/native/external/LinuxTracepoints/CODE_OF_CONDUCT.md b/src/native/external/LinuxTracepoints/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000000..f9ba8cf65f3e3 --- /dev/null +++ b/src/native/external/LinuxTracepoints/CODE_OF_CONDUCT.md @@ -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 diff --git a/src/native/external/LinuxTracepoints/LICENSE b/src/native/external/LinuxTracepoints/LICENSE new file mode 100644 index 0000000000000..9e841e7a26e4e --- /dev/null +++ b/src/native/external/LinuxTracepoints/LICENSE @@ -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 diff --git a/src/native/external/LinuxTracepoints/README.md b/src/native/external/LinuxTracepoints/README.md new file mode 100644 index 0000000000000..740f8aa322496 --- /dev/null +++ b/src/native/external/LinuxTracepoints/README.md @@ -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. diff --git a/src/native/external/LinuxTracepoints/SECURITY.md b/src/native/external/LinuxTracepoints/SECURITY.md new file mode 100644 index 0000000000000..1b52167106105 --- /dev/null +++ b/src/native/external/LinuxTracepoints/SECURITY.md @@ -0,0 +1,41 @@ + + +## 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). + + \ No newline at end of file diff --git a/src/native/external/LinuxTracepoints/SUPPORT.md b/src/native/external/LinuxTracepoints/SUPPORT.md new file mode 100644 index 0000000000000..6ab1097d1aa56 --- /dev/null +++ b/src/native/external/LinuxTracepoints/SUPPORT.md @@ -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. diff --git a/src/native/external/LinuxTracepoints/TestOutput/EventHeaderInterceptorLE64.dat b/src/native/external/LinuxTracepoints/TestOutput/EventHeaderInterceptorLE64.dat new file mode 100644 index 0000000000000..e79121d4372f2 Binary files /dev/null and b/src/native/external/LinuxTracepoints/TestOutput/EventHeaderInterceptorLE64.dat differ diff --git a/src/native/external/LinuxTracepoints/TestOutput/EventHeaderInterceptorLE64.dat.linux.json b/src/native/external/LinuxTracepoints/TestOutput/EventHeaderInterceptorLE64.dat.linux.json new file mode 100644 index 0000000000000..ceb5859d5c44b --- /dev/null +++ b/src/native/external/LinuxTracepoints/TestOutput/EventHeaderInterceptorLE64.dat.linux.json @@ -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 } } ] diff --git a/src/native/external/LinuxTracepoints/TestOutput/EventHeaderInterceptorLE64.dat.windows.json b/src/native/external/LinuxTracepoints/TestOutput/EventHeaderInterceptorLE64.dat.windows.json new file mode 100644 index 0000000000000..c37f9f5af681b --- /dev/null +++ b/src/native/external/LinuxTracepoints/TestOutput/EventHeaderInterceptorLE64.dat.windows.json @@ -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 } } ] diff --git a/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/CMakeLists.txt b/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/CMakeLists.txt new file mode 100644 index 0000000000000..63b41f382272b --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/CMakeLists.txt @@ -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() diff --git a/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/README.md b/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/README.md new file mode 100644 index 0000000000000..c34738bf69a26 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/README.md @@ -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. diff --git a/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/include/eventheader/EventEnumerator.h b/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/include/eventheader/EventEnumerator.h new file mode 100644 index 0000000000000..371a9639019ee --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/include/eventheader/EventEnumerator.h @@ -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 +#include +#include + +#ifdef _WIN32 +#include +#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; + + /// + /// Helper for decoding EventHeader events. + /// + class EventEnumerator + { + static event_field_encoding const EncodingCountMask = static_cast( + event_field_encoding_carray_flag | event_field_encoding_varray_flag); + + static event_field_encoding const ReadFieldError = event_field_encoding_invalid; + + /// + /// Substate allows us to flatten + /// "switch (state) { case X: if (condition) ... }" to + /// "switch (substate) { case X_condition: ... }" + /// which potentially improves performance. + /// + 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; + + /// + /// Initializes a new instance of EventEnumerator. Sets State to None. + /// + EventEnumerator() noexcept; + + /// + /// Returns the current state. + /// + EventEnumeratorState + State() const noexcept; + + /// + /// Gets status for the most recent call to StartEvent, MoveNext, or MoveNextSibling. + /// + EventEnumeratorError + LastError() const noexcept; + + /// + /// Sets State to None. + /// + void + Clear() noexcept; + + /// + /// + /// Starts decoding the specified EventHeader event: decodes the header and + /// positions the enumerator before the first item. + /// + /// On success, changes the state to BeforeFirstItem and returns true. + /// On failure, changes the state to None (not Error) and returns false. + /// + /// 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). + /// + /// + /// Set to tep_event->name, e.g. "MyProvider_L4K1". + /// Must follow the tracepoint name rules described in eventheader.h. + /// Set to strlen(tep_event->name). Must be less + /// than EVENTHEADER_NAME_MAX. + /// 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. + /// Set to size of the data, usually something like + /// tep_record->size - tep_event->format.fields[0].offset. + /// Set to the maximum number of MoveNext calls to allow when + /// processing this event (to guard against DoS attacks from a maliciously-crafted + /// event). + /// Returns false for failure. Check LastError for details. + 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; + + /// + /// + /// Positions the enumerator before the first item. + /// + /// PRECONDITION: Can be called when State != None, i.e. at any time after a + /// successful call to StartEvent, until a call to Clear. + /// + /// + void + Reset(uint32_t moveNextLimit = MoveNextLimitDefault) noexcept; + + /// + /// + /// 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. + /// + /// PRECONDITION: Can be called when State >= BeforeFirstItem, i.e. after a + /// successful call to StartEvent, until MoveNext returns false. + /// + /// Typically called in a loop until it returns false, e.g.: + /// + /// 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(); + /// + /// + /// + /// 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. + /// + bool + MoveNext() noexcept; + + /// + /// + /// 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. + /// + /// PRECONDITION: Can be called when State >= BeforeFirstItem, i.e. after a + /// successful call to StartEvent, until MoveNext returns false. + /// + /// If the current item is ArrayBegin or StructBegin, this efficiently moves + /// enumeration to AFTER the corresponding ArrayEnd or StructEnd. + /// + /// Otherwise, this is the same as MoveNext. + /// + /// 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. + /// + /// 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. + /// } + /// } + /// + /// + /// + /// 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. + /// + bool + MoveNextSibling() noexcept; + + /// + /// + /// 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. + /// + /// PRECONDITION: Can be called after a successful call to StartEvent, until + /// MoveNextMetadata returns false. + /// + /// 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. + /// + /// 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". + /// + /// Typically called in a loop until it returns false. + /// + /// if (!e.StartEvent(...)) return e.LastError(); + /// while (e.MoveNextMetadata()) + /// { + /// DoFieldDeclaration(e.GetItemInfo()); + /// } + /// return e.LastError(); + /// + /// + /// + /// 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. + /// + bool + MoveNextMetadata() noexcept; + + /// + /// + /// Gets information that applies to the current event, e.g. the event name, + /// provider name, options, level, keyword, etc. + /// + /// PRECONDITION: Can be called when State != None, i.e. at any time after a + /// successful call to StartEvent, until a call to Clear. + /// + /// + EventInfo + GetEventInfo() const noexcept; + + /// + /// + /// 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. + /// + /// PRECONDITION: Can be called when State > BeforeFirstItem, i.e. after MoveNext + /// returns true. + /// + /// + EventItemInfo + GetItemInfo() const noexcept; + + /// + /// + /// 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. + /// + /// PRECONDITION: Can be called when State != None, i.e. at any time after a + /// successful call to StartEvent, until a call to Clear. + /// + /// 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. + /// + /// + EventDataPosition + GetRawDataPosition() const noexcept; + + private: + + void + ResetImpl(uint32_t moveNextLimit) noexcept; + + bool + SkipStructMetadata() noexcept; + + bool + NextProperty() noexcept; + + /// + /// 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. + /// + FieldType + ReadFieldNameAndType() noexcept; + + /// + /// Requires m_metaEnd > typeOffset. + /// Reads encoding, format, tag starting at m_stackTop.NameOffset. + /// Updates m_stackTop.NextOffset. + /// On failure, returns Encoding = None. + /// + FieldType + ReadFieldType(uint16_t typeOffset) noexcept; + + bool + StartArray() noexcept; + + void + StartStruct() noexcept; + + bool + StartValue() noexcept; + + void + StartValueSimple() noexcept; + + template + 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; + }; + + /// + /// Enumeration states. + /// + enum EventEnumeratorState : uint8_t + { + /// + /// After construction, a call to Clear, or a failed StartEvent. + /// + EventEnumeratorState_None, + + /// + /// After an error has been returned by MoveNext. + /// + EventEnumeratorState_Error, + + /// + /// Positioned after the last item in the event. + /// + EventEnumeratorState_AfterLastItem, + + // MoveNext() is an invalid operation for all states above this line. + // MoveNext() is a valid operation for all states below this line. + + /// + /// Positioned before the first item in the event. + /// + EventEnumeratorState_BeforeFirstItem, + + // GetItemInfo() is an invalid operation for all states above this line. + // GetItemInfo() is a valid operation for all states below this line. + + /// + /// Positioned at an item with data (a field or an array element). + /// + EventEnumeratorState_Value, + + /// + /// Positioned before the first item in an array. + /// + EventEnumeratorState_ArrayBegin, + + /// + /// Positioned after the last item in an array. + /// + EventEnumeratorState_ArrayEnd, + + /// + /// Positioned before the first item in a struct. + /// + EventEnumeratorState_StructBegin, + + /// + /// Positioned after the last item in a struct. + /// + EventEnumeratorState_StructEnd, + }; + + /// + /// Values for the LastError property. + /// + enum EventEnumeratorError : uint8_t + { + /// + /// No error. + /// + EventEnumeratorError_Success = 0, + + /// + /// Event is smaller than 8 bytes or larger than 2GB, + /// or TracepointName is longer than 255 characters. + /// + EventEnumeratorError_InvalidParameter = EINVAL, + + /// + /// Event does not follow the EventHeader naming/layout rules, + /// is big-endian, has unrecognized flags, or unrecognized types. + /// + EventEnumeratorError_NotSupported = ENOTSUP, + + /// + /// Resource usage limit (moveNextLimit) reached. + /// + EventEnumeratorError_ImplementationLimit = E2BIG, + + /// + /// Event has an out-of-range value. + /// + EventEnumeratorError_InvalidData = EBADMSG, + + /// + /// Event has more than 8 levels of nested structs. + /// + EventEnumeratorError_StackOverflow = EOVERFLOW, + + /// + /// Method call invalid for current State(). + /// + 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 diff --git a/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/include/eventheader/EventFormatter.h b/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/include/eventheader/EventFormatter.h new file mode 100644 index 0000000000000..c1fd823f74b87 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/include/eventheader/EventFormatter.h @@ -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 + +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(0), + EventFormatterMetaFlags metaFlags = static_cast(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(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(0), + EventFormatterMetaFlags metaFlags = static_cast(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(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 diff --git a/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/samples/CMakeLists.txt b/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/samples/CMakeLists.txt new file mode 100644 index 0000000000000..eb5bc50236684 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/samples/CMakeLists.txt @@ -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() diff --git a/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/samples/decode-file.cpp b/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/samples/decode-file.cpp new file mode 100644 index 0000000000000..a1efbee5b3fd6 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/samples/decode-file.cpp @@ -0,0 +1,173 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include + +#include +#include +#include + +#include +#include +#include + +#ifdef _WIN32 +#include +#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(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 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(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(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_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; +} diff --git a/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/samples/decode-live.cpp b/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/samples/decode-live.cpp new file mode 100644 index 0000000000000..aee51e0783ef9 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/samples/decode-live.cpp @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include + +extern "C" { +#include +} + +#include +#include + +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(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(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; +} diff --git a/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/src/CMakeLists.txt b/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/src/CMakeLists.txt new file mode 100644 index 0000000000000..9b2899e6f77aa --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/src/CMakeLists.txt @@ -0,0 +1,37 @@ +# eventheader-decode = libeventheader-decode, DECODE_HEADERS +add_library(eventheader-decode + EventEnumerator.cpp + EventFormatter.cpp) +target_include_directories(eventheader-decode + PUBLIC + "$" + "$") +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") diff --git a/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/src/EventEnumerator.cpp b/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/src/EventEnumerator.cpp new file mode 100644 index 0000000000000..307bda2d54848 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/src/EventEnumerator.cpp @@ -0,0 +1,1308 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 + +#include // UNALIGNED +#define bswap_16(u16) _byteswap_ushort(u16) + +#else // _WIN32 + +#include +#define UNALIGNED __attribute__((aligned(1))) + +#endif // _WIN32 + +using namespace eventheader_decode; + +template +static char const* +LowercaseHexToInt(char const* pch, char const* pchEnd, UINTnn* pVal) noexcept +{ + UINTnn val = 0; + for (; pch != pchEnd; pch += 1) + { + char const ch = *pch; + uint8_t nibble; + if (ch >= '0' && ch <= '9') + { + nibble = static_cast(ch - '0'); + } + else if (ch >= 'a' && ch <= 'f') + { + nibble = static_cast(ch - 'a' + 10); + } + else + { + break; + } + + val = static_cast((val << 4) + nibble); + } + + *pVal = val; + return pch; +} + +EventEnumerator::EventEnumerator() noexcept + : m_state(EventEnumeratorState_None) + , m_subState(SubState_None) + , m_lastError(EventEnumeratorError_Success) +{ + static_assert(sizeof(FieldType) == 4, "Bad FieldType size"); + return; +} + +EventEnumeratorState +EventEnumerator::State() const noexcept +{ + return m_state; +} + +EventEnumeratorError +EventEnumerator::LastError() const noexcept +{ + return m_lastError; +} + +void +EventEnumerator::Clear() noexcept +{ + SetNoneState(EventEnumeratorError_Success); +} + +bool +EventEnumerator::StartEvent( + _In_reads_bytes_(cchTracepointName) char const* pchTracepointName, + size_t cchTracepointName, + _In_reads_bytes_(cbData) void const* pData, + size_t cbData, + uint32_t moveNextLimit) noexcept +{ + static auto constexpr HostEndianFlag = + EVENTHEADER_LITTLE_ENDIAN + ? eventheader_flag_little_endian + : eventheader_flag_none; + + static auto constexpr KnownFlags = static_cast( + eventheader_flag_pointer64 | eventheader_flag_little_endian | eventheader_flag_extension); + + auto const eventBuf = static_cast(pData); + uint32_t eventPos = 0; + uint32_t const eventEnd = static_cast(cbData); + + if (cbData < sizeof(eventheader) || cbData > 0x7FFFFFFF) + { + // Not a supported event: size < 8 or size >= 2GB. + return SetNoneState(EventEnumeratorError_InvalidParameter); + } + + // Get event header and validate it. + + memcpy(&m_header, &eventBuf[eventPos], sizeof(eventheader)); + eventPos += sizeof(eventheader); + + if (m_header.flags != (m_header.flags & KnownFlags)) + { + // Not a supported event: unsupported flags. + return SetNoneState(EventEnumeratorError_NotSupported); + } + + m_needByteSwap = HostEndianFlag != (m_header.flags & eventheader_flag_little_endian); + if (m_needByteSwap) + { + m_header.id = bswap_16(m_header.id); + m_header.tag = bswap_16(m_header.tag); + } + + // Validate Tracepoint name (e.g. "ProviderName_L1K2..."), extract keyword. + + if (cchTracepointName >= EVENTHEADER_NAME_MAX) + { + // Not a supported event: name too long. + return SetNoneState(EventEnumeratorError_NotSupported); + } + + if (memchr(pchTracepointName, 0, cchTracepointName)) + { + // Not a supported event: name contains NUL character. + return SetNoneState(EventEnumeratorError_NotSupported); + } + + m_tracepointName = pchTracepointName; + m_tracepointNameLength = static_cast(cchTracepointName); + + auto const pAttribEnd = pchTracepointName + cchTracepointName; + auto pAttrib = pAttribEnd; + + // Find the last underscore in pchTracepointName. + for (;;) + { + if (pAttrib == pchTracepointName) + { + // Not a supported event: no underscore in name. + return SetNoneState(EventEnumeratorError_NotSupported); + } + + pAttrib -= 1; + if (*pAttrib == '_') + { + break; + } + } + + // Provider name is the part of the tracepoint name before the last underscore. + m_providerNameLength = static_cast(pAttrib - pchTracepointName); + + pAttrib += 1; // Attribs start after the underscore. + + if (pAttrib == pAttribEnd || 'L' != *pAttrib) + { + // Not a supported event: no level in name. + return SetNoneState(EventEnumeratorError_NotSupported); + } + + uint8_t attribLevel; + pAttrib = LowercaseHexToInt(pAttrib + 1, pAttribEnd, &attribLevel); + if (attribLevel != m_header.level) + { + // Not a supported event: name's level != header's level. + return SetNoneState(EventEnumeratorError_NotSupported); + } + + if (pAttrib == pAttribEnd || 'K' != *pAttrib) + { + // Not a supported event: no keyword in name. + return SetNoneState(EventEnumeratorError_NotSupported); + } + + pAttrib = LowercaseHexToInt(pAttrib + 1, pAttribEnd, &m_keyword); + + // Options start after the keyword attribute. + m_optionsIndex = static_cast(pAttrib - pchTracepointName); + + // Validate but ignore any other attributes. + + while (pAttrib != pAttribEnd) + { + char ch; + ch = *pAttrib; + if (ch < 'A' || 'Z' < ch) + { + // Invalid attribute start character. + return SetNoneState(EventEnumeratorError_NotSupported); + } + + // Skip attribute value chars. + for (pAttrib += 1; pAttrib != pAttribEnd; pAttrib += 1) + { + ch = *pAttrib; + if ((ch < '0' || '9' < ch) && (ch < 'a' || 'z' < ch)) + { + break; + } + } + } + + // Parse header extensions. + + m_metaBuf = nullptr; + m_metaEnd = 0; + m_activityIdBuf = nullptr; + m_activityIdSize = 0; + + if (0 != (m_header.flags & eventheader_flag_extension)) + { + eventheader_extension ext; + do + { + if (eventEnd - eventPos < sizeof(eventheader_extension)) + { + return SetNoneState(EventEnumeratorError_InvalidData); + } + + memcpy(&ext, eventBuf + eventPos, sizeof(eventheader_extension)); + eventPos += sizeof(eventheader_extension); + + if (m_needByteSwap) + { + ext.size = bswap_16(ext.size); + ext.kind = bswap_16(ext.kind); + } + + if (eventEnd - eventPos < ext.size) + { + return SetNoneState(EventEnumeratorError_InvalidData); + } + + switch (ext.kind & eventheader_extension_kind_value_mask) + { + case eventheader_extension_kind_invalid: + return SetNoneState(EventEnumeratorError_InvalidData); + + case eventheader_extension_kind_metadata: + if (m_metaBuf != nullptr) + { + // Multiple Metadata extensions. + return SetNoneState(EventEnumeratorError_InvalidData); + } + + m_metaBuf = &eventBuf[eventPos]; + m_metaEnd = ext.size; + break; + + case eventheader_extension_kind_activity_id: + if (m_activityIdBuf != nullptr || + (ext.size != 16 && ext.size != 32)) + { + // Multiple ActivityId extensions, or bad activity id size. + return SetNoneState(EventEnumeratorError_InvalidData); + } + + m_activityIdBuf = &eventBuf[eventPos]; + m_activityIdSize = static_cast(ext.size); + break; + + default: + break; // Ignore other extension types. + } + + eventPos += ext.size; + } while (0 != (ext.kind & eventheader_extension_kind_chain_flag)); + } + + if (m_metaBuf == nullptr) + { + // Not a supported event - no metadata extension. + return SetNoneState(EventEnumeratorError_NotSupported); + } + + m_eventNameSize = static_cast( + strnlen(reinterpret_cast(m_metaBuf), m_metaEnd)); + if (m_eventNameSize == m_metaEnd) + { + // Event name not nul-terminated. + return SetNoneState(EventEnumeratorError_InvalidData); + } + + m_dataBuf = &eventBuf[eventPos]; + m_dataEnd = eventEnd - eventPos; + + ResetImpl(moveNextLimit); + return true; +} + +void +EventEnumerator::Reset(uint32_t moveNextLimit) noexcept +{ + assert(m_state != EventEnumeratorState_None); // PRECONDITION + + if (m_state == EventEnumeratorState_None) + { + m_lastError = EventEnumeratorError_InvalidState; + } + else + { + ResetImpl(moveNextLimit); + } +} + +bool +EventEnumerator::MoveNext() noexcept +{ + assert(m_state >= EventEnumeratorState_BeforeFirstItem); // PRECONDITION + + if (m_moveNextRemaining == 0) + { + return SetErrorState(EventEnumeratorError_ImplementationLimit); + } + + m_moveNextRemaining -= 1; + + bool movedToItem; + + switch (m_subState) + { + default: + + assert(!"Unexpected substate."); + m_lastError = EventEnumeratorError_InvalidState; + movedToItem = false; + break; + + case SubState_BeforeFirstItem: + + assert(m_state == EventEnumeratorState_BeforeFirstItem); + movedToItem = NextProperty(); + break; + + case SubState_Value_Metadata: + + m_lastError = EventEnumeratorError_InvalidState; + movedToItem = false; + break; + + case SubState_Value_Scalar: + + assert(m_state == EventEnumeratorState_Value); + assert(m_fieldType.Encoding != event_field_encoding_struct); + assert(!m_stackTop.ArrayFlags); + assert(m_dataEnd - m_dataPosRaw >= m_itemSizeRaw); + + m_dataPosRaw += m_itemSizeRaw; + movedToItem = NextProperty(); + break; + + case SubState_Value_SimpleArrayElement: + + assert(m_state == EventEnumeratorState_Value); + assert(m_fieldType.Encoding != event_field_encoding_struct); + assert(m_stackTop.ArrayFlags); + assert(m_stackTop.ArrayIndex < m_stackTop.ArrayCount); + assert(m_elementSize != 0); // Eligible for fast path. + assert(m_dataEnd - m_dataPosRaw >= m_itemSizeRaw); + + m_dataPosRaw += m_itemSizeRaw; + m_stackTop.ArrayIndex += 1; + + if (m_stackTop.ArrayCount == m_stackTop.ArrayIndex) + { + // End of array. + SetEndState(EventEnumeratorState_ArrayEnd, SubState_ArrayEnd); + } + else + { + // Middle of array - get next element. + StartValueSimple(); // Fast path for simple array elements. + } + + movedToItem = true; + break; + + case SubState_Value_ComplexArrayElement: + + assert(m_state == EventEnumeratorState_Value); + assert(m_fieldType.Encoding != event_field_encoding_struct); + assert(m_stackTop.ArrayFlags); + assert(m_stackTop.ArrayIndex < m_stackTop.ArrayCount); + assert(m_elementSize == 0); // Not eligible for fast path. + assert(m_dataEnd - m_dataPosRaw >= m_itemSizeRaw); + + m_dataPosRaw += m_itemSizeRaw; + m_stackTop.ArrayIndex += 1; + + if (m_stackTop.ArrayCount == m_stackTop.ArrayIndex) + { + // End of array. + SetEndState(EventEnumeratorState_ArrayEnd, SubState_ArrayEnd); + movedToItem = true; + } + else + { + // Middle of array - get next element. + movedToItem = StartValue(); // Normal path for complex array elements. + } + + break; + + case SubState_ArrayBegin: + + assert(m_state == EventEnumeratorState_ArrayBegin); + assert(m_stackTop.ArrayFlags); + assert(m_stackTop.ArrayIndex == 0); + + if (m_stackTop.ArrayCount == 0) + { + // 0-length array. + SetEndState(EventEnumeratorState_ArrayEnd, SubState_ArrayEnd); + movedToItem = true; + } + else if (m_elementSize != 0) + { + // First element of simple array. + assert(m_fieldType.Encoding != event_field_encoding_struct); + m_itemSizeCooked = m_elementSize; + m_itemSizeRaw = m_elementSize; + SetState(EventEnumeratorState_Value, SubState_Value_SimpleArrayElement); + StartValueSimple(); + movedToItem = true; + } + else if (m_fieldType.Encoding != event_field_encoding_struct) + { + // First element of complex array. + SetState(EventEnumeratorState_Value, SubState_Value_ComplexArrayElement); + movedToItem = StartValue(); + } + else + { + // First element of array of struct. + StartStruct(); + movedToItem = true; + } + + break; + + case SubState_ArrayEnd: + + assert(m_state == EventEnumeratorState_ArrayEnd); + assert(m_stackTop.ArrayFlags); + assert(m_stackTop.ArrayCount == m_stackTop.ArrayIndex); + + // 0-length array of struct means we won't naturally traverse + // the child struct's metadata. Since m_stackTop.NextOffset + // won't get updated naturally, we need to update it manually. + if (m_fieldType.Encoding == event_field_encoding_struct && + m_stackTop.ArrayCount == 0 && + !SkipStructMetadata()) + { + movedToItem = false; + } + else + { + movedToItem = NextProperty(); + } + + break; + + case SubState_StructBegin: + + assert(m_state == EventEnumeratorState_StructBegin); + if (m_stackIndex == sizeof(m_stack) / sizeof(m_stack[0])) + { + movedToItem = SetErrorState(EventEnumeratorError_StackOverflow); + } + else + { + m_stack[m_stackIndex] = m_stackTop; + m_stackIndex += 1; + + m_stackTop.RemainingFieldCount = m_fieldType.Format; + // Parent's NextOffset is the correct starting point for the struct. + movedToItem = NextProperty(); + } + + break; + + case SubState_StructEnd: + + assert(m_state == EventEnumeratorState_StructEnd); + assert(m_fieldType.Encoding == event_field_encoding_struct); + assert(m_itemSizeRaw == 0); + + m_stackTop.ArrayIndex += 1; + + if (m_stackTop.ArrayCount != m_stackTop.ArrayIndex) + { + assert(m_stackTop.ArrayFlags); + assert(m_stackTop.ArrayIndex < m_stackTop.ArrayCount); + + // Middle of array - get next element. + StartStruct(); + movedToItem = true; + } + else if (m_stackTop.ArrayFlags) + { + // End of array. + SetEndState(EventEnumeratorState_ArrayEnd, SubState_ArrayEnd); + movedToItem = true; + } + else + { + // End of property - move to next property. + movedToItem = NextProperty(); + } + + break; + } + + return movedToItem; +} + +bool +EventEnumerator::MoveNextSibling() noexcept +{ + assert(m_state >= EventEnumeratorState_BeforeFirstItem); // PRECONDITION + + bool movedToItem; + int depth = 0; // May reach -1 if we start on ArrayEnd/StructEnd. + do + { + switch (m_state) + { + default: + // Same as MoveNext. + break; + + case EventEnumeratorState_ArrayEnd: + case EventEnumeratorState_StructEnd: + depth -= 1; + break; + + case EventEnumeratorState_StructBegin: + depth += 1; + break; + + case EventEnumeratorState_ArrayBegin: + if (m_elementSize == 0 || m_moveNextRemaining == 0) + { + // Use MoveNext for full processing. + depth += 1; + break; + } + else + { + // Array of simple elements - jump directly to next sibling. + assert(m_subState == SubState_ArrayBegin); + assert(m_fieldType.Encoding != event_field_encoding_struct); + assert(m_stackTop.ArrayFlags); + assert(m_stackTop.ArrayIndex == 0); + m_dataPosRaw += static_cast(m_stackTop.ArrayCount) * m_elementSize; + m_moveNextRemaining -= 1; + movedToItem = NextProperty(); + continue; // Skip MoveNext(). + } + } + + movedToItem = MoveNext(); + } while (movedToItem && depth > 0); + + return movedToItem; +} + +bool +EventEnumerator::MoveNextMetadata() noexcept +{ + if (m_subState != SubState_Value_Metadata) + { + assert(m_state == EventEnumeratorState_BeforeFirstItem); // PRECONDITION + + assert(m_subState == SubState_BeforeFirstItem); + m_stackTop.ArrayIndex = 0; + m_dataPosCooked = m_dataEnd; + m_itemSizeCooked = 0; + m_elementSize = 0; + SetState(EventEnumeratorState_Value, SubState_Value_Metadata); + } + + assert(m_state == EventEnumeratorState_Value); + + bool movedToItem; + if (m_stackTop.NextOffset != m_metaEnd) + { + m_stackTop.NameOffset = m_stackTop.NextOffset; + + m_fieldType = ReadFieldNameAndType(); + if (m_fieldType.Encoding == ReadFieldError) + { + movedToItem = SetErrorState(EventEnumeratorError_InvalidData); + } + else if ( + event_field_encoding_struct == (m_fieldType.Encoding & event_field_encoding_value_mask) && + m_fieldType.Format == 0) + { + // Struct must have at least 1 field (potential for DoS). + movedToItem = SetErrorState(EventEnumeratorError_InvalidData); + } + else if (0 == (m_fieldType.Encoding & EncodingCountMask)) + { + // Non-array. + + m_stackTop.ArrayCount = 1; + m_stackTop.ArrayFlags = 0; + movedToItem = true; + } + else if (event_field_encoding_varray_flag == (m_fieldType.Encoding & EncodingCountMask)) + { + // Runtime-variable array length. + + m_fieldType.Encoding = static_cast(m_fieldType.Encoding & event_field_encoding_value_mask); + m_stackTop.ArrayCount = 0; + m_stackTop.ArrayFlags = event_field_encoding_varray_flag; + movedToItem = true; + } + else if (event_field_encoding_carray_flag == (m_fieldType.Encoding & EncodingCountMask)) + { + // Compile-time-constant array length. + + if (m_metaEnd - m_stackTop.NextOffset < 2) + { + movedToItem = SetErrorState(EventEnumeratorError_InvalidData); + } + else + { + m_stackTop.ArrayCount = *reinterpret_cast(m_metaBuf + m_stackTop.NextOffset); + m_stackTop.ArrayFlags = event_field_encoding_carray_flag; + m_fieldType.Encoding = static_cast(m_fieldType.Encoding & event_field_encoding_value_mask); + m_stackTop.NextOffset += 2; + + if (m_needByteSwap) + { + m_stackTop.ArrayCount = bswap_16(m_stackTop.ArrayCount); + } + + if (m_stackTop.ArrayCount == 0) + { + // Constant-length array cannot have length of 0 (potential for DoS). + movedToItem = SetErrorState(EventEnumeratorError_InvalidData); + } + else + { + movedToItem = true; + } + } + } + else + { + movedToItem = SetErrorState(EventEnumeratorError_NotSupported); + } + } + else + { + // End of event. + + SetEndState(EventEnumeratorState_AfterLastItem, SubState_AfterLastItem); + movedToItem = false; // No more items. + } + + return movedToItem; +} + +EventInfo +EventEnumerator::GetEventInfo() const noexcept +{ + assert(m_state != EventEnumeratorState_None); // PRECONDITION + + EventInfo value; + value.Name = reinterpret_cast(m_metaBuf); + value.TracepointName = m_tracepointName; + value.ActivityId = m_activityIdBuf; + value.RelatedActivityId = m_activityIdSize >= 32 + ? m_activityIdBuf + 16 + : nullptr; + value.Header = m_header; + value.Keyword = m_keyword; + value.TracepointNameLength = m_tracepointNameLength; + value.ProviderNameLength = m_providerNameLength; + value.OptionsIndex = m_optionsIndex; + return value; +} + +EventItemInfo +EventEnumerator::GetItemInfo() const noexcept +{ + assert(m_state > EventEnumeratorState_BeforeFirstItem); // PRECONDITION + + EventItemInfo value; + value.Name = reinterpret_cast(m_metaBuf + m_stackTop.NameOffset); + value.ValueData = m_dataBuf + m_dataPosCooked; + value.ValueSize = m_itemSizeCooked; + value.ArrayIndex = m_stackTop.ArrayIndex; + value.ArrayCount = m_stackTop.ArrayCount; + value.ElementSize = m_elementSize; + value.Encoding = m_fieldType.Encoding; + value.Format = m_fieldType.Format; + value.NeedByteSwap = m_needByteSwap; + value.ArrayFlags = static_cast(m_stackTop.ArrayFlags); + value.FieldTag = m_fieldType.Tag; + return value; +} + +EventDataPosition +EventEnumerator::GetRawDataPosition() const noexcept +{ + assert(m_state != EventEnumeratorState_None); // PRECONDITION + + EventDataPosition value; + value.Data = m_dataBuf + m_dataPosRaw; + value.Size = m_dataEnd - m_dataPosRaw; + return value; +} + +void +EventEnumerator::ResetImpl(uint32_t moveNextLimit) noexcept +{ + m_dataPosRaw = 0; + m_moveNextRemaining = moveNextLimit; + m_stackTop.NextOffset = m_eventNameSize + 1; + m_stackTop.RemainingFieldCount = 255; // Go until we reach end of metadata. + m_stackIndex = 0; + SetState(EventEnumeratorState_BeforeFirstItem, SubState_BeforeFirstItem); + m_lastError = EventEnumeratorError_Success; +} + +/* +Sets m_stackTop.NextOffset to skip the struct's fields. +*/ +bool +EventEnumerator::SkipStructMetadata() noexcept +{ + assert(m_fieldType.Encoding == event_field_encoding_struct); + + bool ok; + for (uint32_t remainingFieldCount = m_fieldType.Format;; + remainingFieldCount -= 1) + { + // It's a bit unusual but completely legal and fully supported to reach + // end-of-metadata before remainingFieldCount == 0. + if (remainingFieldCount == 0 || m_stackTop.NextOffset == m_metaEnd) + { + ok = true; + break; + } + + m_stackTop.NameOffset = m_stackTop.NextOffset; + + // Minimal validation, then skip the field: + + auto type = ReadFieldNameAndType(); + if (type.Encoding == ReadFieldError) + { + ok = SetErrorState(EventEnumeratorError_InvalidData); + break; + } + + if (event_field_encoding_struct == (type.Encoding & event_field_encoding_value_mask)) + { + remainingFieldCount += type.Format; + } + + if (0 == (type.Encoding & event_field_encoding_carray_flag)) + { + // Scalar or runtime length. We're done with the field. + } + else if (event_field_encoding_carray_flag == (type.Encoding & EncodingCountMask)) + { + // FlagCArray is set, FlagVArray is unset. + // Compile-time-constant array length. + // Skip the array length in metadata. + + if (m_metaEnd - m_stackTop.NextOffset < 2) + { + ok = SetErrorState(EventEnumeratorError_InvalidData); + break; + } + + m_stackTop.NextOffset += 2; + } + else + { + // Both FlagCArray and FlagVArray are set (reserved encoding). + ok = SetErrorState(EventEnumeratorError_NotSupported); + break; + } + } + + return ok; +} + +/* +Inputs: m_stackTop.RemainingFieldCount, m_stackTop.NextOffset, m_stackIndex, m_dataPosRaw. +*/ +bool +EventEnumerator::NextProperty() noexcept +{ + bool movedToItem; + if (m_stackTop.RemainingFieldCount != 0 && + m_stackTop.NextOffset != m_metaEnd) + { + m_stackTop.RemainingFieldCount -= 1; + m_stackTop.ArrayIndex = 0; + m_stackTop.NameOffset = m_stackTop.NextOffset; + + // Decode a field: + + m_fieldType = ReadFieldNameAndType(); + if (m_fieldType.Encoding == ReadFieldError) + { + movedToItem = SetErrorState(EventEnumeratorError_InvalidData); + } + else if (0 == (m_fieldType.Encoding & EncodingCountMask)) + { + // Non-array. + + m_stackTop.ArrayCount = 1; + m_stackTop.ArrayFlags = 0; + if (event_field_encoding_struct != (m_fieldType.Encoding & event_field_encoding_value_mask)) + { + SetState(EventEnumeratorState_Value, SubState_Value_Scalar); + movedToItem = StartValue(); + } + else if (m_fieldType.Format == 0) + { + // Struct must have at least 1 field (potential for DoS). + movedToItem = SetErrorState(EventEnumeratorError_InvalidData); + } + else + { + StartStruct(); + movedToItem = true; + } + } + else if (event_field_encoding_varray_flag == (m_fieldType.Encoding & EncodingCountMask)) + { + // Runtime-variable array length. + + if (m_dataEnd - m_dataPosRaw < 2) + { + movedToItem = SetErrorState(EventEnumeratorError_InvalidData); + } + else + { + m_stackTop.ArrayCount = *reinterpret_cast(m_dataBuf + m_dataPosRaw); + m_dataPosRaw += 2; + + if (m_needByteSwap) + { + m_stackTop.ArrayCount = bswap_16(m_stackTop.ArrayCount); + } + + movedToItem = StartArray(); // StartArray will set flags. + } + } + else if (event_field_encoding_carray_flag == (m_fieldType.Encoding & EncodingCountMask)) + { + // Compile-time-constant array length. + + if (m_metaEnd - m_stackTop.NextOffset < 2) + { + movedToItem = SetErrorState(EventEnumeratorError_InvalidData); + } + else + { + m_stackTop.ArrayCount = *reinterpret_cast(m_metaBuf + m_stackTop.NextOffset); + m_stackTop.NextOffset += 2; + + if (m_needByteSwap) + { + m_stackTop.ArrayCount = bswap_16(m_stackTop.ArrayCount); + } + + if (m_stackTop.ArrayCount == 0) + { + // Constant-length array cannot have length of 0 (potential for DoS). + movedToItem = SetErrorState(EventEnumeratorError_InvalidData); + } + else + { + movedToItem = StartArray(); // StartArray will set flags. + } + } + } + else + { + movedToItem = SetErrorState(EventEnumeratorError_NotSupported); + } + } + else if (m_stackIndex != 0) + { + // End of struct. + // It's a bit unusual but completely legal and fully supported to reach + // end-of-metadata before RemainingFieldCount == 0. + + // Pop child from stack. + m_stackIndex -= 1; + auto const childMetadataOffset = m_stackTop.NextOffset; + m_stackTop = m_stack[m_stackIndex]; + + m_fieldType = ReadFieldType( + static_cast(m_stackTop.NameOffset + m_stackTop.NameSize + 1u)); + assert(event_field_encoding_struct == (m_fieldType.Encoding & event_field_encoding_value_mask)); + m_fieldType.Encoding = event_field_encoding_struct; // Mask off array flags. + m_elementSize = 0; + + // Unless parent is in the middle of an array, we need to set the + // "next field" position to the child's metadata position. + assert(m_stackTop.ArrayIndex < m_stackTop.ArrayCount); + if (m_stackTop.ArrayIndex + 1 == m_stackTop.ArrayCount) + { + m_stackTop.NextOffset = childMetadataOffset; + } + + SetEndState(EventEnumeratorState_StructEnd, SubState_StructEnd); + movedToItem = true; + } + else + { + // End of event. + + if (m_stackTop.NextOffset != m_metaEnd) + { + // Event has metadata for more than MaxTopLevelProperties. + movedToItem = SetErrorState(EventEnumeratorError_NotSupported); + } + else + { + SetEndState(EventEnumeratorState_AfterLastItem, SubState_AfterLastItem); + movedToItem = false; // No more items. + } + } + + return movedToItem; +} + +EventEnumerator::FieldType +EventEnumerator::ReadFieldNameAndType() noexcept +{ + uint16_t pos = m_stackTop.NameOffset; + assert(m_metaEnd >= pos); + + auto const fieldName = reinterpret_cast(m_metaBuf + pos); + pos += static_cast(strnlen(fieldName, m_metaEnd - pos)); + + if (m_metaEnd - pos < 2) + { + // Missing nul termination or missing encoding. + return { ReadFieldError, event_field_format_default, 0 }; + } + else + { + m_stackTop.NameSize = pos - m_stackTop.NameOffset; + return ReadFieldType(pos + 1); + } +} + +EventEnumerator::FieldType +EventEnumerator::ReadFieldType(uint16_t typeOffset) noexcept +{ + uint16_t pos = typeOffset; + assert(m_metaEnd > pos); + + auto encoding = static_cast(m_metaBuf[pos]); + pos += 1; + + auto format = event_field_format_default; + uint16_t tag = 0; + if (0 != (encoding & event_field_encoding_chain_flag)) + { + if (m_metaEnd == pos) + { + // Missing format. + encoding = ReadFieldError; + } + else + { + format = static_cast(m_metaBuf[pos]); + pos += 1; + if (0 != (format & event_field_format_chain_flag)) + { + if (m_metaEnd - pos < 2) + { + // Missing tag. + encoding = ReadFieldError; + } + else + { + tag = *reinterpret_cast(m_metaBuf + pos); + pos += 2; + + if (m_needByteSwap) + { + tag = bswap_16(tag); + } + } + } + } + } + + m_stackTop.NextOffset = pos; + return { + static_cast(encoding & ~event_field_encoding_chain_flag), + static_cast(format & ~event_field_format_chain_flag), + tag }; +} + +bool +EventEnumerator::StartArray() noexcept +{ + m_stackTop.ArrayFlags = m_fieldType.Encoding & EncodingCountMask; + m_fieldType.Encoding = static_cast(m_fieldType.Encoding & event_field_encoding_value_mask); + m_elementSize = 0; + m_itemSizeRaw = 0; + m_dataPosCooked = m_dataPosRaw; + m_itemSizeCooked = 0; + SetState(EventEnumeratorState_ArrayBegin, SubState_ArrayBegin); + + // Determine the m_elementSize value. + bool movedToItem; + switch (m_fieldType.Encoding) + { + case event_field_encoding_struct: + movedToItem = true; + goto Done; + + case event_field_encoding_value8: + m_elementSize = 1; + break; + + case event_field_encoding_value16: + m_elementSize = 2; + break; + + case event_field_encoding_value32: + m_elementSize = 4; + break; + + case event_field_encoding_value64: + m_elementSize = 8; + break; + + case event_field_encoding_value128: + m_elementSize = 16; + break; + + case event_field_encoding_zstring_char8: + case event_field_encoding_zstring_char16: + case event_field_encoding_zstring_char32: + case event_field_encoding_string_length16_char8: + case event_field_encoding_string_length16_char16: + case event_field_encoding_string_length16_char32: + movedToItem = true; + goto Done; + + case event_field_encoding_invalid: + movedToItem = SetErrorState(EventEnumeratorError_InvalidData); + goto Done; + + default: + movedToItem = SetErrorState(EventEnumeratorError_NotSupported); + goto Done; + } + + // For simple array element types, validate that Count * m_elementSize <= RemainingSize. + // That way we can skip per-element validation and we can safely expose the array data + // during ArrayBegin. + { + unsigned const cbRemaining = m_dataEnd - m_dataPosRaw; + unsigned const cbArray = static_cast(m_elementSize) * m_stackTop.ArrayCount; + if (cbRemaining < cbArray) + { + movedToItem = SetErrorState(EventEnumeratorError_InvalidData); + } + else + { + m_itemSizeRaw = m_itemSizeCooked = cbArray; + movedToItem = true; + } + } + +Done: + + return movedToItem; +} + +void +EventEnumerator::StartStruct() noexcept +{ + assert(m_fieldType.Encoding == event_field_encoding_struct); + m_elementSize = 0; + m_itemSizeRaw = 0; + m_dataPosCooked = m_dataPosRaw; + m_itemSizeCooked = 0; + SetState(EventEnumeratorState_StructBegin, SubState_StructBegin); +} + +bool +EventEnumerator::StartValue() noexcept +{ + unsigned const cbRemaining = m_dataEnd - m_dataPosRaw; + + assert(m_state == EventEnumeratorState_Value); + assert(m_fieldType.Encoding == + (m_metaBuf[m_stackTop.NameOffset + m_stackTop.NameSize + 1] & event_field_encoding_value_mask)); + m_dataPosCooked = m_dataPosRaw; + m_elementSize = 0; + + bool movedToItem; + switch (m_fieldType.Encoding) + { + case event_field_encoding_value8: + m_itemSizeRaw = m_itemSizeCooked = m_elementSize = 1; + if (m_itemSizeRaw <= cbRemaining) + { + movedToItem = true; + goto Done; + } + break; + + case event_field_encoding_value16: + m_itemSizeRaw = m_itemSizeCooked = m_elementSize = 2; + if (m_itemSizeRaw <= cbRemaining) + { + movedToItem = true; + goto Done; + } + break; + + case event_field_encoding_value32: + m_itemSizeRaw = m_itemSizeCooked = m_elementSize = 4; + if (m_itemSizeRaw <= cbRemaining) + { + movedToItem = true; + goto Done; + } + break; + + case event_field_encoding_value64: + m_itemSizeRaw = m_itemSizeCooked = m_elementSize = 8; + if (m_itemSizeRaw <= cbRemaining) + { + movedToItem = true; + goto Done; + } + break; + + case event_field_encoding_value128: + m_itemSizeRaw = m_itemSizeCooked = m_elementSize = 16; + if (m_itemSizeRaw <= cbRemaining) + { + movedToItem = true; + goto Done; + } + break; + + case event_field_encoding_zstring_char8: + StartValueStringNul(); + break; + + case event_field_encoding_zstring_char16: + StartValueStringNul(); + break; + + case event_field_encoding_zstring_char32: + StartValueStringNul(); + break; + + case event_field_encoding_string_length16_char8: + StartValueStringLength16(0); + break; + + case event_field_encoding_string_length16_char16: + StartValueStringLength16(1); + break; + + case event_field_encoding_string_length16_char32: + StartValueStringLength16(2); + break; + + case event_field_encoding_invalid: + case event_field_encoding_struct: // Should never happen. + default: + assert(m_fieldType.Encoding != event_field_encoding_struct); + m_itemSizeRaw = m_itemSizeCooked = 0; + movedToItem = SetErrorState(EventEnumeratorError_InvalidData); + goto Done; + } + + if (cbRemaining < m_itemSizeRaw) + { + m_itemSizeRaw = m_itemSizeCooked = 0; + movedToItem = SetErrorState(EventEnumeratorError_InvalidData); + } + else + { + movedToItem = true; + } + +Done: + + return movedToItem; +} + +void +EventEnumerator::StartValueSimple() noexcept +{ + assert(m_stackTop.ArrayIndex < m_stackTop.ArrayCount); + assert(m_stackTop.ArrayFlags); + assert(m_fieldType.Encoding != event_field_encoding_struct); + assert(m_elementSize != 0); + assert(m_itemSizeCooked == m_elementSize); + assert(m_itemSizeRaw == m_elementSize); + assert(m_dataEnd >= m_dataPosRaw + m_itemSizeRaw); + assert(m_state == EventEnumeratorState_Value); + m_dataPosCooked = m_dataPosRaw; +} + +template +void +EventEnumerator::StartValueStringNul() noexcept +{ + // cch = strnlen(value, cchRemaining) + CH const UNALIGNED* pch = reinterpret_cast(m_dataBuf + m_dataPosRaw); + uint32_t const cchMax = (m_dataEnd - m_dataPosRaw) / sizeof(CH); + uint32_t cch; + for (cch = 0; cch != cchMax && pch[cch] != 0; cch += 1) {} + + m_itemSizeCooked = static_cast(cch * sizeof(CH)); + m_itemSizeRaw = m_itemSizeCooked + sizeof(CH); +} + +void +EventEnumerator::StartValueStringLength16(uint8_t charSizeShift) noexcept +{ + unsigned const cbRemaining = m_dataEnd - m_dataPosRaw; + if (cbRemaining < sizeof(uint16_t)) + { + m_itemSizeRaw = sizeof(uint16_t); + } + else + { + m_dataPosCooked = m_dataPosRaw + sizeof(uint16_t); + + auto cch = *reinterpret_cast(m_dataBuf + m_dataPosRaw); + if (m_needByteSwap) + { + cch = bswap_16(cch); + } + + m_itemSizeCooked = cch << charSizeShift; + m_itemSizeRaw = m_itemSizeCooked + sizeof(uint16_t); + } +} + +void +EventEnumerator::SetState(EventEnumeratorState newState, SubState newSubState) noexcept +{ + m_state = newState; + m_subState = newSubState; +} + +/* +Resets: m_dataPosCooked, m_itemSizeCooked, m_itemSizeRaw, m_state. +*/ +void +EventEnumerator::SetEndState(EventEnumeratorState newState, SubState newSubState) noexcept +{ + m_dataPosCooked = m_dataPosRaw; + m_itemSizeRaw = 0; + m_itemSizeCooked = 0; + m_state = newState; + m_subState = newSubState; +} + +bool +EventEnumerator::SetNoneState(EventEnumeratorError error) noexcept +{ + m_dataBuf = nullptr; + m_metaBuf = nullptr; + m_tracepointName = {}; + m_state = EventEnumeratorState_None; + m_subState = SubState_None; + m_lastError = error; + return false; +} + +bool +EventEnumerator::SetErrorState(EventEnumeratorError error) noexcept +{ + m_state = EventEnumeratorState_Error; + m_subState = SubState_Error; + m_lastError = error; + return false; +} diff --git a/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/src/EventFormatter.cpp b/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/src/EventFormatter.cpp new file mode 100644 index 0000000000000..5a6369cb615b9 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/src/EventFormatter.cpp @@ -0,0 +1,2234 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#ifndef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 64 +#endif + +#ifndef _TIME_BITS +#define _TIME_BITS 64 +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 + +#include +#include +#define bswap_16(u16) _byteswap_ushort(u16) +#define bswap_32(u32) _byteswap_ulong(u32) +#define bswap_64(u64) _byteswap_uint64(u64) +#define be16toh(u16) _byteswap_ushort(u16) + +#else // _WIN32 + +#include +#include + +#endif // _WIN32 + +#ifndef UNALIGNED +#define UNALIGNED __attribute__((aligned(1))) +#endif + +#ifdef _Printf_format_string_ +#define _Printf_format_func_(formatIndex, argIndex) +#else +#define _Printf_format_string_ +#define _Printf_format_func_(formatIndex, argIndex) \ + __attribute__((__format__(__printf__, formatIndex, argIndex))) +#endif + +#ifndef __fallthrough +#define __fallthrough __attribute__((__fallthrough__)) +#endif + +using namespace std::string_view_literals; +using namespace eventheader_decode; +using namespace tracepoint_decode; + +struct SwapNo +{ + uint8_t operator()(uint8_t val) const { return val; } + uint16_t operator()(uint16_t val) const { return val; } + uint32_t operator()(uint32_t val) const { return val; } +}; + +struct SwapYes +{ + uint16_t operator()(uint16_t val) const { return bswap_16(val); } + uint32_t operator()(uint32_t val) const { return bswap_32(val); } +}; + +class StringBuilder +{ + char* m_pDest; + char const* m_pDestEnd; + std::string& m_dest; + size_t m_destCommitSize; + bool const m_wantJsonSpace; + bool const m_wantFieldTag; + bool m_needJsonComma; + +#ifdef NDEBUG + +#define WriteBegin(cchWorstCase) ((void)0) +#define WriteEnd() ((void)0) + +#else // NDEBUG + +#define WriteBegin(cchWorstCase) \ + char const* const _pLimit = m_pDest + (cchWorstCase); \ + assert(m_pDest <= _pLimit); \ + assert(_pLimit <= m_pDestEnd) +#define WriteEnd() \ + assert(m_pDest <= _pLimit); \ + assert(_pLimit <= m_pDestEnd) + +#endif // NDEBUG + +public: + + StringBuilder(StringBuilder const&) = delete; + void operator=(StringBuilder const&) = delete; + + ~StringBuilder() + { + AssertInvariants(); + m_dest.erase(m_destCommitSize); + } + + StringBuilder(std::string& dest, EventFormatterJsonFlags jsonFlags) noexcept + : m_pDest(dest.data() + dest.size()) + , m_pDestEnd(dest.data() + dest.size()) + , m_dest(dest) + , m_destCommitSize(dest.size()) + , m_wantJsonSpace(jsonFlags & EventFormatterJsonFlags_Space) + , m_wantFieldTag(jsonFlags & EventFormatterJsonFlags_FieldTag) + , m_needJsonComma(false) + { + AssertInvariants(); + } + + bool + WantFieldTag() const noexcept + { + return m_wantFieldTag; + } + + size_t Room() const noexcept + { + return m_pDestEnd - m_pDest; + } + + void + EnsureRoom(size_t roomNeeded) noexcept(false) + { + if (static_cast(m_pDestEnd - m_pDest) < roomNeeded) + { + GrowRoom(roomNeeded); + } + } + + void + Commit() noexcept + { + AssertInvariants(); + m_destCommitSize = m_pDest - m_dest.data(); + } + + // Requires: there is room for utf8.size() chars. + void + WriteUtf8(std::string_view utf8) noexcept + { + WriteBegin(utf8.size()); + memcpy(m_pDest, utf8.data(), utf8.size()); + m_pDest += utf8.size(); + WriteEnd(); + } + + // Requires: there is room for 1 char. + void + WriteUtf8Byte(uint8_t utf8Byte) noexcept + { + assert(m_pDest < m_pDestEnd); + *m_pDest++ = utf8Byte; + } + + // Requires: there is room for 1 char. + // Writes 0..1 chars, either [] or ["]. + void + WriteQuoteIf(bool condition) noexcept + { + assert(m_pDest < m_pDestEnd); + *m_pDest = '"'; + m_pDest += condition; + } + + // Requires: there is room for 7 chars. + // Writes: 1..7 UTF-8 bytes, e.g. [a]. + void + WriteUcsChar(uint32_t ucs4) noexcept + { + WriteBegin(7); + + if (ucs4 >= 0x80) + { + WriteUcsNonAsciiChar(ucs4); + } + else + { + *m_pDest++ = static_cast(ucs4); + } + + WriteEnd(); + } + + // Requires: there is room for 7 chars. + // Requires: nonAsciiUcs4 >= 0x80. + // Writes: 2..7 UTF-8 bytes. + void + WriteUcsNonAsciiChar(uint32_t nonAsciiUcs4) noexcept + { + WriteBegin(7); + assert(nonAsciiUcs4 >= 0x80); + + // Note that this algorithm intentionally accepts non-compliant data + // (surrogates and values above 0x10FFFF). We want to accurately + // forward exactly what we found in the event. + + if (nonAsciiUcs4 < 0x800) + { + *m_pDest++ = static_cast(((nonAsciiUcs4 >> 6)) | 0xc0); + *m_pDest++ = static_cast(((nonAsciiUcs4) & 0x3f) | 0x80); + } + else if (nonAsciiUcs4 < 0x10000) + { + *m_pDest++ = static_cast(((nonAsciiUcs4 >> 12)) | 0xe0); + *m_pDest++ = static_cast(((nonAsciiUcs4 >> 6) & 0x3f) | 0x80); + *m_pDest++ = static_cast(((nonAsciiUcs4) & 0x3f) | 0x80); + } + else if (nonAsciiUcs4 < 0x200000) + { + *m_pDest++ = static_cast(((nonAsciiUcs4 >> 18)) | 0xf0); + *m_pDest++ = static_cast(((nonAsciiUcs4 >> 12) & 0x3f) | 0x80); + *m_pDest++ = static_cast(((nonAsciiUcs4 >> 6) & 0x3f) | 0x80); + *m_pDest++ = static_cast(((nonAsciiUcs4) & 0x3f) | 0x80); + } + else if (nonAsciiUcs4 < 0x4000000) + { + *m_pDest++ = static_cast(((nonAsciiUcs4 >> 24)) | 0xf8); + *m_pDest++ = static_cast(((nonAsciiUcs4 >> 18) & 0x3f) | 0x80); + *m_pDest++ = static_cast(((nonAsciiUcs4 >> 12) & 0x3f) | 0x80); + *m_pDest++ = static_cast(((nonAsciiUcs4 >> 6) & 0x3f) | 0x80); + *m_pDest++ = static_cast(((nonAsciiUcs4) & 0x3f) | 0x80); + } + else if (nonAsciiUcs4 < 0x80000000) + { + *m_pDest++ = static_cast(((nonAsciiUcs4 >> 30)) | 0xfc); + *m_pDest++ = static_cast(((nonAsciiUcs4 >> 24) & 0x3f) | 0x80); + *m_pDest++ = static_cast(((nonAsciiUcs4 >> 18) & 0x3f) | 0x80); + *m_pDest++ = static_cast(((nonAsciiUcs4 >> 12) & 0x3f) | 0x80); + *m_pDest++ = static_cast(((nonAsciiUcs4 >> 6) & 0x3f) | 0x80); + *m_pDest++ = static_cast(((nonAsciiUcs4) & 0x3f) | 0x80); + } + else + { + *m_pDest++ = static_cast(0xfe); + *m_pDest++ = static_cast(((nonAsciiUcs4 >> 30) & 0x3f) | 0x80); + *m_pDest++ = static_cast(((nonAsciiUcs4 >> 24) & 0x3f) | 0x80); + *m_pDest++ = static_cast(((nonAsciiUcs4 >> 18) & 0x3f) | 0x80); + *m_pDest++ = static_cast(((nonAsciiUcs4 >> 12) & 0x3f) | 0x80); + *m_pDest++ = static_cast(((nonAsciiUcs4 >> 6) & 0x3f) | 0x80); + *m_pDest++ = static_cast(((nonAsciiUcs4) & 0x3f) | 0x80); + } + + WriteEnd(); + } + + // Requires: there is room for 2 chars. + // Writes 2 chars, e.g. [7f]. + void + WriteHexByte(uint8_t val) noexcept + { + WriteBegin(2); + static char const* const digits = "0123456789abcdef"; + *m_pDest++ = digits[val >> 4]; + *m_pDest++ = digits[val & 0xf]; + WriteEnd(); + } + + // Requires: there is room for cchWorstCase chars. + // Requires: the printf format can generate no more than cchWorstCase chars. + // Writes up to cchWorstCase plus NUL. + void + WritePrintf( + size_t cchWorstCase, + _Printf_format_string_ char const* format, + ...) noexcept _Printf_format_func_(3, 4) + { + WriteBegin(cchWorstCase); + + va_list args; + va_start(args, format); + unsigned const cchNeeded = vsnprintf(m_pDest, cchWorstCase + 1, format, args); + va_end(args); + + assert(cchNeeded <= cchWorstCase); + m_pDest += (cchNeeded <= cchWorstCase ? cchNeeded : cchWorstCase); + WriteEnd(); + } + + // Requires: there is room for valSize * 3 - 1 chars. + // Requires: valSize != 0. + // Writes: valSize * 3 - 1, e.g. [00 11 22]. + void + WriteHexBytes(void const* val, size_t valSize) noexcept + { + assert(valSize != 0); + WriteBegin(valSize * 3 - 1); + + auto const pbVal = static_cast(val); + WriteHexByte(pbVal[0]); + for (size_t i = 1; i != valSize; i += 1) + { + *m_pDest++ = ' '; + WriteHexByte(pbVal[i]); + } + + WriteEnd(); + } + + // Requires: there is room for 15 chars. + // Writes 7..15 chars, e.g. [0.0.0.0] or [255.255.255.255]. + void + WriteIPv4(uint32_t val) noexcept + { + auto constexpr DestWriteMax = 15u; + WriteBegin(DestWriteMax); + +#if _WIN32 + auto const p = reinterpret_cast(&val); + WritePrintf(DestWriteMax, "%u.%u.%u.%u", + p[0], p[1], p[2], p[3]); +#else // _WIN32 + // INET_ADDRSTRLEN includes 1 nul. + static_assert(INET_ADDRSTRLEN - 1 == DestWriteMax, "WriteIPv4Val length"); + inet_ntop(AF_INET, &val, m_pDest, DestWriteMax + 1); + m_pDest += strnlen(m_pDest, DestWriteMax); +#endif // _WIN32 + + WriteEnd(); + } + + // Requires: there is room for 45 chars. + // Reads 16 bytes. + // Writes 15..45 chars, e.g. [0:0:0:0:0:0:0:0] or [ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255]. + void + WriteIPv6(void const* val) noexcept + { + auto constexpr DestWriteMax = 45u; + WriteBegin(DestWriteMax); + +#if _WIN32 + auto const p = static_cast(val); + WritePrintf(DestWriteMax, + "%x:%x:%x:%x:%x:%x:%x:%x", + bswap_16(p[0]), bswap_16(p[1]), bswap_16(p[2]), bswap_16(p[3]), + bswap_16(p[4]), bswap_16(p[5]), bswap_16(p[6]), bswap_16(p[7])); +#else // _WIN32 + // INET6_ADDRSTRLEN includes 1 nul. + static_assert(INET6_ADDRSTRLEN - 1 == DestWriteMax, "WriteIPv6Val length"); + inet_ntop(AF_INET6, val, m_pDest, DestWriteMax + 1); + m_pDest += strnlen(m_pDest, DestWriteMax); +#endif // _WIN32 + + WriteEnd(); + } + + // Requires: there is room for 36 chars. + // Reads 16 bytes. + // Writes 36 chars, e.g. [00000000-0000-0000-0000-000000000000]. + void + WriteUuid(void const* val) noexcept + { + WriteBegin(36); + + uint8_t const* const pVal = static_cast(val); + WriteHexByte(pVal[0]); + WriteHexByte(pVal[1]); + WriteHexByte(pVal[2]); + WriteHexByte(pVal[3]); + *m_pDest++ = '-'; + WriteHexByte(pVal[4]); + WriteHexByte(pVal[5]); + *m_pDest++ = '-'; + WriteHexByte(pVal[6]); + WriteHexByte(pVal[7]); + *m_pDest++ = '-'; + WriteHexByte(pVal[8]); + WriteHexByte(pVal[9]); + *m_pDest++ = '-'; + WriteHexByte(pVal[10]); + WriteHexByte(pVal[11]); + WriteHexByte(pVal[12]); + WriteHexByte(pVal[13]); + WriteHexByte(pVal[14]); + WriteHexByte(pVal[15]); + + WriteEnd(); + } + + // Requires: there is room for 26 chars. + // Writes 19..26 chars, e.g. [2022-01-01T01:01:01] or [TIME(18000000000000000000)]. + void + WriteDateTime(int64_t val) noexcept + { + auto const DestWriteMax = 26u; + WriteBegin(DestWriteMax); + +#if _WIN32 + if (-11644473600 <= val && val <= 910692730085) + { + int64_t ft = (val + 11644473600) * 10000000; + SYSTEMTIME st; + FileTimeToSystemTime(reinterpret_cast(&ft), &st); + WritePrintf(DestWriteMax, "%04u-%02u-%02uT%02u:%02u:%02u", + st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond); + } +#else // _WIN32 + struct tm tm; + if (gmtime_r(&val, &tm)) + { + WritePrintf(DestWriteMax, "%04d-%02u-%02uT%02u:%02u:%02u", + 1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); + } +#endif // _WIN32 + else + { + WritePrintf(DestWriteMax, "TIME(%" PRId64 ")", val); + } + + WriteEnd(); + } + + // Requires: there is room for 20 chars. + // Writes 6..20 chars, e.g. [EIO(5)] or [ENOTRECOVERABLE(131)]. + void + WriteErrno(uint32_t val) noexcept + { + auto const DestWriteMax = 20u; + + char const* str; + switch (val) + { + default: str = "ERRNO"; break; + case 1: str = "EPERM"; break; + case 2: str = "ENOENT"; break; + case 3: str = "ESRCH"; break; + case 4: str = "EINTR"; break; + case 5: str = "EIO"; break; + case 6: str = "ENXIO"; break; + case 7: str = "E2BIG"; break; + case 8: str = "ENOEXEC"; break; + case 9: str = "EBADF"; break; + case 10: str = "ECHILD"; break; + case 11: str = "EAGAIN"; break; + case 12: str = "ENOMEM"; break; + case 13: str = "EACCES"; break; + case 14: str = "EFAULT"; break; + case 15: str = "ENOTBLK"; break; + case 16: str = "EBUSY"; break; + case 17: str = "EEXIST"; break; + case 18: str = "EXDEV"; break; + case 19: str = "ENODEV"; break; + case 20: str = "ENOTDIR"; break; + case 21: str = "EISDIR"; break; + case 22: str = "EINVAL"; break; + case 23: str = "ENFILE"; break; + case 24: str = "EMFILE"; break; + case 25: str = "ENOTTY"; break; + case 26: str = "ETXTBSY"; break; + case 27: str = "EFBIG"; break; + case 28: str = "ENOSPC"; break; + case 29: str = "ESPIPE"; break; + case 30: str = "EROFS"; break; + case 31: str = "EMLINK"; break; + case 32: str = "EPIPE"; break; + case 33: str = "EDOM"; break; + case 34: str = "ERANGE"; break; + case 35: str = "EDEADLK"; break; + case 36: str = "ENAMETOOLONG"; break; + case 37: str = "ENOLCK"; break; + case 38: str = "ENOSYS"; break; + case 39: str = "ENOTEMPTY"; break; + case 40: str = "ELOOP"; break; + case 42: str = "ENOMSG"; break; + case 43: str = "EIDRM"; break; + case 44: str = "ECHRNG"; break; + case 45: str = "EL2NSYNC"; break; + case 46: str = "EL3HLT"; break; + case 47: str = "EL3RST"; break; + case 48: str = "ELNRNG"; break; + case 49: str = "EUNATCH"; break; + case 50: str = "ENOCSI"; break; + case 51: str = "EL2HLT"; break; + case 52: str = "EBADE"; break; + case 53: str = "EBADR"; break; + case 54: str = "EXFULL"; break; + case 55: str = "ENOANO"; break; + case 56: str = "EBADRQC"; break; + case 57: str = "EBADSLT"; break; + case 59: str = "EBFONT"; break; + case 60: str = "ENOSTR"; break; + case 61: str = "ENODATA"; break; + case 62: str = "ETIME"; break; + case 63: str = "ENOSR"; break; + case 64: str = "ENONET"; break; + case 65: str = "ENOPKG"; break; + case 66: str = "EREMOTE"; break; + case 67: str = "ENOLINK"; break; + case 68: str = "EADV"; break; + case 69: str = "ESRMNT"; break; + case 70: str = "ECOMM"; break; + case 71: str = "EPROTO"; break; + case 72: str = "EMULTIHOP"; break; + case 73: str = "EDOTDOT"; break; + case 74: str = "EBADMSG"; break; + case 75: str = "EOVERFLOW"; break; + case 76: str = "ENOTUNIQ"; break; + case 77: str = "EBADFD"; break; + case 78: str = "EREMCHG"; break; + case 79: str = "ELIBACC"; break; + case 80: str = "ELIBBAD"; break; + case 81: str = "ELIBSCN"; break; + case 82: str = "ELIBMAX"; break; + case 83: str = "ELIBEXEC"; break; + case 84: str = "EILSEQ"; break; + case 85: str = "ERESTART"; break; + case 86: str = "ESTRPIPE"; break; + case 87: str = "EUSERS"; break; + case 88: str = "ENOTSOCK"; break; + case 89: str = "EDESTADDRREQ"; break; + case 90: str = "EMSGSIZE"; break; + case 91: str = "EPROTOTYPE"; break; + case 92: str = "ENOPROTOOPT"; break; + case 93: str = "EPROTONOSUPPORT"; break; + case 94: str = "ESOCKTNOSUPPORT"; break; + case 95: str = "EOPNOTSUPP"; break; + case 96: str = "EPFNOSUPPORT"; break; + case 97: str = "EAFNOSUPPORT"; break; + case 98: str = "EADDRINUSE"; break; + case 99: str = "EADDRNOTAVAIL"; break; + case 100: str = "ENETDOWN"; break; + case 101: str = "ENETUNREACH"; break; + case 102: str = "ENETRESET"; break; + case 103: str = "ECONNABORTED"; break; + case 104: str = "ECONNRESET"; break; + case 105: str = "ENOBUFS"; break; + case 106: str = "EISCONN"; break; + case 107: str = "ENOTCONN"; break; + case 108: str = "ESHUTDOWN"; break; + case 109: str = "ETOOMANYREFS"; break; + case 110: str = "ETIMEDOUT"; break; + case 111: str = "ECONNREFUSED"; break; + case 112: str = "EHOSTDOWN"; break; + case 113: str = "EHOSTUNREACH"; break; + case 114: str = "EALREADY"; break; + case 115: str = "EINPROGRESS"; break; + case 116: str = "ESTALE"; break; + case 117: str = "EUCLEAN"; break; + case 118: str = "ENOTNAM"; break; + case 119: str = "ENAVAIL"; break; + case 120: str = "EISNAM"; break; + case 121: str = "EREMOTEIO"; break; + case 122: str = "EDQUOT"; break; + case 123: str = "ENOMEDIUM"; break; + case 124: str = "EMEDIUMTYPE"; break; + case 125: str = "ECANCELED"; break; + case 126: str = "ENOKEY"; break; + case 127: str = "EKEYEXPIRED"; break; + case 128: str = "EKEYREVOKED"; break; + case 129: str = "EKEYREJECTED"; break; + case 130: str = "EOWNERDEAD"; break; + case 131: str = "ENOTRECOVERABLE"; break; + case 132: str = "ERFKILL"; break; + case 133: str = "EHWPOISON"; break; + } + + WritePrintf(DestWriteMax, "%s(%d)", str, val); + } + + // Requires: there is room for 11 chars. + // Writes 1..11 chars, e.g. [false] or [-2000000000]. + void + WriteBoolean(int32_t boolVal) noexcept + { + auto const DestWriteMax = 11u; + WriteBegin(DestWriteMax); + + switch (boolVal) + { + case 0: + WriteUtf8("false"sv); + break; + case 1: + WriteUtf8("true"sv); + break; + default: + WritePrintf(DestWriteMax, "%d", boolVal); + break; + } + + WriteEnd(); + } + + // Returns true for ASCII control chars, double-quote, and backslash. + static constexpr bool + NeedsJsonEscape(uint8_t utf8Byte) noexcept + { + return utf8Byte < ' ' || utf8Byte == '"' || utf8Byte == '\\'; + } + + bool + WantJsonSpace() const noexcept + { + return m_wantJsonSpace; + } + + bool + NeedJsonComma() const noexcept + { + return m_needJsonComma; + } + + void + SetNeedJsonComma(bool value) noexcept + { + m_needJsonComma = value; + } + + // Requires: there is room for 1 char. + // WriteUtf8Byte('['); SetNeedJsonComma(false); + void + WriteJsonArrayBegin() noexcept + { + assert(m_pDest < m_pDestEnd); + *m_pDest++ = '['; + m_needJsonComma = false; + } + + // Requires: there is room for 1 char. + // WriteUtf8Byte(']'); SetNeedJsonComma(true); + void + WriteJsonArrayEnd() noexcept + { + assert(m_pDest < m_pDestEnd); + *m_pDest++ = ']'; + m_needJsonComma = true; + } + + // Requires: there is room for 1 char. + // WriteUtf8Byte('{'); SetNeedJsonComma(false); + void + WriteJsonStructBegin() noexcept + { + assert(m_pDest < m_pDestEnd); + *m_pDest++ = '{'; + m_needJsonComma = false; + } + + // Requires: there is room for 1 char. + // WriteUtf8Byte('}'); SetNeedJsonComma(true); + void + WriteJsonStructEnd() noexcept + { + assert(m_pDest < m_pDestEnd); + *m_pDest++ = '}'; + m_needJsonComma = true; + } + + // Requires: there is room for 1 char. + // Writes 0..1 chars, [] or [ ]. + void + WriteJsonSpaceIfWanted() noexcept + { + assert(m_pDest < m_pDestEnd); + *m_pDest = ' '; + m_pDest += m_wantJsonSpace; + } + + // Requires: there is room for 2 chars. + // If NeedJsonComma, writes ','. + // If WantJsonSpace, writes ' '. + // Sets NeedJsonComma = true. + void + WriteJsonCommaSpaceAsNeeded() noexcept + { + WriteBegin(2); + *m_pDest = ','; + m_pDest += m_needJsonComma; + *m_pDest = ' '; + m_pDest += m_wantJsonSpace; + m_needJsonComma = true; + WriteEnd(); + } + + // Requires: there is room for 6 chars. + // Requires: NeedsJsonEscape(ch). + // Writes e.g. [\\u00ff]. + void + WriteJsonEscapeChar(uint8_t utf8Byte) noexcept + { + WriteBegin(6); + assert(utf8Byte < 0x80); + + *m_pDest++ = '\\'; + switch (utf8Byte) + { + case '\\': *m_pDest++ = '\\'; break; + case '\"': *m_pDest++ = '"'; break; + case '\b': *m_pDest++ = 'b'; break; + case '\f': *m_pDest++ = 'f'; break; + case '\n': *m_pDest++ = 'n'; break; + case '\r': *m_pDest++ = 'r'; break; + case '\t': *m_pDest++ = 't'; break; + default: + *m_pDest++ = 'u'; + *m_pDest++ = '0'; + *m_pDest++ = '0'; + WriteHexByte(utf8Byte); + break; + } + + WriteEnd(); + } + + // Requires: there is room for 7 chars. + // Writes: 1..7 UTF-8 bytes, e.g. [a] or [\\u00ff]. + void + WriteUcsCharJsonEscaped(uint32_t ucs4) noexcept + { + WriteBegin(7); + if (ucs4 >= 0x80) + { + WriteUcsNonAsciiChar(ucs4); + } + else + { + char const ascii = static_cast(ucs4); + if (NeedsJsonEscape(ascii)) + { + WriteJsonEscapeChar(ascii); + } + else + { + *m_pDest++ = ascii; + } + } + WriteEnd(); + } + +private: + + void + AssertInvariants() noexcept + { + assert(m_pDest >= m_dest.data() + m_destCommitSize); + assert(m_pDest <= m_dest.data() + m_dest.size()); + assert(m_pDestEnd == m_dest.data() + m_dest.size()); + assert(m_destCommitSize <= m_dest.size()); + } + + void + GrowRoom(size_t roomNeeded) + { + AssertInvariants(); + size_t const curSize = m_pDest - m_dest.data(); + size_t const totalSize = curSize + roomNeeded; + size_t const newSize = totalSize < roomNeeded // Did it overflow? + ? ~static_cast(0) // Yes: trigger exception from resize. + : totalSize; + assert(m_dest.size() < newSize); + m_dest.resize(newSize); + m_pDest = m_dest.data() + curSize; + m_pDestEnd = m_dest.data() + m_dest.size(); + AssertInvariants(); + } +}; + +// Requires: there is room for 9 chars. +static void +WriteUcsVal( + StringBuilder& sb, + uint32_t ucs4, + bool json) noexcept +{ + assert(9u <= sb.Room()); + if (json) + { + sb.WriteUtf8Byte('"'); + sb.WriteUcsCharJsonEscaped(ucs4); // UCS1 + sb.WriteUtf8Byte('"'); + } + else + { + sb.WriteUcsChar(ucs4); // UCS1 + } +} + +static void +AppendUtf8( + StringBuilder& sb, + std::string_view utf8) +{ + sb.EnsureRoom(utf8.size()); + sb.WriteUtf8(utf8); +} + +static void +AppendUtf8JsonEscapedWithRoomReserved( + StringBuilder& sb, + std::string_view utf8, + size_t roomReserved) +{ + assert(roomReserved <= sb.Room()); + assert(roomReserved >= utf8.size()); + auto roomNeededForJsonEscape = roomReserved + 5; + for (auto utf8Byte : utf8) + { + if (sb.NeedsJsonEscape(utf8Byte)) + { + sb.EnsureRoom(roomNeededForJsonEscape); + sb.WriteJsonEscapeChar(utf8Byte); + } + else + { + sb.WriteUtf8Byte(utf8Byte); + } + + roomNeededForJsonEscape -= 1; + } +} + +static void +AppendUtf8JsonEscaped( + StringBuilder& sb, + std::string_view utf8, + size_t extraRoomNeeded) +{ + size_t const roomNeeded = utf8.size() + extraRoomNeeded; + sb.EnsureRoom(roomNeeded); + AppendUtf8JsonEscapedWithRoomReserved(sb, utf8, roomNeeded); + assert(sb.Room() >= extraRoomNeeded); +} + +static void +AppendUtf8Val( + StringBuilder& sb, + std::string_view utf8, + bool json) +{ + if (json) + { + sb.EnsureRoom(utf8.size() + 2); + sb.WriteUtf8Byte('"'); + AppendUtf8JsonEscaped(sb, utf8, 1); + sb.WriteUtf8Byte('"'); // 1 extra byte reserved above. + } + else + { + AppendUtf8(sb, utf8); + } +} + +template +static void +AppendUcs( + StringBuilder& sb, + CH const* pchUcs, + size_t cchUcs) +{ + Swapper swapper; + auto const pchEnd = pchUcs + cchUcs; + sb.EnsureRoom(pchEnd - pchUcs); + for (auto pch = pchUcs; pch != pchEnd; pch += 1) + { + uint32_t ucs4 = swapper(*pch); + if (ucs4 >= 0x80) + { + sb.EnsureRoom(pchEnd - pch + 6); + sb.WriteUcsNonAsciiChar(ucs4); + } + else + { + sb.WriteUtf8Byte(static_cast(ucs4)); + } + } +} + +// Guaranteed to reserve at least one byte more than necessary. +template +static void +AppendUcsJsonEscaped( + StringBuilder& sb, + CH const* pchUcs, + size_t cchUcs, + size_t extraRoomNeeded) +{ + Swapper swapper; + auto const pchEnd = pchUcs + cchUcs; + sb.EnsureRoom(pchEnd - pchUcs + extraRoomNeeded); + for (auto pch = pchUcs; pch != pchEnd; pch += 1) + { + uint32_t ucs4 = swapper(*pch); + if (ucs4 >= 0x80) + { + sb.EnsureRoom(pchEnd - pch + extraRoomNeeded + 6); + sb.WriteUcsNonAsciiChar(ucs4); + } + else if (auto const ascii = static_cast(ucs4); + sb.NeedsJsonEscape(ascii)) + { + sb.EnsureRoom(pchEnd - pch + extraRoomNeeded + 5); + sb.WriteJsonEscapeChar(ascii); + } + else + { + sb.WriteUtf8Byte(ascii); + } + } +} + +template +static void +AppendUcsVal( + StringBuilder& sb, + CH const* pchUcs, + size_t cchUcs, + bool json) +{ + if (json) + { + sb.EnsureRoom(cchUcs + 2); + sb.WriteUtf8Byte('"'); + AppendUcsJsonEscaped(sb, pchUcs, cchUcs, 1); + sb.WriteUtf8Byte('"'); // 1 extra byte reserved above. + } + else + { + AppendUcs(sb, pchUcs, cchUcs); + } +} + +template +static void +AppendUtf16Val( + StringBuilder& sb, + uint16_t const* pchUtf, + size_t cchUtf, + bool json) +{ + if (json) + { + sb.EnsureRoom(cchUtf + 2); + sb.WriteUtf8Byte('"'); + AppendUcsJsonEscaped(sb, pchUtf, cchUtf, 1); // TODO: Surrogates + sb.WriteUtf8Byte('"'); // 1 extra byte reserved above. + } + else + { + AppendUcs(sb, pchUtf, cchUtf); // TODO: Surrogates + } +} + +static bool +TryAppendUtfBomVal( + StringBuilder& sb, + void const* pUtf, + size_t cbUtf, + bool json) +{ + static uint32_t const Bom32SwapNo = 0x0000FEFF; + static uint32_t const Bom32SwapYes = 0xFFFE0000; + static uint16_t const Bom16SwapNo = 0xFEFF; + static uint16_t const Bom16SwapYes = 0xFFFE; + static uint8_t const Bom8[] = { 0xEF, 0xBB, 0xBF }; + + bool matchedBom; + if (cbUtf >= 4 && 0 == memcmp(pUtf, &Bom32SwapNo, 4)) + { + AppendUcsVal(sb, + static_cast(pUtf) + 1, + cbUtf / sizeof(uint32_t) - 1, + json); + matchedBom = true; + } + else if (cbUtf >= 4 && 0 == memcmp(pUtf, &Bom32SwapYes, 4)) + { + AppendUcsVal(sb, + static_cast(pUtf) + 1, + cbUtf / sizeof(uint32_t) - 1, + json); + matchedBom = true; + } + else if (cbUtf >= 2 && 0 == memcmp(pUtf, &Bom16SwapNo, 2)) + { + AppendUtf16Val(sb, + static_cast(pUtf) + 1, + cbUtf / sizeof(uint16_t) - 1, + json); + matchedBom = true; + } + else if (cbUtf >= 2 && 0 == memcmp(pUtf, &Bom16SwapYes, 2)) + { + AppendUtf16Val(sb, + static_cast(pUtf) + 1, + cbUtf / sizeof(uint16_t) - 1, + json); + matchedBom = true; + } + else if (cbUtf >= 3 && 0 == memcmp(pUtf, Bom8, 3)) + { + AppendUtf8Val(sb, + { static_cast(pUtf) + 3, cbUtf - 3 }, + json); + matchedBom = true; + } + else + { + matchedBom = false; + } + + return matchedBom; +} + +static void +AppendHexBytesVal( + StringBuilder& sb, + void const* val, + size_t valSize, + bool json) +{ + size_t const roomNeeded = (valSize * 3) + (json * 2u); + sb.EnsureRoom(roomNeeded); + + sb.WriteQuoteIf(json); + if (valSize != 0) + { + sb.WriteHexBytes(val, valSize); + } + sb.WriteQuoteIf(json); +} + +// e.g. [, ]. +static void +AppendJsonValueBegin( + StringBuilder& sb, + size_t extraRoomNeeded) +{ + sb.EnsureRoom(2 + extraRoomNeeded); + sb.WriteJsonCommaSpaceAsNeeded(); +} + +// e.g. [, "abc": ]. +static void +AppendJsonMemberBegin( + StringBuilder& sb, + uint16_t fieldTag, + std::string_view nameUtf8, + size_t extraRoomNeeded) +{ + unsigned const MemberNeeded = 17; // [, ";tag=0xFFFF": ] + size_t roomNeeded = MemberNeeded + nameUtf8.size() + extraRoomNeeded; + sb.EnsureRoom(roomNeeded); + + sb.WriteJsonCommaSpaceAsNeeded(); + sb.WriteUtf8Byte('"'); + + AppendUtf8JsonEscapedWithRoomReserved(sb, nameUtf8, roomNeeded - 3); + + if (fieldTag != 0 && sb.WantFieldTag()) + { + sb.WritePrintf(11, ";tag=0x%X", fieldTag); + } + + sb.WriteUtf8Byte('"'); + sb.WriteUtf8Byte(':'); + sb.WriteJsonSpaceIfWanted(); + + assert(sb.Room() >= extraRoomNeeded); +} + +[[nodiscard]] static int +AppendValueImpl( + StringBuilder& sb, + void const* valData, + size_t valSize, + event_field_encoding encoding, + event_field_format format, + bool needsByteSwap, + bool json) +{ + int err; + + switch (encoding) + { + default: + err = ENOTSUP; + break; + case event_field_encoding_invalid: + case event_field_encoding_struct: + err = EINVAL; + break; + case event_field_encoding_value8: + if (valSize != 1) + { + err = EINVAL; + } + else + { + unsigned const RoomNeeded = 11; + sb.EnsureRoom(RoomNeeded); + auto const val = *static_cast(valData); + switch (format) + { + default: + case event_field_format_unsigned_int: + // [255] = 3 + sb.WritePrintf(RoomNeeded, "%u", val); + break; + case event_field_format_signed_int: + // [-128] = 4 + sb.WritePrintf(RoomNeeded, "%d", static_cast(val)); + break; + case event_field_format_hex_int: + // ["0xFF"] = 6 + sb.WriteQuoteIf(json); + sb.WritePrintf(RoomNeeded - 2, "0x%X", val); + sb.WriteQuoteIf(json); + break; + case event_field_format_boolean: + // [-2000000000] = 11 + sb.WriteBoolean(val); + break; + case event_field_format_hex_bytes: + // ["00"] = 4 + sb.WriteQuoteIf(json); + sb.WriteHexBytes(valData, valSize); + sb.WriteQuoteIf(json); + break; + case event_field_format_string8: + // ["\u0000"] = 9 + WriteUcsVal(sb, val, json); // UCS1 + break; + } + + err = 0; + } + break; + case event_field_encoding_value16: + if (valSize != 2) + { + err = EINVAL; + } + else + { + unsigned const RoomNeeded = 9; + sb.EnsureRoom(RoomNeeded); + auto const val = *static_cast(valData); + switch (format) + { + default: + case event_field_format_unsigned_int: + // [65535] = 5 + sb.WritePrintf(RoomNeeded, "%u", + needsByteSwap ? bswap_16(val) : val); + break; + case event_field_format_signed_int: + // [-32768] = 6 + sb.WritePrintf(RoomNeeded, "%d", + static_cast(needsByteSwap ? bswap_16(val) : val)); + break; + case event_field_format_hex_int: + // ["0xFFFF"] = 8 + sb.WriteQuoteIf(json); + sb.WritePrintf(RoomNeeded - 2, "0x%X", + needsByteSwap ? bswap_16(val) : val); + sb.WriteQuoteIf(json); + break; + case event_field_format_boolean: + // [-32768] = 6 + sb.WriteBoolean(needsByteSwap ? bswap_16(val) : val); + break; + case event_field_format_hex_bytes: + // ["00 00"] = 7 + sb.WriteQuoteIf(json); + sb.WriteHexBytes(valData, valSize); + sb.WriteQuoteIf(json); + break; + case event_field_format_string_utf: + // ["\u0000"] = 9 + WriteUcsVal(sb, needsByteSwap ? bswap_16(val) : val, json); // UCS2 + break; + case event_field_format_port: + // [65535] = 5 + sb.WritePrintf(RoomNeeded, "%u", + be16toh(val)); + break; + } + + err = 0; + } + break; + case event_field_encoding_value32: + if (valSize != 4) + { + err = EINVAL; + } + else + { + unsigned const RoomNeeded = 28; + sb.EnsureRoom(RoomNeeded); + auto const val = *static_cast(valData); + switch (format) + { + default: + case event_field_format_unsigned_int: + // [4000000000] = 10 + sb.WritePrintf(RoomNeeded, "%u", + needsByteSwap ? bswap_32(val) : val); + break; + case event_field_format_signed_int: + case event_field_format_pid: + // [-2000000000] = 11 + sb.WritePrintf(RoomNeeded, "%d", + static_cast(needsByteSwap ? bswap_32(val) : val)); + break; + case event_field_format_hex_int: + // ["0xFFFFFFFF"] = 12 + sb.WriteQuoteIf(json); + sb.WritePrintf(RoomNeeded - 2, "0x%X", + needsByteSwap ? bswap_32(val) : val); + sb.WriteQuoteIf(json); + break; + case event_field_format_errno: + // ["ENOTRECOVERABLE[131]"] = 22 + sb.WriteQuoteIf(json); + sb.WriteErrno(needsByteSwap ? bswap_32(val) : val); + sb.WriteQuoteIf(json); + break; + case event_field_format_time: + // ["TIME(18000000000000000000)"] = 28 + sb.WriteQuoteIf(json); + sb.WriteDateTime(static_cast(needsByteSwap ? bswap_32(val) : val)); + sb.WriteQuoteIf(json); + break; + case event_field_format_boolean: + // [-2000000000] = 11 + sb.WriteBoolean(needsByteSwap ? bswap_32(val) : val); + break; + case event_field_format_float: + { + // ["1.000000001"] = 13 + uint32_t const valSwapped = needsByteSwap ? bswap_32(val) : val; + float valFloat; + static_assert(sizeof(valFloat) == sizeof(valSwapped), "Expected 32-bit float"); + memcpy(&valFloat, &valSwapped, sizeof(valSwapped)); + bool const needQuote = json && !isfinite(valFloat); + sb.WriteQuoteIf(needQuote); + sb.WritePrintf(RoomNeeded - 2, "%.9g", valFloat); + sb.WriteQuoteIf(needQuote); + break; + } + case event_field_format_hex_bytes: + // ["00 00 00 00"] = 13 + sb.WriteQuoteIf(json); + sb.WriteHexBytes(valData, valSize); + sb.WriteQuoteIf(json); + break; + case event_field_format_string_utf: + // ["nnnnnnn"] = 9 (up to 7 utf-8 bytes) + WriteUcsVal(sb, needsByteSwap ? bswap_32(val) : val, json); // UCS4 + break; + case event_field_format_ipv4: + // ["255.255.255.255"] = 17 + sb.WriteQuoteIf(json); + sb.WriteIPv4(val); + sb.WriteQuoteIf(json); + break; + } + + err = 0; + } + break; + case event_field_encoding_value64: + if (valSize != 8) + { + err = EINVAL; + } + else + { + unsigned const RoomNeeded = 28; + sb.EnsureRoom(RoomNeeded); + auto const val = *static_cast(valData); + switch (format) + { + default: + case event_field_format_unsigned_int: + // [18000000000000000000] = 20 + sb.WritePrintf(RoomNeeded, "%" PRIu64, + needsByteSwap ? bswap_64(val) : val); + break; + case event_field_format_signed_int: + // [-9000000000000000000] = 20 + sb.WritePrintf(RoomNeeded, "%" PRId64, + static_cast(needsByteSwap ? bswap_64(val) : val)); + break; + case event_field_format_hex_int: + // ["0xFFFFFFFFFFFFFFFF"] = 20 + sb.WriteQuoteIf(json); + sb.WritePrintf(RoomNeeded - 2, "0x%" PRIX64, + needsByteSwap ? bswap_64(val) : val); + sb.WriteQuoteIf(json); + break; + case event_field_format_time: + // ["TIME(18000000000000000000)"] = 28 + sb.WriteQuoteIf(json); + sb.WriteDateTime(static_cast(needsByteSwap ? bswap_64(val) : val)); + sb.WriteQuoteIf(json); + break; + case event_field_format_float: + { + // ["1.00000000000000001"] = 21 + uint64_t const valSwapped = needsByteSwap ? bswap_64(val) : val; + double valFloat; + static_assert(sizeof(valFloat) == sizeof(valSwapped), "Expected 64-bit double"); + memcpy(&valFloat, &valSwapped, sizeof(valSwapped)); + bool const needQuote = json && !isfinite(valFloat); + sb.WriteQuoteIf(needQuote); + sb.WritePrintf(RoomNeeded - 2, "%.17g", + valFloat); + sb.WriteQuoteIf(needQuote); + break; + } + case event_field_format_hex_bytes: + // ["00 00 00 00 00 00 00 00"] = 25 + sb.WriteQuoteIf(json); + sb.WriteHexBytes(valData, valSize); + sb.WriteQuoteIf(json); + break; + } + + err = 0; + } + break; + case event_field_encoding_value128: + if (valSize != 16) + { + err = EINVAL; + } + else + { + unsigned const RoomNeeded = 49; + sb.EnsureRoom(RoomNeeded); + switch (format) + { + default: + case event_field_format_hex_bytes: + // ["00 00 00 00 ... 00 00 00 00"] = 49 + sb.WriteQuoteIf(json); + sb.WriteHexBytes(valData, valSize); + sb.WriteQuoteIf(json); + break; + case event_field_format_uuid: + // ["00000000-0000-0000-0000-000000000000"] = 38 + sb.WriteQuoteIf(json); + sb.WriteUuid(valData); + sb.WriteQuoteIf(json); + break; + case event_field_format_ipv6: + // ["ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"] = 47 + sb.WriteQuoteIf(json); + sb.WriteIPv6(valData); + sb.WriteQuoteIf(json); + break; + } + + err = 0; + } + break; + case event_field_encoding_zstring_char8: + case event_field_encoding_string_length16_char8: + switch (format) + { + case event_field_format_hex_bytes: + AppendHexBytesVal(sb, valData, valSize, json); + break; + case event_field_format_string8: + AppendUcsVal(sb, + static_cast(valData), valSize / sizeof(uint8_t), + json); + break; + case event_field_format_string_utf_bom: + case event_field_format_string_xml: + case event_field_format_string_json: + if (TryAppendUtfBomVal(sb, valData, valSize, json)) + { + break; + } + __fallthrough; + default: + case event_field_format_string_utf: + AppendUtf8Val(sb, { static_cast(valData), valSize }, json); + break; + } + + err = 0; + break; + case event_field_encoding_zstring_char16: + case event_field_encoding_string_length16_char16: + if (valSize & 1) + { + err = EINVAL; + } + else + { + switch (format) + { + case event_field_format_hex_bytes: + AppendHexBytesVal(sb, valData, valSize, json); + break; + case event_field_format_string_utf_bom: + case event_field_format_string_xml: + case event_field_format_string_json: + if (TryAppendUtfBomVal(sb, valData, valSize, json)) + { + break; + } + __fallthrough; + default: + case event_field_format_string_utf: + if (needsByteSwap) + { + AppendUtf16Val(sb, + static_cast(valData), valSize / sizeof(uint16_t), + json); + } + else + { + AppendUtf16Val(sb, + static_cast(valData), valSize / sizeof(uint16_t), + json); + } + break; + } + + err = 0; + } + break; + case event_field_encoding_zstring_char32: + case event_field_encoding_string_length16_char32: + if (valSize & 3) + { + err = EINVAL; + } + else + { + switch (format) + { + case event_field_format_hex_bytes: + AppendHexBytesVal(sb, valData, valSize, json); + break; + case event_field_format_string_utf_bom: + case event_field_format_string_xml: + case event_field_format_string_json: + if (TryAppendUtfBomVal(sb, valData, valSize, json)) + { + break; + } + __fallthrough; + default: + case event_field_format_string_utf: + if (needsByteSwap) + { + AppendUcsVal(sb, + static_cast(valData), valSize / sizeof(uint32_t), + json); + } + else + { + AppendUcsVal(sb, + static_cast(valData), valSize / sizeof(uint32_t), + json); + } + break; + } + + err = 0; + } + break; + } + + return err; +} + +[[nodiscard]] static int +AppendItemAsJsonImpl( + StringBuilder& sb, + EventEnumerator& enumerator, + bool wantName) +{ + int err; + int depth = 0; + + do + { + EventItemInfo itemInfo; + + switch (enumerator.State()) + { + default: + assert(!"Enumerator in invalid state."); + err = EINVAL; + goto Done; + + case EventEnumeratorState_BeforeFirstItem: + depth += 1; + break; + + case EventEnumeratorState_Value: + + itemInfo = enumerator.GetItemInfo(); + wantName && !itemInfo.ArrayFlags + ? AppendJsonMemberBegin(sb, itemInfo.FieldTag, itemInfo.Name, 0) + : AppendJsonValueBegin(sb, 0); + err = AppendValueImpl( + sb, itemInfo.ValueData, itemInfo.ValueSize, + itemInfo.Encoding, itemInfo.Format, itemInfo.NeedByteSwap, + true); + if (err != 0) + { + goto Done; + } + break; + + case EventEnumeratorState_ArrayBegin: + + itemInfo = enumerator.GetItemInfo(); + wantName + ? AppendJsonMemberBegin(sb, itemInfo.FieldTag, itemInfo.Name, 1) + : AppendJsonValueBegin(sb, 1); + sb.WriteJsonArrayBegin(); // 1 extra byte reserved above. + + depth += 1; + break; + + case EventEnumeratorState_ArrayEnd: + + sb.EnsureRoom(2); + sb.WriteJsonSpaceIfWanted(); + sb.WriteJsonArrayEnd(); + + depth -= 1; + break; + + case EventEnumeratorState_StructBegin: + + itemInfo = enumerator.GetItemInfo(); + wantName && !itemInfo.ArrayFlags + ? AppendJsonMemberBegin(sb, itemInfo.FieldTag, itemInfo.Name, 1) + : AppendJsonValueBegin(sb, 1); + sb.WriteJsonStructBegin(); // 1 extra byte reserved above. + + depth += 1; + break; + + case EventEnumeratorState_StructEnd: + + sb.EnsureRoom(2); + sb.WriteJsonSpaceIfWanted(); + sb.WriteJsonStructEnd(); + + depth -= 1; + break; + } + + wantName = true; + } while (enumerator.MoveNext() && depth > 0); + + err = enumerator.LastError(); + +Done: + + return err; +} + +static void +AppendMetaN( + StringBuilder& sb, + EventInfo const& ei) +{ + uint8_t cchName = 0; + while (ei.Name[cchName] != '\0' && ei.Name[cchName] != ';') + { + cchName += 1; + } + + AppendJsonMemberBegin(sb, 0, "n"sv, 1); + sb.WriteUtf8Byte('"'); // 1 extra byte reserved above. + AppendUtf8JsonEscaped(sb, { ei.TracepointName, ei.ProviderNameLength }, 1); + sb.WriteUtf8Byte(':'); // 1 extra byte reserved above. + AppendUtf8JsonEscaped(sb, { ei.Name, cchName }, 1); + sb.WriteUtf8Byte('"'); // 1 extra byte reserved above. +} + +static void +AppendMetaEventInfo( + StringBuilder& sb, + EventFormatterMetaFlags metaFlags, + EventInfo const& ei) +{ + if (metaFlags & EventFormatterMetaFlags_provider) + { + AppendJsonMemberBegin(sb, 0, "provider"sv, 1); + sb.WriteUtf8Byte('"'); // 1 extra byte reserved above. + AppendUtf8JsonEscaped(sb, { ei.TracepointName, ei.ProviderNameLength }, 1); + sb.WriteUtf8Byte('"'); // 1 extra byte reserved above. + } + + if (metaFlags & EventFormatterMetaFlags_event) + { + AppendJsonMemberBegin(sb, 0, "event"sv, 1); + sb.WriteUtf8Byte('"'); // 1 extra byte reserved above. + AppendUtf8JsonEscaped(sb, ei.Name, 1); + sb.WriteUtf8Byte('"'); // 1 extra byte reserved above. + } + + if ((metaFlags & EventFormatterMetaFlags_id) && ei.Header.id != 0) + { + AppendJsonMemberBegin(sb, 0, "id"sv, 5); + sb.WritePrintf(5, "%u", ei.Header.id); + } + + if ((metaFlags & EventFormatterMetaFlags_version) && ei.Header.version != 0) + { + AppendJsonMemberBegin(sb, 0, "version"sv, 3); + sb.WritePrintf(3, "%u", ei.Header.version); + } + + if ((metaFlags & EventFormatterMetaFlags_level) && ei.Header.level != 0) + { + AppendJsonMemberBegin(sb, 0, "level"sv, 3); + sb.WritePrintf(3, "%u", ei.Header.level); + } + + if ((metaFlags & EventFormatterMetaFlags_keyword) && ei.Keyword != 0) + { + AppendJsonMemberBegin(sb, 0, "keyword"sv, 20); + sb.WritePrintf(20, "\"0x%" PRIX64 "\"", ei.Keyword); + } + + if ((metaFlags & EventFormatterMetaFlags_opcode) && ei.Header.opcode != 0) + { + AppendJsonMemberBegin(sb, 0, "opcode"sv, 3); + sb.WritePrintf(3, "%u", ei.Header.opcode); + } + + if ((metaFlags & EventFormatterMetaFlags_tag) && ei.Header.tag != 0) + { + AppendJsonMemberBegin(sb, 0, "tag"sv, 8); + sb.WritePrintf(8, "\"0x%X\"", ei.Header.tag); + } + + if ((metaFlags & EventFormatterMetaFlags_activity) && ei.ActivityId != nullptr) + { + AppendJsonMemberBegin(sb, 0, "activity"sv, 38); + sb.WriteUtf8Byte('"'); + sb.WriteUuid(ei.ActivityId); + sb.WriteUtf8Byte('"'); + } + + if ((metaFlags & EventFormatterMetaFlags_relatedActivity) && ei.RelatedActivityId != nullptr) + { + AppendJsonMemberBegin(sb, 0, "relatedActivity"sv, 38); + sb.WriteUtf8Byte('"'); + sb.WriteUuid(ei.RelatedActivityId); + sb.WriteUtf8Byte('"'); + } + + if ((metaFlags & EventFormatterMetaFlags_options) && ei.OptionsIndex < ei.TracepointNameLength) + { + AppendJsonMemberBegin(sb, 0, "options"sv, 1); + sb.WriteUtf8Byte('"'); // 1 extra byte reserved above. + std::string_view options = { + ei.TracepointName + ei.OptionsIndex, + (size_t)ei.TracepointNameLength - ei.OptionsIndex }; + AppendUtf8JsonEscaped(sb, options, 1); + sb.WriteUtf8Byte('"'); // 1 extra byte reserved above. + } + + if (metaFlags & EventFormatterMetaFlags_flags) + { + AppendJsonMemberBegin(sb, 0, "flags"sv, 6); + sb.WritePrintf(6, "\"0x%X\"", ei.Header.flags); + } +} + +// Assumes that there is room for '[' when called. +static void +AppendIntegerSampleFieldAsJsonImpl( + StringBuilder& sb, + std::string_view fieldRawData, + PerfFieldMetadata const& fieldMetadata, + bool fileBigEndian, + char const* format32, + char const* format64) +{ + assert(sb.Room() > 0); + PerfByteReader const byteReader(fileBigEndian); + + if (fieldMetadata.Array() == PerfFieldArrayNone) + { + switch (fieldMetadata.ElementSize()) + { + case PerfFieldElementSize8: + if (fieldRawData.size() < sizeof(uint8_t)) + { + AppendUtf8(sb, "null"sv); + } + else + { + unsigned const RoomNeeded = 6; // ["0xFF"] + sb.EnsureRoom(RoomNeeded); + auto val = byteReader.ReadAsU8(fieldRawData.data()); + sb.WritePrintf(RoomNeeded, format32, val); + } + break; + case PerfFieldElementSize16: + if (fieldRawData.size() < sizeof(uint16_t)) + { + AppendUtf8(sb, "null"sv); + } + else + { + unsigned const RoomNeeded = 8; // ["0xFFFF"] + sb.EnsureRoom(RoomNeeded); + auto val = byteReader.ReadAsU16(fieldRawData.data()); + sb.WritePrintf(RoomNeeded, format32, val); + } + break; + case PerfFieldElementSize32: + if (fieldRawData.size() < sizeof(uint32_t)) + { + AppendUtf8(sb, "null"sv); + } + else + { + unsigned const RoomNeeded = 12; // ["0xFFFFFFFF"] + sb.EnsureRoom(RoomNeeded); + auto val = byteReader.ReadAsU32(fieldRawData.data()); + sb.WritePrintf(RoomNeeded, format32, val); + } + break; + case PerfFieldElementSize64: + if (fieldRawData.size() < sizeof(uint64_t)) + { + AppendUtf8(sb, "null"sv); + } + else + { + unsigned const RoomNeeded = 20; // ["0xFFFFFFFFFFFFFFFF"] + sb.EnsureRoom(RoomNeeded); + auto val = byteReader.ReadAsU64(fieldRawData.data()); + sb.WritePrintf(RoomNeeded, format64, val); + } + break; + } + } + else + { + void const* const pvData = fieldRawData.data(); + auto const cbData = fieldRawData.size(); + + sb.WriteJsonArrayBegin(); // Caller is expected to give us room for 1 char. + switch (fieldMetadata.ElementSize()) + { + case PerfFieldElementSize8: + for (auto p = static_cast(pvData), pEnd = p + cbData / sizeof(p[0]); p != pEnd; p += 1) + { + unsigned const RoomNeeded = 6; // ["0xFF"] + sb.EnsureRoom(RoomNeeded + 2); + sb.WriteJsonCommaSpaceAsNeeded(); + sb.WritePrintf(RoomNeeded, format32, byteReader.Read(p)); + } + break; + case PerfFieldElementSize16: + for (auto p = static_cast(pvData), pEnd = p + cbData / sizeof(p[0]); p != pEnd; p += 1) + { + unsigned const RoomNeeded = 8; // ["0xFFFF"] + sb.EnsureRoom(RoomNeeded + 2); + sb.WriteJsonCommaSpaceAsNeeded(); + sb.WritePrintf(RoomNeeded, format32, byteReader.Read(p)); + } + break; + case PerfFieldElementSize32: + for (auto p = static_cast(pvData), pEnd = p + cbData / sizeof(p[0]); p != pEnd; p += 1) + { + unsigned const RoomNeeded = 12; // ["0xFFFFFFFF"] + sb.EnsureRoom(RoomNeeded + 2); + sb.WriteJsonCommaSpaceAsNeeded(); + sb.WritePrintf(RoomNeeded, format32, byteReader.Read(p)); + } + break; + case PerfFieldElementSize64: + for (auto p = static_cast(pvData), pEnd = p + cbData / sizeof(p[0]); p != pEnd; p += 1) + { + unsigned const RoomNeeded = 20; // ["0xFFFFFFFFFFFFFFFF"] + sb.EnsureRoom(RoomNeeded + 2); + sb.WriteJsonCommaSpaceAsNeeded(); + sb.WritePrintf(RoomNeeded, format64, byteReader.Read(p)); + } + break; + } + + sb.EnsureRoom(2); + sb.WriteJsonSpaceIfWanted(); + sb.WriteJsonArrayEnd(); + } +} + +static void +AppendSampleFieldAsJsonImpl( + StringBuilder& sb, + _In_reads_bytes_(fieldRawDataSize) void const* fieldRawData, + size_t fieldRawDataSize, + PerfFieldMetadata const& fieldMetadata, + bool fileBigEndian, + bool wantName) +{ + PerfByteReader const byteReader(fileBigEndian); + auto const fieldRawDataChars = static_cast(fieldRawData); + + // Note: AppendIntegerSampleFieldAsJsonImpl expects 1 byte reserved for '['. + wantName + ? AppendJsonMemberBegin(sb, 0, fieldMetadata.Name(), 1) + : AppendJsonValueBegin(sb, 1); + switch (fieldMetadata.Format()) + { + default: + case PerfFieldFormatNone: + if (fieldMetadata.Array() == PerfFieldArrayNone || + fieldMetadata.ElementSize() == PerfFieldElementSize8) + { + // Single unknown item, or an array of 8-bit unknown items: Treat as one binary blob. + AppendHexBytesVal(sb, fieldRawDataChars, fieldRawDataSize, true); + break; + } + // Array of unknown items: Treat as hex integers. + [[fallthrough]]; + case PerfFieldFormatHex: + AppendIntegerSampleFieldAsJsonImpl(sb, { fieldRawDataChars, fieldRawDataSize }, + fieldMetadata, fileBigEndian, "\"0x%" PRIX32 "\"", "\"0x%" PRIX64 "\""); + break; + case PerfFieldFormatUnsigned: + AppendIntegerSampleFieldAsJsonImpl(sb, { fieldRawDataChars, fieldRawDataSize }, + fieldMetadata, fileBigEndian, "%" PRIu32, "%" PRIu64); + break; + case PerfFieldFormatSigned: + AppendIntegerSampleFieldAsJsonImpl(sb, { fieldRawDataChars, fieldRawDataSize }, + fieldMetadata, fileBigEndian, "%" PRId32, "%" PRId64); + break; + case PerfFieldFormatString: + AppendUcsVal(sb, + reinterpret_cast(fieldRawDataChars), + strnlen(fieldRawDataChars, fieldRawDataSize), + true); + break; + } +} + +int +EventFormatter::AppendSampleAsJson( + std::string& dest, + PerfSampleEventInfo const& sampleEventInfo, + bool fileBigEndian, + EventFormatterJsonFlags jsonFlags, + EventFormatterMetaFlags metaFlags, + uint32_t moveNextLimit) +{ + StringBuilder sb(dest, jsonFlags); + int err = 0; + + EventEnumerator enumerator; + EventInfo eventInfo; + bool eventInfoValid; + std::string_view sampleEventName; + std::string_view sampleProviderName; + auto const sampleEventInfoSampleType = sampleEventInfo.SampleType(); + auto const sampleEventInfoMetadata = sampleEventInfo.Metadata(); + + if (sampleEventInfoMetadata && + sampleEventInfoMetadata->Kind() == PerfEventKind::EventHeader) + { + // eventheader metadata. + + auto const& meta = *sampleEventInfoMetadata; + auto const eventHeaderOffset = meta.Fields()[meta.CommonFieldCount()].Offset(); + if (eventHeaderOffset > sampleEventInfo.raw_data_size || + !enumerator.StartEvent( + meta.Name().data(), + meta.Name().size(), + static_cast(sampleEventInfo.raw_data) + eventHeaderOffset, + sampleEventInfo.raw_data_size - eventHeaderOffset, + moveNextLimit)) + { + goto NotEventHeader; + } + + eventInfo = enumerator.GetEventInfo(); + eventInfoValid = true; + + (jsonFlags & EventFormatterJsonFlags_Name) + ? AppendJsonMemberBegin(sb, 0, eventInfo.Name, 1) + : AppendJsonValueBegin(sb, 1); + sb.WriteJsonStructBegin(); // top-level + + if (metaFlags & EventFormatterMetaFlags_n) + { + AppendMetaN(sb, eventInfo); + } + + if (metaFlags & EventFormatterMetaFlags_common) + { + for (size_t iField = 0; iField != meta.CommonFieldCount(); iField += 1) + { + auto const fieldMeta = meta.Fields()[iField]; + auto const fieldData = fieldMeta.GetFieldBytes( + sampleEventInfo.raw_data, + sampleEventInfo.raw_data_size, + fileBigEndian); + AppendSampleFieldAsJsonImpl(sb, fieldData.data(), fieldData.size(), fieldMeta, fileBigEndian, true); + } + } + + err = AppendItemAsJsonImpl(sb, enumerator, true); + if (err != 0) + { + goto Done; + } + } + else + { + NotEventHeader: + + auto const sampleEventInfoName = sampleEventInfo.Name(); + if (sampleEventInfoName[0] == 0 && sampleEventInfoMetadata != nullptr) + { + // No name from PERF_HEADER_EVENT_DESC, but metadata is present so use that. + sampleProviderName = sampleEventInfoMetadata->SystemName(); + sampleEventName = sampleEventInfoMetadata->Name(); + } + else + { + auto const sampleEventNameColon = strchr(sampleEventInfoName, ':'); + if (sampleEventNameColon == nullptr) + { + // No colon in name. + // Put everything into provider name (probably "" anyway). + sampleProviderName = sampleEventInfoName; + sampleEventName = ""; + } + else + { + // Name contained a colon. + // Provider name is everything before colon, event name is everything after. + sampleProviderName = { sampleEventInfoName, static_cast(sampleEventNameColon - sampleEventInfoName) }; + sampleEventName = sampleEventNameColon + 1; + } + } + + PerfByteReader const byteReader(fileBigEndian); + + eventInfoValid = false; + + (jsonFlags & EventFormatterJsonFlags_Name) + ? AppendJsonMemberBegin(sb, 0, sampleEventName, 1) + : AppendJsonValueBegin(sb, 1); + sb.WriteJsonStructBegin(); // top-level + + if (metaFlags & EventFormatterMetaFlags_n) + { + AppendJsonMemberBegin(sb, 0, "n"sv, 1); + sb.WriteUtf8Byte('"'); // 1 extra byte reserved above. + AppendUcsJsonEscaped(sb, + reinterpret_cast(sampleProviderName.data()), + sampleProviderName.size(), + 1); + sb.WriteUtf8Byte(':'); // 1 extra byte reserved above. + AppendUcsJsonEscaped(sb, + reinterpret_cast(sampleEventName.data()), + sampleEventName.size(), + 1); + sb.WriteUtf8Byte('"'); // 1 extra byte reserved above. + } + + if (sampleEventInfoMetadata) + { + auto const& meta = *sampleEventInfoMetadata; + size_t const firstField = (metaFlags & EventFormatterMetaFlags_common) + ? 0u + : meta.CommonFieldCount(); + for (size_t iField = firstField; iField < meta.Fields().size(); iField += 1) + { + auto const fieldMeta = meta.Fields()[iField]; + auto const fieldData = fieldMeta.GetFieldBytes( + sampleEventInfo.raw_data, + sampleEventInfo.raw_data_size, + fileBigEndian); + AppendSampleFieldAsJsonImpl(sb, fieldData.data(), fieldData.size(), fieldMeta, fileBigEndian, true); + } + } + else if (sampleEventInfoSampleType & PERF_SAMPLE_RAW) + { + AppendJsonMemberBegin(sb, 0, "raw"sv, 0); + AppendHexBytesVal(sb, + sampleEventInfo.raw_data, sampleEventInfo.raw_data_size, + true); + } + } + + if (0 != (metaFlags & ~EventFormatterMetaFlags_n)) + { + AppendJsonMemberBegin(sb, 0, "meta"sv, 1); + sb.WriteJsonStructBegin(); // meta + + if ((metaFlags & EventFormatterMetaFlags_time) && (sampleEventInfoSampleType & PERF_SAMPLE_TIME)) + { + AppendJsonMemberBegin(sb, 0, "time"sv, 39); // "DATETIME.nnnnnnnnnZ" = 1 + 26 + 12 + if (sampleEventInfo.session_info->ClockOffsetKnown()) + { + auto timeSpec = sampleEventInfo.session_info->TimeToRealTime(sampleEventInfo.time); + sb.WriteUtf8Byte('\"'); + sb.WriteDateTime(timeSpec.tv_sec); + sb.WritePrintf(12, ".%09uZ\"", timeSpec.tv_nsec); + } + else + { + sb.WritePrintf(22, "%" PRIu64 ".%09u", + sampleEventInfo.time / 1000000000, + static_cast(sampleEventInfo.time % 1000000000)); + } + } + + if ((metaFlags & EventFormatterMetaFlags_cpu) && (sampleEventInfoSampleType & PERF_SAMPLE_CPU)) + { + AppendJsonMemberBegin(sb, 0, "cpu"sv, 10); + sb.WritePrintf(10, "%u", sampleEventInfo.cpu); + } + + if ((metaFlags & EventFormatterMetaFlags_pid) && (sampleEventInfoSampleType & PERF_SAMPLE_TID)) + { + AppendJsonMemberBegin(sb, 0, "pid"sv, 10); + sb.WritePrintf(10, "%u", sampleEventInfo.pid); + } + + if ((metaFlags & EventFormatterMetaFlags_tid) && (sampleEventInfoSampleType & PERF_SAMPLE_TID)) + { + AppendJsonMemberBegin(sb, 0, "tid"sv, 10); + sb.WritePrintf(10, "%u", sampleEventInfo.tid); + } + + if (eventInfoValid) + { + AppendMetaEventInfo(sb, metaFlags, eventInfo); + } + else + { + if ((metaFlags & EventFormatterMetaFlags_provider) && !sampleProviderName.empty()) + { + AppendJsonMemberBegin(sb, 0, "provider"sv, 1); + sb.WriteUtf8Byte('"'); // 1 extra byte reserved above. + AppendUtf8JsonEscaped(sb, sampleProviderName, 1); + sb.WriteUtf8Byte('"'); // 1 extra byte reserved above. + } + + if ((metaFlags & EventFormatterMetaFlags_event) && !sampleEventName.empty()) + { + AppendJsonMemberBegin(sb, 0, "event"sv, 1); + sb.WriteUtf8Byte('"'); // 1 extra byte reserved above. + AppendUtf8JsonEscaped(sb, sampleEventName, 1); + sb.WriteUtf8Byte('"'); // 1 extra byte reserved above. + } + + } + + sb.EnsureRoom(4); // Room to end meta and top-level. + sb.WriteJsonSpaceIfWanted(); + sb.WriteJsonStructEnd(); // meta + } + else + { + sb.EnsureRoom(2); // Room to end top-level. + } + + sb.WriteJsonSpaceIfWanted(); + sb.WriteJsonStructEnd(); // top-level + +Done: + + if (err == 0) + { + sb.Commit(); + } + + return err; +} + +int +EventFormatter::AppendSampleFieldAsJson( + std::string& dest, + _In_reads_bytes_(fieldRawDataSize) void const* fieldRawData, + size_t fieldRawDataSize, + PerfFieldMetadata const& fieldMetadata, + bool fileBigEndian, + EventFormatterJsonFlags jsonFlags) +{ + StringBuilder sb(dest, jsonFlags); + AppendSampleFieldAsJsonImpl(sb, fieldRawData, fieldRawDataSize, fieldMetadata, fileBigEndian, + (jsonFlags & EventFormatterJsonFlags_Name)); + sb.Commit(); + return 0; +} + +int +EventFormatter::AppendEventAsJsonAndMoveToEnd( + std::string& dest, + EventEnumerator& enumerator, + EventFormatterJsonFlags jsonFlags, + EventFormatterMetaFlags metaFlags) +{ + assert(EventEnumeratorState_BeforeFirstItem == enumerator.State()); + + StringBuilder sb(dest, jsonFlags); + auto const ei = enumerator.GetEventInfo(); + + int err; + + (jsonFlags & EventFormatterJsonFlags_Name) + ? AppendJsonMemberBegin(sb, 0, ei.Name, 1) + : AppendJsonValueBegin(sb, 1); + sb.WriteJsonStructBegin(); // top-level. + + if (metaFlags & EventFormatterMetaFlags_n) + { + AppendMetaN(sb, ei); + } + + err = AppendItemAsJsonImpl(sb, enumerator, true); + if (err == 0) + { + if (0 != (metaFlags & ~EventFormatterMetaFlags_n)) + { + AppendJsonMemberBegin(sb, 0, "meta"sv, 1); + sb.WriteJsonStructBegin(); // meta. + + AppendMetaEventInfo(sb, metaFlags, ei); + + sb.EnsureRoom(4); // Room to end meta and top-level. + sb.WriteJsonSpaceIfWanted(); + sb.WriteJsonStructEnd(); // meta + } + else + { + sb.EnsureRoom(2); // Room to end top-level. + } + + sb.WriteJsonSpaceIfWanted(); + sb.WriteJsonStructEnd(); // top-level + } + + if (err == 0) + { + sb.Commit(); + } + + return err; +} + +int +EventFormatter::AppendItemAsJsonAndMoveNextSibling( + std::string& dest, + EventEnumerator& enumerator, + EventFormatterJsonFlags jsonFlags) +{ + StringBuilder sb(dest, jsonFlags); + int const err = AppendItemAsJsonImpl(sb, enumerator, (jsonFlags & EventFormatterJsonFlags_Name)); + if (err == 0) + { + sb.Commit(); + } + return err; +} + +int +EventFormatter::AppendValue( + std::string& dest, + EventEnumerator const& enumerator) +{ + return AppendValue(dest, enumerator.GetItemInfo()); +} + +int +EventFormatter::AppendValue( + std::string& dest, + EventItemInfo const& valueItemInfo) +{ + return AppendValue(dest, valueItemInfo.ValueData, valueItemInfo.ValueSize, + valueItemInfo.Encoding, valueItemInfo.Format, valueItemInfo.NeedByteSwap); +} + +int +EventFormatter::AppendValue( + std::string& dest, + _In_reads_bytes_(valueSize) void const* valueData, + uint32_t valueSize, + event_field_encoding encoding, + event_field_format format, + bool needsByteSwap) +{ + StringBuilder sb(dest, EventFormatterJsonFlags_None); + int const err = AppendValueImpl(sb, valueData, valueSize, + encoding, format, needsByteSwap, false); + if (err == 0) + { + sb.Commit(); + } + return err; +} + +void +EventFormatter::AppendUuid( + std::string& dest, + _In_reads_bytes_(16) uint8_t const* uuid) +{ + StringBuilder sb(dest, EventFormatterJsonFlags_None); + sb.EnsureRoom(36); + sb.WriteUuid(uuid); + sb.Commit(); +} diff --git a/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/src/eventheader-decodeConfig.cmake.in b/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/src/eventheader-decodeConfig.cmake.in new file mode 100644 index 0000000000000..4933f4499afc5 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/src/eventheader-decodeConfig.cmake.in @@ -0,0 +1,2 @@ +@PACKAGE_INIT@ +include("${CMAKE_CURRENT_LIST_DIR}/eventheader-decodeTargets.cmake") diff --git a/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/tools/CMakeLists.txt b/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/tools/CMakeLists.txt new file mode 100644 index 0000000000000..d67240506d0ec --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/tools/CMakeLists.txt @@ -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) diff --git a/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/tools/decode-perf.cpp b/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/tools/decode-perf.cpp new file mode 100644 index 0000000000000..e40a1bcefc7eb --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-decode-cpp/tools/decode-perf.cpp @@ -0,0 +1,140 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include +#include +#include +#include + +#include +#include + +#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& 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 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_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; +} diff --git a/src/native/external/LinuxTracepoints/libeventheader-tracepoint/CMakeLists.txt b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/CMakeLists.txt new file mode 100644 index 0000000000000..b835dab023db7 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/CMakeLists.txt @@ -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() diff --git a/src/native/external/LinuxTracepoints/libeventheader-tracepoint/README.md b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/README.md new file mode 100644 index 0000000000000..94b44ea587c88 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/README.md @@ -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 + +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) diff --git a/src/native/external/LinuxTracepoints/libeventheader-tracepoint/include/CMakeLists.txt b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/include/CMakeLists.txt new file mode 100644 index 0000000000000..cd74a357248e4 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/include/CMakeLists.txt @@ -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 + "$" + "$") +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") diff --git a/src/native/external/LinuxTracepoints/libeventheader-tracepoint/include/eventheader-headersConfig.cmake.in b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/include/eventheader-headersConfig.cmake.in new file mode 100644 index 0000000000000..ae6bd644295e5 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/include/eventheader-headersConfig.cmake.in @@ -0,0 +1,2 @@ +@PACKAGE_INIT@ +include("${CMAKE_CURRENT_LIST_DIR}/eventheader-headersTargets.cmake") diff --git a/src/native/external/LinuxTracepoints/libeventheader-tracepoint/include/eventheader/EventHeaderDynamic.h b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/include/eventheader/EventHeaderDynamic.h new file mode 100644 index 0000000000000..e4690f6b091b3 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/include/eventheader/EventHeaderDynamic.h @@ -0,0 +1,1582 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +/* +C++ API for runtime-specified eventheader-encoded Linux Tracepoints via +libtracepoint + +This API is intended for use when the set of events to be logged is not known +at compile-time, e.g. when implementing a middle-layer for a high-level tracing +API such as OpenTelemetry. This API should not be directly used by developers +instrumenting their own code because it is less user-friendly and less efficient +than alternatives like TraceLoggingProvider.h. + +Basic usage of this API: + +- Create a Provider object with a name and an optional group. +- Use the Provider to get the EventSet with the level and keyword you need. +- As an optimization, use Enabled(eventSet) to determine whether anybody + is listening for a particular provider + event level + event keyword + combination. If nobody is listening, you should skip the remaining steps + because there is no need to build and write an event that nobody will + receive. +- Use an EventBuilder to build and write the event: + - Create an EventBuilder. + - Call eventBuilder.Reset(...) to set the event name and to set the event + tag (if any). + - Call eventBuilder.AddValue(...) and similar methods to add fields to the + event or to configure the event's other attributes (e.g. opcode, id, + version). + - Call eventBuilder.Write(eventSet, ...) to send the event to the system + with the eventSet's provider, level, and keyword. + +Notes: + +- EventBuilder is reusable. If you need to generate several events, you may + get a small performance benefit by reusing one EventBuilder rather than + using a new EventBuilder for each event. +- Events are limited in size (event size = headers + metadata + data). The + kernel will ignore any event that is larger than 64KB. +- All event sets for a provider will become disabled when the provider is + destroyed or when you call provider.Unregister(). +- The Provider object is not thread-safe, but the EventSet object is + thread-safe. Possible strategies for multi-threaded programs might include: + - 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() as needed on any thread 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. +- Each event set maps to one tracepoint name, e.g. if the provider name is + "MyCompany_MyComponent", level is verbose (5), and keyword is 0x1f, the + event set will correspond to a tracepont named + "user_events:MyCompany_MyComponent_L5K1f". +- Collect events to a file using a tool such as "perf", e.g. + "perf record -k monotonic -e user_events:MyCompany_MyComponent_L5K1f". +- Decode events using a tool such as "decode-perf" (from the tools in the + libeventheader-decode-cpp library). +*/ + +#pragma once +#ifndef _included_EventHeaderDynamic_h +#define _included_EventHeaderDynamic_h 1 + +#include "eventheader-tracepoint.h" +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#endif +#ifndef _In_opt_ +#define _In_opt_ +#endif +#ifndef _In_reads_bytes_opt_ +#define _In_reads_bytes_opt_(size) +#endif +#ifndef _In_reads_ +#define _In_reads_(count) +#endif +#ifndef _Success_ +#define _Success_(condition) +#endif +#ifndef _Outptr_result_bytebuffer_ +#define _Outptr_result_bytebuffer_(size) +#endif +#ifndef _Out_opt_ +#define _Out_opt_ +#endif + +namespace ehd +{ + /* + Represents a group of events with the same provider, level, and keyword. + + Each EventSet corresponds to one tracepoint name. For example, the EventSet + with provider name "MyCompany_MyComponent", level verbose (5), and keyword + 0x1f would correspond to a tracepoint named + "user_events:MyCompany_MyComponent_L5K1f". + + Get an EventSet by calling provider.RegisterSet(level, keyword) or + provider.FindSet(level, keyword). + + Use an EventSet by calling Enabled(eventSet) or + eventBuilder.Write(eventSet, ...); + */ + class EventSet + { + friend class Provider; // Forward declaration + friend class EventBuilder; // Forward declaration + tracepoint_state m_tracepointState; + uint8_t m_level; + int m_errno; + + public: + + EventSet(EventSet const&) = delete; + void operator=(EventSet const&) = delete; + + /* + Creates an inactive (unregistered) EventSet. The returned event set's + Enabled() method will always return false, and any attempt to write + to this event set will have no effect (safe no-op). + + This method may be used to create a placeholder event set. Active + (registered) event sets are created using provider.RegisterSet(). + */ + constexpr + EventSet() noexcept + : m_tracepointState(TRACEPOINT_STATE_INIT) + , m_level() + , m_errno(22) // EINVAL + { + return; + } + + /* + Returns true if any logging session is listening for events with the + provider, level, and keyword associated with this event set. + + For shared_ptr where the eventSetPtr might be NULL, consider + using Enabled(eventSetPtr), which is equivalent to + (eventSetPtr != NULL && eventSetPtr->Enabled()). + */ + bool + Enabled() const noexcept + { + return TRACEPOINT_ENABLED(&m_tracepointState); + } + + /* + For diagnostics/debugging (usually ignored in production). + Returns 0 if this event set was successfully registered, or a nonzero + error code if ioctl(user_events_data, DIAG_IOCSREG, ...) returned an + error. + */ + int + Errno() const noexcept + { + return m_errno; + } + }; + + /* + Represents a named event provider. Use a provider to manage the EventSets. + + - Call provider.RegisterSet(level, keyword) to get a shared_ptr + that can be used to write events with the corresponding + provider + level + keyword. + - Call provider.FindSet(level, keyword) to get a shared_ptr to + an EventSet that was previously registered. + - Call provider.Unregister() if you want to disconnect the provider before + it goes out of scope. + + Note that Provider is not thread-safe, but the shared_ptr objects + returned by RegisterSet() and FindSet() are thread-safe. Possible ways to + safely use a provider in a multi-threaded program: + + - 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. + */ + class Provider + { + static auto constexpr NamesMax = EVENTHEADER_NAME_MAX - sizeof("_LffKffffffffffffffffG"); + + struct EventKey + { + uint64_t Keyword; + uint8_t Level; // Comes after keyword so that there is no padding between them. + }; + + struct EventKeyOps + { + // hash + size_t + operator()(EventKey const& a) const noexcept + { + // FNV-1a + constexpr auto Prime = sizeof(size_t) == 8 + ? static_cast(0x00000100000001B3) + : static_cast(0x01000193); + auto h = sizeof(size_t) == 8 + ? static_cast(0xcbf29ce484222325) + : static_cast(0x811c9dc5); + auto const p = reinterpret_cast(&a); + assert(&a.Level - p == 8); + for (unsigned i = 0; i != 9; i += 1) + { + h = (h ^ p[i]) * Prime; + } + return h; + } + + // equals + bool + operator()(EventKey const& a, EventKey const& b) const noexcept + { + return 0 == memcmp(&a, &b, 9); + } + }; + + using EventSetMap = std::unordered_map, EventKeyOps, EventKeyOps>; + + EventSetMap m_eventSets; + tracepoint_provider_state m_providerState; + eventheader_provider m_provider; + int m_errno; + char m_nameOptionsBuffer[NamesMax + 3]; // 3 = provider name's NUL + 'G' + option's NUL. + + public: + + Provider(Provider const&) = delete; + void operator=(Provider const&) = delete; + + ~Provider() + { + eventheader_close_provider(&m_provider); + } + + /* + Creates a new provider. + + - providerName is the name to use for this provider. It must be less than 234 + chars and must not contain '\0', ' ', or ':'. For best compatibility with + trace processing components, it should contain only ASCII letters, digits, + and '_'. It should be short, human-readable, and unique enough to not + conflict with names of other providers. The provider name will typically + include a company name and a component name, e.g. "MyCompany_MyComponent". + + - groupName can usually be "". If the provider needs to join a provider group, + specify the group name, which must contain only ASCII lowercase letters and + digits. The total length of the provider name + the group name must be less + than 234. + + Use RegisterSet() to register event sets and add them to the per-provider list + of event sets. Use FindSet() to find a set that is already in the list. + + Use EventBuilder to create events, then write them to an event set. + + Requires: strlen(providerName) + strlen(groupName) < 234. + Requires: providerName does not contain '\0', ' ', or ':'. + Requires: groupName contains only ASCII lowercase letters and digits. + */ + explicit + Provider( + std::string_view providerName, + std::string_view groupName = std::string_view()) noexcept + : m_eventSets() + , m_providerState(TRACEPOINT_PROVIDER_STATE_INIT) + , m_provider() + , m_errno() + { + // Precondition violation: providerName must not contain these chars. + assert(providerName.npos == providerName.find('\0')); + assert(providerName.npos == providerName.find(' ')); + assert(providerName.npos == providerName.find(':')); + + using namespace std::string_view_literals; + constexpr size_t NamesMax = EVENTHEADER_NAME_MAX - "_LffKffffffffffffffffG"sv.size(); + + // Precondition violation: providerName must be less than 234 chars. + assert(providerName.size() < NamesMax); + + auto const cchProviderName = providerName.size() < NamesMax + ? providerName.size() + : NamesMax - 1; + + m_provider.state = &m_providerState; + m_provider.name = m_nameOptionsBuffer; + m_provider.options = m_nameOptionsBuffer + cchProviderName + 1; + + size_t cchGroupName; + for (cchGroupName = 0; groupName.size() != cchGroupName; cchGroupName += 1) + { + if (cchGroupName == NamesMax - cchProviderName - 1) + { + assert(false); // Precondition violation: providerName + groupName too long. + break; // In release builds, stop here. + } + + auto const ch = groupName[cchGroupName]; + if ((ch < '0' || '9' < ch) && (ch < 'a' || 'z' < ch)) + { + assert(false); // Precondition violation: groupName contains invalid chars. + break; // In release builds, stop here. + } + } + + auto pStrings = m_nameOptionsBuffer; + + // Create provider name string. + + assert(pStrings == m_provider.name); + memcpy(pStrings, providerName.data(), cchProviderName); + pStrings += cchProviderName; + *pStrings = '\0'; + pStrings += 1; + + // Create options string. + + assert(pStrings == m_provider.options); + if (cchGroupName != 0) + { + *pStrings = 'G'; + pStrings += 1; + memcpy(pStrings, groupName.data(), cchGroupName); + pStrings += cchGroupName; + } + + *pStrings = '\0'; + pStrings += 1; + + assert(pStrings <= m_nameOptionsBuffer + sizeof(m_nameOptionsBuffer)); + + m_errno = eventheader_open_provider(&m_provider); + } + + /* + Returns this provider's name. + */ + std::string_view + Name() const noexcept + { + assert(m_provider.name < m_provider.options); + assert(m_provider.options[-1] == 0); + return std::string_view(m_provider.name, m_provider.options - m_provider.name - 1); + } + + /* + Returns the options string, e.g. "" or "Gmygroup". + */ + std::string_view + Options() const noexcept + { + return m_provider.options; + } + + /* + For diagnostics/debugging (usually ignored in production). + Returns 0 if this provider was successfully registered, or a nonzero + errno code if eventheader_open_provider() failed. + */ + int + Errno() const noexcept + { + return m_errno; + } + + /* + If this provider is not registered, does nothing and returns 0. + Otherwise, unregisters all event sets that were registered by this provider + and clears the list of already-created event sets. + + Use provider.Unregister() if you want to unregister the provider before it goes + out of scope. The provider automatically unregisters when it is destroyed so + most users do not need to call Unregister() directly. + */ + void + Unregister() noexcept + { + eventheader_close_provider(&m_provider); + m_eventSets.clear(); + } + + /* + If an event set with the specified level and keyword is in the list of + already-created sets, returns it. Otherwise, returns nullptr. + */ + std::shared_ptr + FindSet(event_level level, uint64_t keyword) const noexcept + { + EventKey const k = { keyword, static_cast(level) }; + auto const it = m_eventSets.find(k); + return it == m_eventSets.end() ? std::shared_ptr() : it->second; + } + + /* + If an event set with the specified level and keyword is in the list of + already-created sets, returns it. Otherwise, creates a new event set, adds it to + the list of already-created sets, attempts to register it, and returns the new + event set. If registration fails, the new event set will have a non-zero errno + and will never be enabled. + + In case of out-of-memory, returns nullptr. + */ + std::shared_ptr + RegisterSet(event_level level, uint64_t keyword) noexcept + { + std::shared_ptr result; + + EventKey const k = { keyword, static_cast(level) }; + std::pair emplace_result; + try + { + emplace_result = m_eventSets.emplace(k, std::shared_ptr()); + } + catch (...) + { + return result; // nullptr. + } + + if (!emplace_result.second) + { + result = emplace_result.first->second; + } + else try + { + auto created = std::make_shared(); + created->m_level = static_cast(level); + if (m_errno) + { + created->m_errno = m_errno; // Propagate error from eventheader_open_provider. + } + else + { + eventheader_tracepoint tp = { + &created->m_tracepointState, + nullptr, + { 0, 0, 0, 0, 0, static_cast(level) }, + keyword }; + created->m_errno = eventheader_connect(&tp, &m_provider); + } + + emplace_result.first->second = std::move(created); + result = emplace_result.first->second; + } + catch (...) + { + m_eventSets.erase(k); + } + + return result; + } + + /* + For testing purposes: Creates an inactive (unregistered) event set. + + If an event set with the specified level and keyword is in the list of + already-created sets, returns it. Otherwise, creates a new **unregistered** + event set, adds it to the list of already-created sets, and returns the new + event set. + + In case of out-of-memory, returns nullptr. + */ + std::shared_ptr + CreateUnregistered(event_level level, uint64_t keyword, bool enabled = false) noexcept + { + std::shared_ptr result; + + EventKey const k = { keyword, static_cast(level) }; + std::pair emplace_result; + try + { + emplace_result = m_eventSets.emplace(k, std::shared_ptr()); + } + catch (...) + { + return result; // nullptr. + } + + if (!emplace_result.second) + { + result = emplace_result.first->second; + } + else try + { + auto created = std::make_shared(); + created->m_level = static_cast(level); + created->m_errno = 0; + created->m_tracepointState.status_word = enabled; + + emplace_result.first->second = std::move(created); + result = emplace_result.first->second; + } + catch (...) + { + m_eventSets.erase(k); + } + + return result; + } + }; + + /* + Stores event attributes: name, fields, id, version, tag, opcode. + + Usage: + + - Create a provider. + - Use the provider to get an eventSet. + - If the eventSet is not enabled, skip the remaining steps. + - Create an eventBuilder. + - Call eventBuilder.Reset(name, ...) to start building an event. + - Call other methods on eventBuilder to set attributes or add fields. + - Call eventBuilder.Write(eventSet, ...) to write the event. + */ + class EventBuilder + { + // Optimization: Use vector for capacity management but not size management. + // Otherwise, vector repeatedly zeroes memory that we just overwrite. + class Buffer + { + uint8_t* m_next; + std::vector m_data; + + public: + + Buffer() noexcept + : m_next() + , m_data() + { + m_next = m_data.data(); + } + + size_t + size() const noexcept + { + return m_next - m_data.data(); + } + + uint8_t const* + data() const noexcept + { + return m_data.data(); + } + + uint8_t* + data() noexcept + { + return m_data.data(); + } + + void + clear() noexcept + { + m_next = m_data.data(); + } + + void + advance(size_t cbUsed) noexcept + { + assert(cbUsed <= static_cast(m_data.data() + m_data.size() - m_next)); + m_next += cbUsed; + } + + _Success_(return) bool + ensure_space_for(size_t cbRequired, _Outptr_result_bytebuffer_(cbRequired) uint8_t** ppBuffer) noexcept + { + // Keep small so it's likely to be inlined. + *ppBuffer = m_next; + return cbRequired <= static_cast(m_data.data() + m_data.size() - m_next) + || GrowToProvideSpaceFor(cbRequired, ppBuffer); + } + + private: + + _Success_(return) bool + GrowToProvideSpaceFor(size_t cbRequired, _Outptr_result_bytebuffer_(cbRequired) uint8_t** ppBuffer) noexcept + { + size_t const oldSize = m_next - m_data.data(); + assert(oldSize <= m_data.size()); + + size_t const minSize = oldSize + cbRequired; + if (minSize < cbRequired) + { + // integer overflow + } + else try + { + m_data.reserve(minSize); + m_data.resize(m_data.capacity()); + assert(minSize <= m_data.size()); + m_next = m_data.data() + oldSize; + *ppBuffer = m_next; + return true; + } + catch (...) + { + // length error or out-of-memory. + } + + // integer overflow, length error, or out-of-memory. + *ppBuffer = nullptr; + return false; + } + }; + + template + struct TypeInfo + { + static event_field_encoding const ValueEncoding = + Size == 1 ? event_field_encoding_value8 + : Size == 2 ? event_field_encoding_value16 + : Size == 4 ? event_field_encoding_value32 + : Size == 8 ? event_field_encoding_value64 + : Size == 16 ? event_field_encoding_value128 + : event_field_encoding_invalid; + + static event_field_encoding const StringEncoding = + Size == 1 ? event_field_encoding_string_length16_char8 + : Size == 2 ? event_field_encoding_string_length16_char16 + : Size == 4 ? event_field_encoding_string_length16_char32 + : event_field_encoding_invalid; + + static event_field_encoding const ZStringEncoding = + Size == 1 ? event_field_encoding_zstring_char8 + : Size == 2 ? event_field_encoding_zstring_char16 + : Size == 4 ? event_field_encoding_zstring_char32 + : event_field_encoding_invalid; + }; + + /* + Detects whether the specified value type has a supported size + (1, 2, 4, 8, or 16) and is trivially-copyable. If so, supplies the + ValueEncoding (value8, value16, value32, value64, value128). + */ + template + struct ValueEnabled + : std::enable_if< + std::is_trivially_copyable::value && + TypeInfo::ValueEncoding != event_field_encoding_invalid, + EventBuilder&> + , TypeInfo + {}; + + /* + Detects whether the specified char type has a supported size + (1, 2, or 4) and is trivially-copyable. If so, supplies the StringEncoding + and ZStringEncoding (char8, char16, char32). + */ + template + struct StringEnabled + : std::enable_if< + ExtraCondition && + std::is_trivially_copyable::value && + TypeInfo::StringEncoding != event_field_encoding_invalid, + EventBuilder&> + , TypeInfo + {}; + + Buffer m_meta; + Buffer m_data; + bool m_error; + uint8_t m_version; + uint16_t m_id; + uint16_t m_tag; + uint8_t m_opcode; + + public: + + /* + Returns a new event builder with specified initial buffer capacities. + Buffers will automatically grow as needed. + + Call Reset() to start building a new event. + */ + explicit + EventBuilder(uint16_t metaCapacity = 256, uint16_t dataCapacity = 256) noexcept + : m_meta() + , m_data() + , m_error(true) + , m_version() + , m_id() + , m_tag() + , m_opcode() + { + uint8_t* pIgnored; + m_meta.ensure_space_for(metaCapacity, &pIgnored); + m_data.ensure_space_for(dataCapacity, &pIgnored); + } + + /* + Clears the previous event (if any) from the builder and starts building a new + event. + + - name is the event name. It should be short and unique. It must not contain '\0'. + - tag is a 16-bit integer that will be recorded in the event and can be + used for any provider-defined purpose. Use 0 if you are not using event tags. + */ + EventBuilder& + Reset(std::string_view name, uint16_t tag = 0) noexcept + { + // Precondition violation: name must not contain '\0'. + assert(name.find('\0') == name.npos); + + m_meta.clear(); + m_data.clear(); + m_error = false; + m_version = 0; + m_id = 0; + m_tag = tag; + m_opcode = event_opcode_info; + + uint8_t* pMeta; + if (!m_meta.ensure_space_for(name.size() + 1, &pMeta)) + { + m_error = true; + } + else + { + memcpy(pMeta, name.data(), name.size()); + pMeta[name.size()] = '\0'; + m_meta.advance(name.size() + 1); + } + + return *this; + } + + /* + Sends the finished event to the kernel with the provider, event level, and event + keyword of the specified event set. + + - eventSet should be a registered and enabled event set. Calling Write on an + unregistered or disabled event set is a safe no-op (usually returns 0 in this + case, though it may return ERANGE if the event is too large). + + - activityId contains a pointer to the 16-byte activity id to be assigned to the + event, or nullptr if the event is not part of an activity. (An activity is a + group of related events that all have the same activity id, started by an event + with event_opcode_activity_start and ended by an event with + event_opcode_activity_stop.) + + - relatedId contains a pointer to the 16-byte related activity id (parent activity) + to be used for an activity-start event, or nullptr if the event is not an + activity-start event or does not have a parent activity. If activityId is nullptr, + this must also be nullptr. + + Returns 0 for success. Returns a nonzero errno value for failure. The return + value is for diagnostic/debugging purposes only and should generally be ignored + in retail builds. Returns EBADF (9) if tracepoint is unregistered or disabled. + Returns ENOMEM (12) if out of memory. Returns ERANGE (34) if the event (headers + + metadata + data) is greater than 64KB. Returns other errors as reported by writev. + */ + int + Write( + EventSet const& eventSet, + _In_reads_bytes_opt_(16) void const* activityId = nullptr, + _In_reads_bytes_opt_(16) void const* relatedId = nullptr) const noexcept + { + assert(relatedId == nullptr || activityId != nullptr); + if (m_error) + { + return 12; // ENOMEM + } + else if (m_meta.size() + m_data.size() > 65535 - (52 + 16)) + { + return 34; // ERANGE + } + else + { + eventheader_extension metadataExt = {}; + metadataExt.size = static_cast(m_meta.size()); + metadataExt.kind = eventheader_extension_kind_metadata; + + iovec dataVecs[EVENTHEADER_PREFIX_DATAVEC_COUNT_NO_METADATA + 3]; + dataVecs[EVENTHEADER_PREFIX_DATAVEC_COUNT_NO_METADATA + 0] = { &metadataExt, sizeof(metadataExt) }; + dataVecs[EVENTHEADER_PREFIX_DATAVEC_COUNT_NO_METADATA + 1] = { (void*)m_meta.data(), m_meta.size() }; + dataVecs[EVENTHEADER_PREFIX_DATAVEC_COUNT_NO_METADATA + 2] = { (void*)m_data.data(), m_data.size() }; + + eventheader_tracepoint tp = {}; + tp.state = const_cast(&eventSet.m_tracepointState); + tp.header.flags = eventheader_flag_default_with_extension; + tp.header.version = m_version; + tp.header.id = m_id; + tp.header.tag = m_tag; + tp.header.opcode = m_opcode; + tp.header.level = eventSet.m_level; + + return eventheader_write( + &tp, + activityId, + relatedId, + sizeof(dataVecs) / sizeof(dataVecs[0]), + dataVecs); + } + } + + /* + Sets the manually-assigned id and version of the event to be generated + by this builder. Most events will use id = 0, version = 0, indicating + that the event does not have a manually-assigned id and is identified + by event name rather than by event id. + + Call this with a nonzero value for id if the event has a + manually-assigned (durable) unique id. + + When the id is first assigned, use version 0. Increment version each + time the event schema changes (each time you make a change to the + event name, field names, field types, etc.). + */ + EventBuilder& + IdVersion(uint16_t id, uint8_t version) noexcept + { + m_id = id; + m_version = version; + return *this; + } + + /* + Sets the opcode of the event, indicating special semantics to be used + by event decoders, e.g. activity-start or activity-stop. Most events + do not have special semantics so most events use the default opcode, + info (0). + */ + EventBuilder& + Opcode(event_opcode opcode) noexcept + { + m_opcode = static_cast(opcode); + return *this; + } + + /* + Adds a field containing the specified number of sub-fields. + + A struct is a way to logically group a number of fields. To add a struct to + an event, call builder.AddStruct("StructName", structFieldCount). + Then add structFieldCount more fields and they will be considered to be + members of the struct. + + - fieldName should be a short and distinct string that describes the field. + + - structFieldCount specifies the number of subsequent fields that will be + considered to be part of this struct field. This must be in the range 1 to + 127. Empty structs (structs that contain no fields) are not permitted. + + - fieldTag is a 16-bit integer that will be recorded in the field and can be + used for any provider-defined purpose. Use 0 if you are not using field tags. + + - pFieldCountBookmark (advanced): If you don't know how many fields will be in + the struct ahead of time, pass 1 for structFieldCount and pass the address of + a size_t variable to receive a bookmark. After you have added all of the fields + you can then use the bookmark to set the actual structFieldCount value. Note + that structFieldCount still cannot be 0 or larger than 127. Use nullptr if + you are passing the actual field count in the structFieldCount parameter. + + Structs can nest. Each nested struct and its fields count as 1 field for the + parent struct. + */ + EventBuilder& + AddStruct( + std::string_view fieldName, + uint8_t structFieldCount, + uint16_t fieldTag = 0, + _Out_opt_ size_t* pFieldCountBookmark = nullptr) noexcept + { + uint8_t maskedFieldCount = structFieldCount & event_field_format_value_mask; + assert(structFieldCount == maskedFieldCount); // Precondition: structFieldCount must be less than 128. + + size_t fieldCountBookmark; + if (structFieldCount == 0) + { + assert(structFieldCount != 0); // Precondition: structFieldCount must not be 0. + fieldCountBookmark = -1; + } + else + { + fieldCountBookmark = RawAddMeta(fieldName, event_field_encoding_struct, maskedFieldCount, fieldTag); + } + + if (pFieldCountBookmark) + { + *pFieldCountBookmark = fieldCountBookmark; + } + + return *this; + } + + /* + Advanced: Resets the number of logical fields in a structure. + + Requires: + + - fieldCountBookmark is a bookmark value returned by AddStruct, and you + haven't called Reset since that bookmark was returned. + - structFieldCount is a value from 1 to 127. (Must not be 0. Must be less + than 128.) + + If the final number of fields of a structure is not known at the time + you need to start the structure, you can follow this procedure: + + - Create a size_t bookmark variable. + - In the AddStruct call, pass 1 as the number of fields, and pass &bookmark + as the pFieldCountBookmark parameter. + - After you know the actual number of fields, call + SetStructFieldCount(bookmark, fieldCount). + */ + EventBuilder& + SetStructFieldCount(size_t fieldCountBookmark, uint8_t structFieldCount) noexcept + { + uint8_t maskedFieldCount = structFieldCount & event_field_format_value_mask; + assert(structFieldCount == maskedFieldCount); // Precondition: structFieldCount must be less than 128. + + if (structFieldCount == 0) + { + assert(structFieldCount != 0); // Precondition: structFieldCount must not be 0. + } + else if (fieldCountBookmark < m_meta.size()) + { + auto const pEncoding = m_meta.data() + fieldCountBookmark; + *pEncoding = (*pEncoding & 0x80) | maskedFieldCount; + } + else + { + // Precondition: fieldCountBookmark must be a valid bookmark from AddStruct. + assert(fieldCountBookmark == static_cast(-1)); + } + + return *this; + } + + /* + Adds a field containing a simple value. + + - fieldName should be a short and distinct string that describes the field. + + - fieldValue provides the data for the field. Note that the data is treated + as raw bytes, i.e. there will be no error, warning, or data conversion if the + type of the fieldValue parameter conflicts with the format parameter. See below + for the types accepted for this parameter. + + - format indicates how the decoder should interpret the field data. For example, + if the field value is int8_t or int32_t, you would likely set format to + event_field_format_signed_int, and if the field value is float or double, you + would likely set format to event_field_format_float. + + - fieldTag is a 16-bit integer that will be recorded in the field and can be + used for any provider-defined purpose. Use 0 if you are not using field tags. + + Types: + + - If fieldValue is a 1-byte type (e.g. char, bool), the field will be encoded as + value8. For 1-byte types, if format is default, the field will be formatted as + unsigned_int. Usable formats for 1-byte types include: unsigned_int, signed_int, + hex_int, boolean, hex_bytes, string8. + - If fieldValue is a 2-byte type (e.g. short), the field will be encoded as + value16. For 2-byte types, if format is default, the field will be formatted as + unsigned_int. Usable formats for 2-byte types include: unsigned_int, signed_int, + hex_int, boolean, hex_bytes, string_utf, port. + - If fieldValue is a 4-byte type (e.g. int, float), the field will be encoded as + value32. For 4-byte types, if format is default, the field will be formatted as + unsigned_int. Usable formats for 4-byte types include: unsigned_int, signed_int, + hex_int, errno, pid, time, boolean, float, hex_bytes, string_utf, ipv4. + - If fieldValue is an 8-byte type (e.g. long long, double), the field will be + encoded as value64. For 8-byte types, if format is default], the field will be + formatted as unsigned_int. Usable formats include: unsigned_int, signed_int, + hex_int, time, float, hex_bytes. + - If fieldValue is a pointer-size type (e.g. void* or intptr_t), the field will be + encoded as value_ptr (which is an alias for either value32 or value64). For + pointer-sized types, if format is default, the field will be formatted as + unsigned_int. Usable formats for pointer-sized types include: unsigned_int, + signed_int, hex_int, time, float, hex_bytes. + - If fieldValue is a 16-byte type (e.g. ehd::Value128 or your own + trivially-copyable 16-byte struct), the field will be encoded as value128. For + 16-byte types, if format is default, the field will be formatted as hex_bytes. + Usable formats for 16-byte types include: hex_bytes, uuid, ipv6. + + Notes: + + - Using event_field_format_default instead of another event_field_format value + saves 1 byte per event in the trace data. + - For small values (1-8 bytes), event_field_format_default is equivalent to + event_field_format_unsigned_int, so if logging a small value that you want + formatted as an unsigned decimal integer, you can save 1 byte per event with no + change in decoding behavior by using event_field_format_default instead of + event_field_format_unsigned_int for that field. + - For 16-byte values, event_field_format_default is equivalent to + event_field_format_hex_bytes, so if logging a 16-byte value that you want + formatted as hexadecimal bytes, you can save 1 byte per event with no change in + decoding behavior by using event_field_format_default instead of + event_field_format_hex_bytes for that field. + */ + template + auto // Returns EventBuilder& + AddValue( + std::string_view fieldName, + ValTy const& fieldValue, + event_field_format format, + uint16_t fieldTag = 0) noexcept + -> typename ValueEnabled::type // enable_if + { + RawAddMeta(fieldName, ValueEnabled::ValueEncoding, format, fieldTag); + RawAddDataValue(fieldValue); + return *this; + } + + /* + Adds a field containing a sequence of simple values such as an array of integers. + + - fieldName should be a short and distinct string that describes the field. + + - fieldBeginIterator..fieldEndIterator provide the data for the field as a + beginIterator-endIterator pair (can be beginPtr and endPtr if values are in a + contiguous range). The value types accepted by this method are the same as the + types accepted by AddValue. Note that, as with AddValue, the element data is + treated as raw bytes, i.e. there will be no error, warning, or data conversion + if the type of the value conflicts with the format parameter. + + - format indicates how the decoder should interpret the field data. For example, + if the field values are int8_t or int32_t, you would likely set format to + signed_int, and if the field values are float or double, you would likely set + format to float. + + - fieldTag is a 16-bit integer that will be recorded in the field and can be + used for any provider-defined purpose. Use 0 if you are not using field tags. + + See AddValue for additional details about the compatible element types and how + they are treated. + + For strings or binary blobs, use AddString instead of this method. + If you pass a string or blob to this method, the decoder will format the field as + an array of values (e.g. ['a', 'b', 'c'] or [0x61, 0x62, 0x63]) rather than as a + string or blob (e.g. "abc" or "61 62 63"). + */ + template + auto // Returns EventBuilder& + AddValueRange( + std::string_view fieldName, + BeginItTy fieldBeginIterator, + EndItTy fieldEndIterator, + event_field_format format, + uint16_t fieldTag = 0) noexcept + -> typename ValueEnabled::type>::type // enable_if + { + using ValTy = typename std::decay::type; + RawAddMeta( + fieldName, + ValueEnabled::ValueEncoding | event_field_encoding_varray_flag, + format, + fieldTag); + RawAddDataRange(fieldBeginIterator, fieldEndIterator, + [](EventBuilder* pThis, ValTy const& value) + { + pThis->RawAddDataValue(value); + }); + return *this; + } + + /* + Adds a field containing a string or a binary blob. + + - fieldName should be a short and distinct string that describes the field. + + - fieldValue provides the data for the field as a basic_string_view, e.g. + std::string_view, std::wstring_view, std::u16string_view, std::u32string_view. + + Note: you can either provide an actual std::string_view for this parameter or you + can explicitly specify the char type, e.g. AddString(), and then you can + take advantage of string_view's implicit conversions. + + - format indicates how the decoder should interpret the field data. For example, + if the field value is a Unicode string, you would likely set format to + default (resulting in the field decoding as string_utf, and if the field value + is a binary blob, you would likely set format to hex_bytes. + + - fieldTag is a 16-bit integer that will be recorded in the field and can be + used for any provider-defined purpose. Use 0 if you are not using field tags. + + Types: + + - The field will be encoded as one of string_length16_char8, + string_length16_char16, or string_length16_char32. + - If format is default, the field will be formatted as string_utf. + - Usable formats include: hex_bytes, string_utf_bom, string_xml, string_json. + - For 8-bit char types, you may also use format string8, indicating a non-Unicode + string (usually treated as Latin-1). + + Note that event_field_format_default saves 1 byte in the trace. For string/binary + encodings, event_field_format_default is treated as event_field_format_string_utf, + so you can save 1 byte in the trace by using event_field_format_default instead of + event_field_format_string_utf for string fields. + + This is the same as AddNulTerminatedString except that the field will be encoded as + a counted sequence instead of as a nul-terminated string. In most cases you + should prefer this method and use AddNulTerminatedString only if you specifically + need the nul-terminated encoding. + */ + template + auto // Returns EventBuilder& + AddString( + std::string_view fieldName, + std::basic_string_view fieldValue, + event_field_format format, + uint16_t fieldTag = 0) noexcept + -> typename StringEnabled::type // enable_if + { + RawAddMeta(fieldName, StringEnabled::StringEncoding, format, fieldTag); + RawAddDataCounted(fieldValue); + return *this; + } + + /* + Adds a field containing a sequence of strings or binary blobs. + Note that you must provide the char type as the first template parameter, e.g. + AddStringRange or AddStringRange. + + - fieldName should be a short and distinct string that describes the field. + + - fieldBeginIterator..fieldEndIterator provide the data for the field as a + beginIterator-endIterator pair (can be beginPtr and endPtr if values are in a + contiguous range). The iterators must return a value that is + implicitly-convertible to std::basic_string_view, e.g. + *fieldBeginIterator must return something like a char* or a std::string_view. + + - format indicates how the decoder should interpret the field data. For example, + if the field value contains Unicode strings, you would likely set format to + default (resulting in the field decoding as string_utf, and if the field value + contains binary blobs, you would likely set format to hex_bytes. + + - fieldTag is a 16-bit integer that will be recorded in the field and can be + used for any provider-defined purpose. Use 0 if you are not using field tags. + + This is the same as AddNulTerminatedStringRange except that the field will be + encoded as a counted sequence instead of as a nul-terminated string. In most cases + you should prefer this method and use AddNulTerminatedStringRange only if you + specifically need the nul-terminated encoding. + */ + template + auto // Returns EventBuilder& + AddStringRange( + std::string_view fieldName, + BeginItTy fieldBeginIterator, + EndItTy fieldEndIterator, + event_field_format format, + uint16_t fieldTag = 0) noexcept + -> typename StringEnabled< + CharTy, + std::is_convertible>::value + >::type // enable_if + { + RawAddMeta( + fieldName, + StringEnabled::StringEncoding | event_field_encoding_varray_flag, + format, + fieldTag); + RawAddDataRange(fieldBeginIterator, fieldEndIterator, + [](EventBuilder* pThis, std::basic_string_view value) + { + pThis->RawAddDataCounted(value); + }); + return *this; + } + + /* + Adds a field containing a nul-terminated string. + + - fieldName should be a short and distinct string that describes the field. + + - fieldValue provides the data for the field as a basic_string_view, e.g. + std::string_view, std::wstring_view, std::u16string_view, std::u32string_view. + The event will include the provided data up to the first '\0' value (if any). + + Note: you can either provide an actual std::string_view for this parameter or you + can explicitly specify the char type, e.g. AddString(), and then you can + take advantage of string_view's implicit conversions. + + - format indicates how the decoder should interpret the field data. For example, + if the field value is a Unicode string, you would likely set format to + default (resulting in the field decoding as string_utf, and if the field value + is a binary blob, you would likely set format to hex_bytes. + + - fieldTag is a 16-bit integer that will be recorded in the field and can be + used for any provider-defined purpose. Use 0 if you are not using field tags. + + Types: + + - The field will be encoded as one of zstring_char8, zstring_char16, or + zstring_char32. + - If format is default, the field will be formatted as string_utf. + - Usable formats include: hex_bytes, string_utf_bom, string_xml, string_json. + - For 8-bit char types, you may also use format string8, indicating a non-Unicode + string (usually treated as Latin-1). + + Note that event_field_format_default saves 1 byte in the trace. For string/binary + encodings, event_field_format_default is treated as event_field_format_string_utf, + so you can save 1 byte in the trace by using event_field_format_default instead of + event_field_format_string_utf for string fields. + + This is the same as AddString except that the field will be encoded as a + nul-terminated string instead of as a counted string. In most cases you + should prefer AddString and use this method only if you specifically need + the nul-terminated encoding. + */ + template + auto // Returns EventBuilder& + AddNulTerminatedString( + std::string_view fieldName, + std::basic_string_view fieldValue, + event_field_format format, + uint16_t fieldTag = 0) noexcept + -> typename StringEnabled::type // enable_if + { + RawAddMeta(fieldName, StringEnabled::ZStringEncoding, format, fieldTag); + RawAddDataNulTerminated(fieldValue); + return *this; + } + + /* + Adds a field containing a sequence of nul-terminated strings. + Note that you must provide the char type as the first template parameter, e.g. + AddNulTerminatedStringRange or AddNulTerminatedStringRange. + + - fieldName should be a short and distinct string that describes the field. + + - fieldBeginIterator..fieldEndIterator provide the data for the field as a + beginIterator-endIterator pair (can be beginPtr and endPtr if values are in a + contiguous range). The iterators must return a value that is + implicitly-convertible to std::basic_string_view, e.g. + *fieldBeginIterator must return something like a char* or a std::string_view. + + - format indicates how the decoder should interpret the field data. For example, + if the field value contains Unicode strings, you would likely set format to + default (resulting in the field decoding as string_utf, and if the field value + contains binary blobs, you would likely set format to hex_bytes. + + - fieldTag is a 16-bit integer that will be recorded in the field and can be + used for any provider-defined purpose. Use 0 if you are not using field tags. + + This is the same as AddStringRange except that the field will be + encoded as a nul-terminated string instead of as a counted string. In most cases + you should prefer AddStringRange and use this method only if you + specifically need the nul-terminated encoding. + */ + template + auto // Returns EventBuilder& + AddNulTerminatedStringRange( + std::string_view fieldName, + BeginItTy fieldBeginIterator, + EndItTy fieldEndIterator, + event_field_format format, + uint16_t fieldTag = 0) noexcept + -> typename StringEnabled< + CharTy, + std::is_convertible>::value + >::type // enable_if + { + RawAddMeta( + fieldName, + StringEnabled::ZStringEncoding | event_field_encoding_varray_flag, + format, + fieldTag); + RawAddDataRange(fieldBeginIterator, fieldEndIterator, + [](EventBuilder* pThis, std::basic_string_view value) + { + pThis->RawAddDataNulTerminated(value); + }); + return *this; + } + + /* + *Advanced scenarios:* Directly adds unchecked metadata to the event. Using this + method may result in events that do not decode correctly. + + There are a few things that are supported by EventHeader that cannot be expressed + by directly calling the add methods, e.g. array-of-struct. If these edge cases are + important, you can use the RawAddMeta and RawAddData methods to generate events + that would otherwise be impossible. Doing this requires advanced understanding of + the EventHeader encoding system. If done incorrectly, the resulting events will not + decode properly. + */ + EventBuilder& + RawAddMetaScalar( + std::string_view fieldName, + event_field_encoding encoding, + event_field_format format, + uint16_t fieldTag = 0) noexcept + { + assert(encoding == (encoding & event_field_encoding_value_mask)); + RawAddMeta(fieldName, encoding, format, fieldTag); + return *this; + } + + /* + *Advanced scenarios:* Directly adds unchecked metadata to the event. Using this + method may result in events that do not decode correctly. + + There are a few things that are supported by EventHeader that cannot be expressed + by directly calling the add methods, e.g. array-of-struct. If these edge cases are + important, you can use the RawAddMeta and RawAddData methods to generate events + that would otherwise be impossible. Doing this requires advanced understanding of + the EventHeader encoding system. If done incorrectly, the resulting events will not + decode properly. + */ + EventBuilder& + RawAddMetaVcount( + std::string_view fieldName, + event_field_encoding encoding, + event_field_format format, + uint16_t fieldTag = 0) noexcept + { + assert(encoding == (encoding & event_field_encoding_value_mask)); + RawAddMeta(fieldName, encoding | event_field_encoding_varray_flag, format, fieldTag); + return *this; + } + + /* + *Advanced scenarios:* Directly adds unchecked metadata to the event. Using this + method may result in events that do not decode correctly. + + There are a few things that are supported by EventHeader that cannot be expressed + by directly calling the add methods, e.g. array-of-struct. If these edge cases are + important, you can use the RawAddMeta and RawAddData methods to generate events + that would otherwise be impossible. Doing this requires advanced understanding of + the EventHeader encoding system. If done incorrectly, the resulting events will not + decode properly. + */ + template + EventBuilder& + RawAddDataValue(ValTy const& fieldValue) noexcept + { + auto const cb = sizeof(fieldValue); + uint8_t* pData; + if (!m_data.ensure_space_for(cb, &pData)) + { + m_error = true; + } + else + { + memcpy(pData, std::addressof(fieldValue), cb); + m_data.advance(cb); + } + + return *this; + } + + /* + *Advanced scenarios:* Directly adds unchecked metadata to the event. Using this + method may result in events that do not decode correctly. + + There are a few things that are supported by EventHeader that cannot be expressed + by directly calling the add methods, e.g. array-of-struct. If these edge cases are + important, you can use the RawAddMeta and RawAddData methods to generate events + that would otherwise be impossible. Doing this requires advanced understanding of + the EventHeader encoding system. If done incorrectly, the resulting events will not + decode properly. + */ + template + EventBuilder& + RawAddDataValues(_In_reads_(count) ValTy const* values, uint16_t count) noexcept + { + auto const cb = sizeof(values[0]) * count; + uint8_t* pData; + if (!m_data.ensure_space_for(cb, &pData)) + { + m_error = true; + } + else + { + memcpy(pData, values, cb); + m_data.advance(cb); + } + + return *this; + } + + private: + + // Returns: The offset of the format byte, or -1. + size_t + RawAddMeta(std::string_view fieldName, uint8_t encoding, uint8_t format, uint16_t fieldTag) noexcept + { + size_t formatOffset = -1; + + // Precondition violation: fieldName must not contain '\0'. + assert(fieldName.find('\0') == fieldName.npos); + + uint8_t* pMeta; + if (!m_meta.ensure_space_for(fieldName.size() + 7, &pMeta)) + { + m_error = true; + } + else + { + size_t cMeta = 0; + + memcpy(pMeta, fieldName.data(), fieldName.size()); + cMeta += fieldName.size(); + + pMeta[cMeta] = '\0'; + cMeta += 1; + + if (fieldTag != 0) + { + pMeta[cMeta] = encoding | 0x80; + cMeta += 1; + pMeta[cMeta] = format | 0x80; + formatOffset = m_meta.size() + cMeta; + cMeta += 1; + memcpy(&pMeta[cMeta], &fieldTag, sizeof(fieldTag)); + cMeta += sizeof(fieldTag); + } + else if (format != 0) + { + pMeta[cMeta] = encoding | 0x80; + cMeta += 1; + pMeta[cMeta] = format; + formatOffset = m_meta.size() + cMeta; + cMeta += 1; + } + else + { + pMeta[cMeta] = encoding; + cMeta += 1; + } + + m_meta.advance(cMeta); + } + + return formatOffset; + } + + template + EventBuilder& + RawAddDataNulTerminated(std::basic_string_view fieldValue) noexcept + { + auto cch = fieldValue.find('\0'); + if (cch > fieldValue.size()) + { + cch = fieldValue.size(); + } + + auto const cb = cch * sizeof(CharTy); + uint8_t* pData; + if (!m_data.ensure_space_for(cb + sizeof(CharTy), &pData)) + { + m_error = true; + } + else + { + memcpy(pData, fieldValue.data(), cb); + memset(pData + cb, 0, sizeof(CharTy)); + m_data.advance(cb + sizeof(CharTy)); + } + + return *this; + } + + template + EventBuilder& + RawAddDataCounted(std::basic_string_view fieldValue) noexcept + { + uint16_t count = fieldValue.size() <= UINT16_MAX ? fieldValue.size() : UINT16_MAX; + auto const cb = fieldValue.size() * sizeof(CharTy); + uint8_t* pData; + if (!m_data.ensure_space_for(sizeof(count) + cb, &pData)) + { + m_error = true; + } + else + { + memcpy(pData, &count, sizeof(count)); + memcpy(pData + sizeof(count), fieldValue.data(), cb); + m_data.advance(sizeof(count) + cb); + } + + return *this; + } + + template + EventBuilder& + RawAddDataRange(BeginItTy beginIt, EndItTy endIt, AddDataFnTy&& addData) noexcept + { + uint16_t count = 0; + uint8_t* pCountDontUse; // We may reallocate before we update count. + auto const countPos = m_data.size(); // Use this instead. + if (!m_data.ensure_space_for(sizeof(count), &pCountDontUse)) + { + m_error = true; + } + else + { + m_data.advance(sizeof(count)); + + for (; beginIt != endIt; ++beginIt) + { + if (count == UINT16_MAX) + { + break; + } + + count += 1; + addData(this, *beginIt); + } + + memcpy(m_data.data() + countPos, &count, sizeof(count)); + } + + return *this; + } + }; + + /* + Returns true if the eventSet is valid and enabled. + Returns false if the eventSet is NULL or disabled. + + Since RegisterSet() can return NULL for out-of-memory, it is important to + check for NULL before calling methods on eventSet. In cases where the + eventSet might be NULL, you can use if(Enabled(eventSet)) instead of + if(eventSet && eventSet->Enabled()). + */ + inline bool + Enabled(std::shared_ptr const& eventSet) noexcept + { + return eventSet && eventSet->Enabled(); + } + + /* + Returns true if the eventSet is valid and enabled. + Returns false if the eventSet is NULL or disabled. + + Since RegisterSet() can return NULL for out-of-memory, it is important to + check for NULL before calling methods on eventSet. In cases where the + eventSet might be NULL, you can use if(Enabled(eventSet)) instead of + if(eventSet && eventSet->Enabled()). + */ + inline bool + Enabled(EventSet const* eventSet) noexcept + { + return eventSet && eventSet->Enabled(); + } + + /* + Convenience type for passing a 16-byte (128-bit) value to AddValue or + AddValueRange: + + - If you already have a 16-byte trivially-copyable struct defined, + AddValue and AddValueRange will accept that directly, so there is no + need to use this. + + - If you have a 16-byte array like "char MyGuid[16]" or "char* pMyGuid", + you will need to cast it to a 128-bit value type like Value128 to use it + with AddValue or AddValueRange. + + Examples: + + - eb.AddValue("GUID", *(Value128 const*)MyGuid, event_field_format_uuid); + + - eb.AddValueRange("IPv6 addresses", + (Value128 const*)pMyAddressesBegin, (Value128 const*)pMyAddressesEnd, + event_field_format_ipv6); + */ + struct Value128 + { + char Data[16]; + }; +} +// namespace ehd + +#endif // _included_EventHeaderDynamic_h diff --git a/src/native/external/LinuxTracepoints/libeventheader-tracepoint/include/eventheader/TraceLoggingProvider.h b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/include/eventheader/TraceLoggingProvider.h new file mode 100644 index 0000000000000..809f505100de0 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/include/eventheader/TraceLoggingProvider.h @@ -0,0 +1,2150 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +/* +This is an implementation of TraceLoggingProvider.h that writes data to +Linux tracepoints via the UserEvents system. + +Prerequisites: + +- If prerequisites are not met then register/write/unregister will be no-ops. +- Kernel must be built with tracefs and UserEvents (CONFIG_USER_EVENTS=y). +- tracefs mounted (e.g. /sys/kernel/tracing or /sys/kernel/debug/tracing). +- Caller must have appropriate permissions: x on tracefs mount point, + rw on tracefs/user_events_data. + +Quick start: + +#include + +TRACELOGGING_DEFINE_PROVIDER( // defines the MyProvider symbol + MyProvider, // Name of the provider symbol to define + "MyCompany_MyComponent_MyProvider", // Human-readable provider name, no ' ' or ':' chars. + // {d5b90669-1aad-5db8-16c9-6286a7fcfe33} // Provider guid (not used on Linux) + (0xd5b90669,0x1aad,0x5db8,0x16,0xc9,0x62,0x86,0xa7,0xfc,0xfe,0x33)); + +int main(int argc, char* argv[]) +{ + TraceLoggingRegister(MyProvider); + TraceLoggingWrite(MyProvider, "MyEventName", + TraceLoggingString(argv[0], "arg0"), // field name is "arg0" + TraceLoggingInt32(argc)); // field name is implicitly "argc" + TraceLoggingUnregister(MyProvider); + return 0; +} + +Usage note: + +Symbols starting with "TRACELOGGING" or "TraceLogging" are part of the public +interface of this header. Symbols starting with "_tlg" are private internal +implementation details that are not supported for use outside this header and +may be changed or removed in future versions of this header. + +TraceLoggingProvider.h for Linux UserEvents behaves differently from the ETW +(Windows) version: + +- TRACELOGGING_DEFINE_PROVIDER requires a provider name that is less than + EVENTHEADER_NAME_MAX (256) characters in length and contains no ' ' + or ':' characters. (Decoding tools may impose additional restrictions; for + best compatibility, use only [A-Za-z0-9_] chars.) +- TRACELOGGING_DEFINE_PROVIDER ignores the provider GUID. +- TraceLoggingHProvider is not provided. In the UserEvents implementation of + TraceLoggingProvider.h, providers are referenced by symbol (token), not by + handle (pointer). You cannot store a provider symbol in a variable, pass it + as a parameter or return it from a function. The provider symbol used as the + first parameter to TraceLoggingWrite must be the same symbol as was used in + TRACELOGGING_DEFINE_PROVIDER. +- TRACELOGGING_DEFINE_PROVIDER_STORAGE is not provided. +- TraceLoggingOptionGroup is not provided. Instead, use + TraceLoggingOptionGroupName. +- TraceLoggingRegisterEx is not provided. No support for notification + callbacks. +- TraceLoggingProviderId is not provided. Instead, use + TraceLoggingProviderName. +- TraceLoggingSetInformation is not provided. +- TraceLoggingWriteEx is not provided. +- TraceLoggingChannel is not provided. +- TraceLoggingEventTag supports 16-bit tag. (ETW supports a 28-bit tag.) +- TraceLoggingDescription is ignored. +- TraceLoggingCustomAttribute is not provided. +- TraceLoggingValue will not accept GUID, FILETIME, SYSTEMTIME, or SID values. +- TraceLoggingCodePointer is not provided. +- TraceLoggingFileTime, TraceLoggingFileTimeUtc, TraceLoggingSystemTime, and + TraceLoggingSystemTimeUtc are not provided. Instead, use Linux-only + TraceLoggingTime32 or TraceLoggingTime64. +- TraceLoggingTid is not provided. Instead, use TraceLoggingPid. +- TraceLoggingWinError, TraceLoggingNTStatus, TraceLoggingHResult are not + provided. Instead, use Linux-only TraceLoggingErrno. +- TraceLoggingAnsiString and TraceLoggingUnicodeString are not provided. On + Windows, these are used for Windows-specific ANSI_STRING and UNICODE_STRING + structures. +- TraceLoggingSid is not provided. +- TraceLoggingBinaryBuffer is not provided. +- TraceLoggingCustom is not provided. +- TraceLoggingPackedDataEx is not provided. +- TraceLoggingGuid expects a uint8_t[16] value in RFC 4122 (big-endian) byte + order. In Windows, TraceLoggingGuid expects a GUID (struct) and the data is + stored as little-endian. +- Field tag is a 16-bit value. (ETW supports a 28-bit tag.) + +The following features are specific to Linux UserEvents (not present for ETW): + +- Use TraceLoggingIdVersion to specify a stable id and version for an event. +- Use TraceLoggingProviderName instead of TraceLoggingProviderId. +- Use TraceLoggingOptionGroupName instead of TraceLoggingOptionGroup. +- Use TraceLoggingErrno for logging errno values. +- Use TraceLoggingTime32 and TraceLoggingTime64 for logging time_t values. +- Use TraceLoggingChar32 and TraceLoggingString32 for logging char32_t + characters and strings. +*/ + +#pragma once +#ifndef _included_TraceLoggingProvider_h +#define _included_TraceLoggingProvider_h 1 + +#include "eventheader-tracepoint.h" +#include +#include +#include +#include + +#ifdef __EDG__ +#pragma region Public_interface +#endif + +/* +This structure is left undefined to ensure a compile error for any attempt to +copy or dereference the provider symbol. The provider symbol is a token, not a +variable or a handle. +*/ +struct TraceLoggingProviderSymbol; + +/* +Macro TRACELOGGING_DECLARE_PROVIDER(providerSymbol): +Invoke this macro to forward-declare a provider symbol. +TRACELOGGING_DECLARE_PROVIDER is typically used in a header. + +An invocation of + + TRACELOGGING_DECLARE_PROVIDER(MyProvider); + +can be thought of as expanding to something like this: + + extern "C" TraceLoggingProviderSymbol MyProvider; + +A symbol declared by TRACELOGGING_DECLARE_PROVIDER must later be defined in a +.c or .cpp file using the TRACELOGGING_DEFINE_PROVIDER macro. +*/ +#define TRACELOGGING_DECLARE_PROVIDER(providerSymbol) \ + _tlg_EXTERN_C eventheader_tracepoint const* _tlg_PASTE2(__start__tlgEventPtrs_, providerSymbol)[] __attribute__((weak, visibility("hidden"))); \ + _tlg_EXTERN_C eventheader_tracepoint const* _tlg_PASTE2(__stop__tlgEventPtrs_, providerSymbol)[] __attribute__((weak, visibility("hidden"))); \ + _tlg_EXTERN_C struct TraceLoggingProviderSymbol providerSymbol __attribute__((visibility("hidden"))); /* Empty provider variable to help with code navigation. */ \ + _tlg_EXTERN_C eventheader_provider const _tlg_PASTE2(_tlgProv_, providerSymbol) __attribute__((visibility("hidden"))) /* Actual provider variable is hidden behind prefix. */ + +/* +Macro TRACELOGGING_DEFINE_PROVIDER(providerSymbol, "ProviderName", (providerId), [option]): +Invoke this macro to create the global storage for a provider. + +An invocation of + + TRACELOGGING_DEFINE_PROVIDER(MyProvider, "MyProviderName", + (0xb3864c38, 0x4273, 0x58c5, 0x54, 0x5b, 0x8b, 0x36, 0x08, 0x34, 0x34, 0x71)); + +can be thought of as expanding to something like this: + + extern "C" TraceLoggingProviderSymbol MyProvider = { ... }; + +The "ProviderName" specifies a unique name that identifies the provider in the +logged events. It must be a char string literal (not a variable), must be less +than EVENTHEADER_NAME_MAX (256) characters long, and may not contain +' ' or ':' characters. Some versions of libtracefs impose additional +restrictions; for best compatibility, use only [A-Za-z0-9_] characters. + +The providerId specifies a unique GUID that identifies the provider. The +providerId parameter must be a parenthesized list of 11 integers e.g. +(n1, n2, n3, ... n11). Typically the GUID is generated as a hash of the name. + +Established convention for GUID generation, expressed as a Python function: + + def providerid_from_name(providername : str) -> uuid.UUID: + sha1 = hashlib.sha1(usedforsecurity = False) + sha1.update(b'\x48\x2C\x2D\xB2\xC3\x90\x47\xC8\x87\xF8\x1A\x15\xBF\xC1\x30\xFB') + sha1.update(providername.upper().encode('utf_16_be')) + arr = bytearray(sha1.digest()[0:16]) + arr[7] = (arr[7] & 0x0F) | 0x50 + return uuid.UUID(bytes_le = bytes(arr)) + +After the providerId GUID, you may optionally specify a +TraceLoggingOptionGroupName("...") macro to set the provider group name, e.g. + + TRACELOGGING_DEFINE_PROVIDER(MyProvider, "MyProviderName", + (0xb3864c38, 0x4273, 0x58c5, 0x54, 0x5b, 0x8b, 0x36, 0x08, 0x34, 0x34, 0x71), + TraceLoggingOptionGroupName("mygroupname")); + +Note that the provider symbol is created in the "unregistered" state. A call +to TraceLoggingWrite with an unregistered provider is a no-op. Call +TraceLoggingRegister to register the provider. +*/ +#define TRACELOGGING_DEFINE_PROVIDER(providerSymbol, providerName, providerId, ...) \ + TRACELOGGING_DECLARE_PROVIDER(providerSymbol); \ + static_assert( \ + EVENTHEADER_NAME_MAX >= sizeof("" providerName "_LnnKnnnnnnnnnnnnnnnn" _tlgProviderOptions(__VA_ARGS__)), \ + "TRACELOGGING_DEFINE_PROVIDER providerName + options is too long"); \ + _tlgParseProviderId(providerId) \ + static tracepoint_provider_state _tlg_PASTE2(_tlgProvState_, providerSymbol) = TRACEPOINT_PROVIDER_STATE_INIT; \ + _tlg_EXTERN_C_CONST eventheader_provider _tlg_PASTE2(_tlgProv_, providerSymbol) = { \ + &_tlg_PASTE2(_tlgProvState_, providerSymbol), \ + _tlgProviderOptions(__VA_ARGS__), \ + "" providerName \ + } + +/* +Macro TraceLoggingOptionGroupName("groupname"): +Wrapper macro for use in TRACELOGGING_DEFINE_PROVIDER that declares the +provider's membership in a provider group. + +The "groupname" specifies a string that can be used to identify a group of +related providers. This must be a char string literal containing only ASCII +digits and lowercase letters, [a-z0-9]. The total +strlen(ProviderName + groupname) must be less than +EVENTHEADER_NAME_MAX (256). +*/ +#define TraceLoggingOptionGroupName(groupName) \ + TraceLoggingOptionGroupName(groupName) + +/* +Macro TraceLoggingUnregister(providerSymbol): +Call this function to unregister your provider. Normally you will register at +component initialization (program startup or shared object load) and unregister +at component shutdown (program exit or shared object unload). + +Thread safety: It is NOT safe to call TraceLoggingUnregister while a +TraceLoggingRegister, TraceLoggingUnregister, TraceLoggingWrite, or +TraceLoggingProviderEnabled for the same provider could be in progress on +another thread. + +It is ok to call TraceLoggingUnregister on a provider that has not been +registered (e.g. if the call to TraceLoggingRegister failed). Unregistering an +unregistered provider is a safe no-op. + +After unregistering a provider, it is ok to register it again. In other words, +the following sequence is ok: + + TraceLoggingRegister(MyProvider); + ... + TraceLoggingUnregister(MyProvider); + ... + TraceLoggingRegister(MyProvider); + ... + TraceLoggingUnregister(MyProvider); + +Re-registering a provider should only happen because a component has been +uninitialized and then reinitialized. You should not register and unregister a +provider each time you need to write a few events. + +Note that unregistration is important, especially in the case of a shared +object that might be dynamically unloaded before the process ends. Failure to +unregister may cause process memory corruption as the kernel tries to update +the enabled/disabled states of tracepoint variables that no longer exist. +*/ +#define TraceLoggingUnregister(providerSymbol) \ + (eventheader_close_provider( \ + &_tlg_PASTE2(_tlgProv_, providerSymbol) )) + +/* +Macro TraceLoggingRegister(providerSymbol): +Call this function to register your provider. Normally you will register at +component initialization (program startup or shared object load) and unregister +at component shutdown (program exit or shared object unload). + +Returns 0 for success, errno otherwise. Result is primarily for +debugging/diagnostics and is usually ignored for production code. If +registration fails, subsequent TraceLoggingWrite and TraceLoggingUnregister +will be safe no-ops. + +Thread safety: It is NOT safe to call TraceLoggingRegister while a +TraceLoggingRegister or TraceLoggingUnregister for the same provider might be +in progress on another thread. + +The provider must be in the "unregistered" state. It is an error to call +TraceLoggingRegister on a provider that is already registered. +*/ +#define TraceLoggingRegister(providerSymbol) \ + (eventheader_open_provider_with_events( \ + &_tlg_PASTE2(_tlgProv_, providerSymbol), \ + _tlg_PASTE2(__start__tlgEventPtrs_, providerSymbol), \ + _tlg_PASTE2(__stop__tlgEventPtrs_, providerSymbol) )) + +/* +Macro TraceLoggingProviderEnabled(providerSymbol, eventLevel, eventKeyword): +Returns true (non-zero) if a TraceLoggingWrite using the specified +providerSymbol, eventLevel, and eventKeyword would be enabled, false if it +would be disabled. + +Example: + + if (TraceLoggingProviderEnabled(MyProvider, event_level_warning, 0x1f)) + { + // Prepare complex data needed for event. + int myIntVar; + wchar_t const* myString; + + ExpensiveGetIntVar(&myIntVar); + ExpensiveGetString(&myString); + + TraceLoggingWrite(MyProvider, "MyEventName", + TraceLoggingLevel(event_level_warning), + TraceLoggingKeyword(0x1f), + TraceLoggingInt32(myIntVar), + TraceLoggingWideString(myString)); + + CleanupString(myString); + } + +Note that the TraceLoggingWrite macro already checks whether the tracepoint is +enabled -- it skips evaluating the field value expressions and skips sending +the event if the tracepoint is not enabled. You only need to make your own +call to TraceLoggingProviderEnabled if you want to control something other +than TraceLoggingWrite. + +Implementation details: This macro registers an inert tracepoint with the +specified provider, level, and keyword, and returns true if that tracepoint is +enabled. +*/ +#define TraceLoggingProviderEnabled(providerSymbol, eventLevel, eventKeyword) ({ \ + enum { \ + _tlgKeywordVal = (uint64_t)(eventKeyword), \ + _tlgLevelVal = (uint64_t)(eventLevel) \ + }; \ + static tracepoint_state _tlgEvtState = TRACEPOINT_STATE_INIT; \ + static eventheader_tracepoint const _tlgEvt = { \ + &_tlgEvtState, \ + (eventheader_extension*)0, \ + { \ + eventheader_flag_default, \ + 0, \ + 0, \ + 0, \ + 0, \ + _tlgLevelVal \ + }, \ + _tlgKeywordVal \ + }; \ + static eventheader_tracepoint const* const _tlgEvtPtr \ + __attribute__((section("_tlgEventPtrs_" _tlg_STRINGIZE(providerSymbol)), used)) \ + = &_tlgEvt; \ + TRACEPOINT_ENABLED(&_tlgEvtState); }) + +/* +Macro TraceLoggingProviderName(providerSymbol): +Returns the provider's name as a nul-terminated const char*. +*/ +#define TraceLoggingProviderName(providerSymbol) \ + (&_tlg_PASTE2(_tlgProv_, providerSymbol).name[0]) + +/* +Macro TraceLoggingProviderOptions(providerSymbol): +Returns the provider's options as a nul-terminated const char*. +*/ +#define TraceLoggingProviderOptions(providerSymbol) \ + (&_tlg_PASTE2(_tlgProv_, providerSymbol).options[0]) + +/* +Macro TraceLoggingWrite(providerSymbol, "EventName", args...): +Invoke this macro to log an event. + +Example: + + TraceLoggingWrite(MyProvider, "MyEventName", + TraceLoggingInt32(myIntVar), + TraceLoggingWideString(myString)); + +The eventName parameter must be a char string literal (not a variable) and must +not contain any ';' or '\0' characters. The name will be treated as utf-8. + +Supports up to 99 args (subject to compiler limitations). Each arg must be a +wrapper macro such as TraceLoggingLevel, TraceLoggingKeyword, TraceLoggingInt32, +TraceLoggingString, etc. +*/ +#define TraceLoggingWrite(providerSymbol, eventName, ...) \ + _tlgWriteImp(providerSymbol, eventName, _tlg_NULL, _tlg_NULL, ##__VA_ARGS__) + +/* +Macro TraceLoggingWriteActivity(providerSymbol, "EventName", pActivityId, pRelatedActivityId, args...): +Invoke this macro to log an event with ActivityId and optional RelatedActivityId data. + +Example: + + TraceLoggingWriteActivity(MyProvider, "MyEventName", + pActivityGuid, // 128-bit ID, i.e. uint8_t[16]. + pRelatedActivityGuid, // 128-bit ID, or NULL. Usually NULL (non-NULL only when used with opcode START). + TraceLoggingOpcode(WINEVENT_OPCODE_START), + TraceLoggingInt32(myIntVar), + TraceLoggingWideString(myString)); + +The event name must be a char string literal (not a variable) and must not +contain any ';' or '\0' characters. The name will be treated as utf-8. + +Supports up to 99 args (subject to compiler limitations). Each arg must be a +wrapper macro such as TraceLoggingLevel, TraceLoggingKeyword, TraceLoggingInt32, +TraceLoggingString, etc. +*/ +#define TraceLoggingWriteActivity(providerSymbol, eventName, pActivityId, pRelatedActivityId, ...) \ + _tlgWriteImp(providerSymbol, eventName, pActivityId, pRelatedActivityId, ##__VA_ARGS__) + +/* +Macro TraceLoggingLevel(eventLevel) +Wrapper macro for setting the event's level. + +Example: + + TraceLoggingWrite(MyProvider, "MyEventName", + TraceLoggingLevel(event_level_warning), + TraceLoggingWideString(myString)); + +The eventLevel parameter must be a compile-time constant 1 to 255, typically +an event_level_??? constant from eventheader.h. If no TraceLoggingLevel(n) arg +is set on an event, the event will default to level 5 (Verbose). If multiple +TraceLoggingLevel(n) args are provided, the level from the last +TraceLoggingLevel(n) will be used. +*/ +#define TraceLoggingLevel(eventLevel) _tlgArgLevel(eventLevel) + +/* +Macro TraceLoggingKeyword(eventKeyword): +Wrapper macro for setting the event's keyword(s). + +Example: + + TraceLoggingWrite(MyProvider, "MyEventName", + TraceLoggingKeyword(MyNetworkingKeyword), + TraceLoggingWideString(myString)); + +The eventKeyword parameter must be a compile-time constant 0 to UINT64_MAX. +Each bit in the parameter corresponds to a user-defined event category. If an +event belongs to multiple categories, the bits for each category should be +OR'd together to create the event's keyword value. If no +TraceLoggingKeyword(n) arg is provided, the default keyword is 0. If multiple +TraceLoggingKeyword(n) args are provided, they are OR'd together. +*/ +#define TraceLoggingKeyword(eventKeyword) _tlgArgKeyword(eventKeyword) + +/* +Macro TraceLoggingIdVersion(eventId, eventVersion): +Wrapper macro for setting the stable id and/or version for an event. + +Example: + + TraceLoggingWrite(MyProvider, "MyEventName", + TraceLoggingIdVersion(123, 0), + TraceLoggingWideString(myString)); + +By default, TraceLogging events have event id = 0 and version = 0, indicating +that they have not been assigned a stable numeric event id. The events are +identified by ProviderName+EventName which is usually sufficient. + +In some cases, it is useful to manually assign a stable numeric event id to an +event. This can help with event routing and filtering. Use +TraceLoggingIdVersion to specify the id and version of an event. + +- The id should be a manually-assigned value from 1 to 65535. +- The version must be a value from 0 to 255. It should start at 0 and should + be incremented each time the event is changed (e.g. when a field is added, + removed, renamed, or the field type is changed, or if event semantics change + in some other way). + +If multiple TraceLoggingIdVersion args are provided, the values from the last +TraceLoggingIdVersion are used. +*/ +#define TraceLoggingIdVersion(eventId, eventVersion) _tlgArgIdVersion(eventId, eventVersion) + +/* +Macro TraceLoggingOpcode(eventOpcode): +Wrapper macro for setting the event's opcode. + +Example: + + TraceLoggingWrite(MyProvider, "MyEventName", + TraceLoggingOpcode(event_opcode_activity_start), + TraceLoggingWideString(myString)); + +The eventOpcode parameter must be a compile-time constant 0 to 255 (typically +an event_opcode_??? constant from eventheader.h). If multiple +TraceLoggingOpcode(n) args are provided, the value from the last +TraceLoggingOpcode(n) is used. +*/ +#define TraceLoggingOpcode(eventOpcode) _tlgArgOpcode(eventOpcode) + +/* +Macro TraceLoggingEventTag(eventTag): +Wrapper macro for setting the event's tag. + +Example: + + TraceLoggingWrite(MyProvider, "MyEventName", + TraceLoggingEventTag(0x200), + TraceLoggingWideString(myString)); + +Tag is a 16-bit integer. The semantics of the tag are defined by the event +provider. +*/ +#define TraceLoggingEventTag(eventTag) _tlgArgEventTag(eventTag) + +/* +Macro TraceLoggingDescription(description): +Wrapper macro for setting a description for an event. + +UserEvents semantics: TraceLoggingDescription has no effect and functions as a +comment. + +Example: + + TraceLoggingWrite(MyProvider, "MyEventName", + TraceLoggingDescription("My event's detailed description"), + TraceLoggingWideString(myString)); +*/ +#define TraceLoggingDescription(description) _tlgArgIgnored() + +/* +Macro TraceLoggingStruct(fieldCount, "structName", "description", tag): +Wrapper macro for defining a group of related fields in an event. + +The description and tag parameters are optional. + +The fieldCount parameter must be a compile-time constant 1 to 127. It indicates +the number of fields that will be considered to be part of the struct. A struct +and all of its contained fields count as a single field in any parent structs. + +The name parameter must be a char string literal (not a variable) and must not +contain any ';' or '\0' characters. + +If provided, the description parameter must be a char string literal. + +If provided, the tag parameter must be a 16-bit integer value. + +Example: + + TraceLoggingWrite(MyProvider, "MyEventName", + TraceLoggingStruct(2, "PersonName"), + TraceLoggingWideString(Last), + TraceLoggingWideString(First)); +*/ +#define TraceLoggingStruct(fieldCount, name, ...) \ + _tlgArgStruct(fieldCount, event_field_encoding_struct, _tlgNdt(TraceLoggingStruct, value, name, ##__VA_ARGS__)) + +#ifdef __cplusplus +/* +Macro TraceLoggingValue(value, "name", "description", tag): +Wrapper macro for event fields. Automatically deduces value type. C++ only. + +The name, description, and tag parameters are optional. + +If provided, the name parameter must be a char string literal (not a variable) +and must not contain any ';' or '\0' characters. If the name is not provided, +the value parameter is used to generate a name. Name is treated as utf-8. + +If provided, the description parameter must be a string literal. +Field description has no effect and functions as a comment. + +If provided, the tag parameter must be a 16-bit integer value. + +Examples: +- TraceLoggingValue(val1) // field name = "val1", description = unset, tag = 0. +- TraceLoggingValue(val1, "name") // field name = "name", description = unset, tag = 0. +- TraceLoggingValue(val1, "name", "desc" // field name = "name", description = "desc", tag = 0. +- TraceLoggingValue(val1, "name", "desc", 0x4) // field name = "name", description = "desc", tag = 0x4. + +Based on the type of val, TraceLoggingValue(val, ...) is equivalent to one of +the following: +- bool --> TraceLoggingBoolean(val, ...) +- char --> TraceLoggingChar(val, ...) +- char16_t --> TraceLoggingChar16(val, ...) +- char32_t --> TraceLoggingChar32(val, ...) +- wchar_t --> TraceLoggingWChar(val, ...) +- intNN_t --> TraceLoggingIntNN(val, ...) +- uintNN_t --> TraceLoggingUIntNN(val, ...) +- float --> TraceLoggingFloat32(val, ...) +- double --> TraceLoggingFloat64(val, ...) +- const void* --> TraceLoggingPointer(val, ...) // Logs the pointer's value, not the data at which it points. +- const char* --> TraceLoggingString(val, ...) // Assumes nul-terminated latin1 string. NULL is the same as "". +- const char16_t* --> TraceLoggingString16(val, ...) // Assumes nul-terminated utf-16 string. NULL is the same as u"". +- const char32_t* --> TraceLoggingString32(val, ...) // Assumes nul-terminated utf-32 string. NULL is the same as U"". +- const wchar_t* --> TraceLoggingWideString(val, ...) // Assumes nul-terminated utf-16/32 string (based on size of wchar_t). NULL is the same as L"". +*/ +#define TraceLoggingValue(value, ...) _tlgArgAuto(value, _tlgNdt(TraceLoggingValue, value, ##__VA_ARGS__)) +#endif // __cplusplus + +/* +Wrapper macros for event fields with simple scalar values. +Usage: TraceLoggingInt32(value, "name", "description", tag). + +The name, description, and tag parameters are optional. + +If provided, the name parameter must be a char string literal (not a variable) +and must not contain any ';' or '\0' characters. If the name is not provided, +the value parameter is used to generate a name. Name is treated as utf-8. + +If provided, the description parameter must be a string literal. +Field description has no effect and functions as a comment. + +If provided, the tag parameter must be a 16-bit integer value. + +Notes: +- TraceLoggingBool is for 32-bit boolean values (e.g. int). +- TraceLoggingBoolean is for 8-bit boolean values (e.g. bool or char). + +Examples: +- TraceLoggingInt32(val1) // field name = "val1", description = unset, tag = 0. +- TraceLoggingInt32(val1, "name") // field name = "name", description = unset, tag = 0. +- TraceLoggingInt32(val1, "name", "desc") // field name = "name", description = "desc", tag = 0. +- TraceLoggingInt32(val1, "name", "desc", 0x4) // field name = "name", description = "desc", tag = 0x4. +*/ +#define TraceLoggingInt8(value, ...) _tlgArgValue(int8_t, value, event_field_encoding_value8, (event_field_format_signed_int), _tlgNdt(TraceLoggingInt8, value, ##__VA_ARGS__)) +#define TraceLoggingUInt8(value, ...) _tlgArgValue(uint8_t, value, event_field_encoding_value8, (), _tlgNdt(TraceLoggingUInt8, value, ##__VA_ARGS__)) +#define TraceLoggingInt16(value, ...) _tlgArgValue(int16_t, value, event_field_encoding_value16, (event_field_format_signed_int), _tlgNdt(TraceLoggingInt16, value, ##__VA_ARGS__)) +#define TraceLoggingUInt16(value, ...) _tlgArgValue(uint16_t, value, event_field_encoding_value16, (), _tlgNdt(TraceLoggingUInt16, value, ##__VA_ARGS__)) +#define TraceLoggingInt32(value, ...) _tlgArgValue(int32_t, value, event_field_encoding_value32, (event_field_format_signed_int), _tlgNdt(TraceLoggingInt32, value, ##__VA_ARGS__)) +#define TraceLoggingUInt32(value, ...) _tlgArgValue(uint32_t, value, event_field_encoding_value32, (), _tlgNdt(TraceLoggingUInt32, value, ##__VA_ARGS__)) +#define TraceLoggingInt64(value, ...) _tlgArgValue(int64_t, value, event_field_encoding_value64, (event_field_format_signed_int), _tlgNdt(TraceLoggingInt64, value, ##__VA_ARGS__)) +#define TraceLoggingUInt64(value, ...) _tlgArgValue(uint64_t, value, event_field_encoding_value64, (), _tlgNdt(TraceLoggingUInt64, value, ##__VA_ARGS__)) +#define TraceLoggingIntPtr(value, ...) _tlgArgValue(intptr_t, value, event_field_encoding_value_ptr, (event_field_format_signed_int), _tlgNdt(TraceLoggingIntPtr, value, ##__VA_ARGS__)) +#define TraceLoggingUIntPtr(value, ...) _tlgArgValue(uintptr_t, value, event_field_encoding_value_ptr, (), _tlgNdt(TraceLoggingUIntPtr, value, ##__VA_ARGS__)) +#define TraceLoggingLong(value, ...) _tlgArgValue(signed long, value, event_field_encoding_value_long,(event_field_format_signed_int), _tlgNdt(TraceLoggingLong, value, ##__VA_ARGS__)) +#define TraceLoggingULong(value, ...) _tlgArgValue(unsigned long,value, event_field_encoding_value_long,(), _tlgNdt(TraceLoggingULong, value, ##__VA_ARGS__)) +#define TraceLoggingHexInt8(value, ...) _tlgArgValue(int8_t, value, event_field_encoding_value8, (event_field_format_hex_int), _tlgNdt(TraceLoggingHexInt8, value, ##__VA_ARGS__)) +#define TraceLoggingHexUInt8(value, ...) _tlgArgValue(uint8_t, value, event_field_encoding_value8, (event_field_format_hex_int), _tlgNdt(TraceLoggingHexUInt8, value, ##__VA_ARGS__)) +#define TraceLoggingHexInt16(value, ...) _tlgArgValue(int16_t, value, event_field_encoding_value16, (event_field_format_hex_int), _tlgNdt(TraceLoggingHexInt16, value, ##__VA_ARGS__)) +#define TraceLoggingHexUInt16(value, ...) _tlgArgValue(uint16_t, value, event_field_encoding_value16, (event_field_format_hex_int), _tlgNdt(TraceLoggingHexUInt16, value, ##__VA_ARGS__)) +#define TraceLoggingHexInt32(value, ...) _tlgArgValue(int32_t, value, event_field_encoding_value32, (event_field_format_hex_int), _tlgNdt(TraceLoggingHexInt32, value, ##__VA_ARGS__)) +#define TraceLoggingHexUInt32(value, ...) _tlgArgValue(uint32_t, value, event_field_encoding_value32, (event_field_format_hex_int), _tlgNdt(TraceLoggingHexUInt32, value, ##__VA_ARGS__)) +#define TraceLoggingHexInt64(value, ...) _tlgArgValue(int64_t, value, event_field_encoding_value64, (event_field_format_hex_int), _tlgNdt(TraceLoggingHexInt64, value, ##__VA_ARGS__)) +#define TraceLoggingHexUInt64(value, ...) _tlgArgValue(uint64_t, value, event_field_encoding_value64, (event_field_format_hex_int), _tlgNdt(TraceLoggingHexUInt64, value, ##__VA_ARGS__)) +#define TraceLoggingHexIntPtr(value, ...) _tlgArgValue(intptr_t, value, event_field_encoding_value_ptr, (event_field_format_hex_int), _tlgNdt(TraceLoggingHexIntPtr, value, ##__VA_ARGS__)) +#define TraceLoggingHexUIntPtr(value, ...) _tlgArgValue(uintptr_t, value, event_field_encoding_value_ptr, (event_field_format_hex_int), _tlgNdt(TraceLoggingHexUIntPtr, value, ##__VA_ARGS__)) +#define TraceLoggingHexLong(value, ...) _tlgArgValue(signed long, value, event_field_encoding_value_long,(event_field_format_hex_int), _tlgNdt(TraceLoggingHexLong, value, ##__VA_ARGS__)) +#define TraceLoggingHexULong(value, ...) _tlgArgValue(unsigned long,value, event_field_encoding_value_long,(event_field_format_hex_int), _tlgNdt(TraceLoggingHexULong, value, ##__VA_ARGS__)) +#define TraceLoggingFloat32(value, ...) _tlgArgValue(float, value, event_field_encoding_value_float,(event_field_format_float), _tlgNdt(TraceLoggingFloat32, value, ##__VA_ARGS__)) +#define TraceLoggingFloat64(value, ...) _tlgArgValue(double, value, event_field_encoding_value_double,(event_field_format_float), _tlgNdt(TraceLoggingFloat64, value, ##__VA_ARGS__)) +#define TraceLoggingBoolean(value, ...) _tlgArgValue(uint8_t, value, event_field_encoding_value8, (event_field_format_boolean), _tlgNdt(TraceLoggingBoolean, value, ##__VA_ARGS__)) +#define TraceLoggingBool(value, ...) _tlgArgValue(int32_t, value, event_field_encoding_value32, (event_field_format_boolean), _tlgNdt(TraceLoggingBool, value, ##__VA_ARGS__)) +#define TraceLoggingChar(value, ...) _tlgArgValue(char, value, event_field_encoding_value8, (event_field_format_string8), _tlgNdt(TraceLoggingChar, value, ##__VA_ARGS__)) +#define TraceLoggingChar16(value, ...) _tlgArgValue(char16_t, value, event_field_encoding_value16, (event_field_format_string_utf), _tlgNdt(TraceLoggingChar16, value, ##__VA_ARGS__)) +#define TraceLoggingChar32(value, ...) _tlgArgValue(char32_t, value, event_field_encoding_value32, (event_field_format_string_utf), _tlgNdt(TraceLoggingChar32, value, ##__VA_ARGS__)) +#define TraceLoggingWChar(value, ...) _tlgArgValue(wchar_t, value, event_field_encoding_value_wchar,(event_field_format_string_utf),_tlgNdt(TraceLoggingWChar, value, ##__VA_ARGS__)) +#define TraceLoggingPointer(value, ...) _tlgArgValue(void const*, value, event_field_encoding_value_ptr, (event_field_format_hex_int), _tlgNdt(TraceLoggingPointer, value, ##__VA_ARGS__)) +#define TraceLoggingPid(value, ...) _tlgArgValue(int32_t, value, event_field_encoding_value32, (event_field_format_pid), _tlgNdt(TraceLoggingPid, value, ##__VA_ARGS__)) +#define TraceLoggingPort(value, ...) _tlgArgValue(uint16_t, value, event_field_encoding_value16, (event_field_format_port), _tlgNdt(TraceLoggingPort, value, ##__VA_ARGS__)) +#define TraceLoggingErrno(value, ...) _tlgArgValue(int32_t, value, event_field_encoding_value32, (event_field_format_errno), _tlgNdt(TraceLoggingErrno, value, ##__VA_ARGS__)) +#define TraceLoggingTime32(value, ...) _tlgArgValue(int32_t, value, event_field_encoding_value32, (event_field_format_time), _tlgNdt(TraceLoggingTime32, value, ##__VA_ARGS__)) +#define TraceLoggingTime64(value, ...) _tlgArgValue(int64_t, value, event_field_encoding_value64, (event_field_format_time), _tlgNdt(TraceLoggingTime64, value, ##__VA_ARGS__)) + +/* +Wrapper macros for GUID/UUID values in big-endian (RFC 4122) byte order. +Usage: TraceLoggingGuid(pValue, "name", "description", tag). + +The pValue is expected to be a const uint8_t[16] in big-endian byte order to +match the definition of uuid_t in libuuid. + +The name, description, and tag parameters are optional. + +If provided, the name parameter must be a char string literal (not a variable) +and must not contain any ';' or '\0' characters. If the name is not provided, +the value parameter is used to generate a name. Name is treated as utf-8. + +If provided, the description parameter must be a string literal. +Field description has no effect and functions as a comment. + +If provided, the tag parameter must be a 16-bit integer value. + +Examples: +- TraceLoggingGuid(val1) // field name = "val1", description = unset, tag = 0. +- TraceLoggingGuid(val1, "name") // field name = "name", description = unset, tag = 0. +- TraceLoggingGuid(val1, "name", "desc") // field name = "name", description = "desc", tag = 0. +- TraceLoggingGuid(val1, "name", "desc", 0x4) // field name = "name", description = "desc", tag = 0x4. +*/ +#define TraceLoggingGuid(pValue, ...) _tlgArgPackedField(uint8_t, pValue, 16, event_field_encoding_value128, (event_field_format_uuid), _tlgNdt(TraceLoggingGuid, pValue, ##__VA_ARGS__)) + +/* +Wrapper macros for event fields with string values. +Usage: TraceLoggingString(pszVal, "name", "description", tag), where pszVal is const char*. +Usage: TraceLoggingUtf8String(pszVal, "name", "description", tag), where pszVal is const char*. +Usage: TraceLoggingString16(pszVal, "name", "description", tag), where pszVal is const char16_t*. +Usage: TraceLoggingString32(pszVal, "name", "description", tag), where pszVal is const char32_t*. +Usage: TraceLoggingWideString(pszVal, "name", "description", tag), where pszVal is const wchar_t*. +Usage: TraceLoggingCountedString(pchVal, cchVal, "name", "description", tag), where pchVal is const char*. +Usage: TraceLoggingCountedUtf8String(pchVal, cbVal, "name", "description", tag), where pchVal is const char*. +Usage: TraceLoggingCountedString16(pchVal, cchVal, "name", "description", tag), where pchVal is const char16_t*. +Usage: TraceLoggingCountedString32(pchVal, cchVal, "name", "description", tag), where pchVal is const char32_t*. +Usage: TraceLoggingCountedWideString(pchVal, cchVal, "name", "description", tag), where pchVal is const wchar_t*. + +The name, description, and tag parameters are optional. + +For TraceLoggingString, TraceLoggingUtf8String, TraceLoggingString16, +TraceLoggingString32, and TraceLoggingWideString, the pszValue parameter is +treated as a nul-terminated string. If pszValue is NULL, it is treated as an +empty (zero-length) string. + +For TraceLoggingCountedString, TraceLoggingCountedUtf8String, +TraceLoggingCountedString16, TraceLoggingCountedString32, and +TraceLoggingCountedWideString, the pchValue parameter is treated as a counted +string, with cchValue specifying an array element count (0 to 65535). +The pchValue parameter may be NULL only if cchValue is 0. + +The name, description, and tag parameters are optional. + +If provided, the name parameter must be a char string literal (not a variable) +and must not contain any ';' or '\0' characters. If the name is not provided, +the value parameter is used to generate a name. Name is treated as utf-8. + +If provided, the description parameter must be a string literal. +Field description has no effect and functions as a comment. + +If provided, the tag parameter must be a 16-bit integer value. + +Notes: +- TraceLoggingString and TraceLoggingCountedString use unspecified charset but + are usually treated as latin1 (ISO-8859-1) or CP-1252 text. +- The other macros expect UTF-8, UTF-16 or UTF-32 data. + +Examples: +- TraceLoggingString(psz1) // field name = "psz1", description = unset, tag = 0. +- TraceLoggingString(psz1, "name") // field name = "name", description = unset, tag = 0. +- TraceLoggingString(psz1, "name", "desc") // field name = "name", description = "desc", tag = 0. +- TraceLoggingString(psz1, "name", "desc", 0x4) // field name = "name", description = "desc", tag = 0x4. +*/ +#define TraceLoggingString(pszValue, ...) _tlgArgStrNul(char, pszValue, event_field_encoding_zstring_char8, (event_field_format_string8),_tlgNdt(TraceLoggingString, pszValue, ##__VA_ARGS__)) +#define TraceLoggingUtf8String(pszValue, ...) _tlgArgStrNul(char, pszValue, event_field_encoding_zstring_char8, (), _tlgNdt(TraceLoggingUtf8String, pszValue, ##__VA_ARGS__)) +#define TraceLoggingWideString(pszValue, ...) _tlgArgStrNul(wchar_t, pszValue, event_field_encoding_zstring_wchar, (), _tlgNdt(TraceLoggingWideString, pszValue, ##__VA_ARGS__)) +#define TraceLoggingString16(pszValue, ...) _tlgArgStrNul(char16_t, pszValue, event_field_encoding_zstring_char16, (), _tlgNdt(TraceLoggingString16, pszValue, ##__VA_ARGS__)) +#define TraceLoggingString32(pszValue, ...) _tlgArgStrNul(char32_t, pszValue, event_field_encoding_zstring_char32, (), _tlgNdt(TraceLoggingString32, pszValue, ##__VA_ARGS__)) +#define TraceLoggingCountedString(pchValue, cchValue, ...) _tlgArgStrCch(char, pchValue, cchValue, event_field_encoding_string_length16_char8, (event_field_format_string8),_tlgNdt(TraceLoggingCountedString, pchValue, ##__VA_ARGS__)) +#define TraceLoggingCountedUtf8String(pchValue, cbValue, ...) _tlgArgStrCch(char, pchValue, cbValue, event_field_encoding_string_length16_char8, (), _tlgNdt(TraceLoggingCountedUtf8String, pchValue, ##__VA_ARGS__)) +#define TraceLoggingCountedWideString(pchValue, cchValue, ...) _tlgArgStrCch(wchar_t, pchValue, cchValue, event_field_encoding_string_length16_wchar, (), _tlgNdt(TraceLoggingCountedWideString, pchValue, ##__VA_ARGS__)) +#define TraceLoggingCountedString16(pchValue, cchValue, ...) _tlgArgStrCch(char16_t, pchValue, cchValue, event_field_encoding_string_length16_char16,(), _tlgNdt(TraceLoggingCountedString16, pchValue, ##__VA_ARGS__)) +#define TraceLoggingCountedString32(pchValue, cchValue, ...) _tlgArgStrCch(char32_t, pchValue, cchValue, event_field_encoding_string_length16_char32,(), _tlgNdt(TraceLoggingCountedString32, pchValue, ##__VA_ARGS__)) + +/* +Wrapper macro for raw binary data. +Usage: TraceLoggingBinary(pValue, cbValue, "name", "description", tag). +Usage: TraceLoggingBinaryEx(pValue, cbValue, format, "name", "description", tag). + +Use TraceLoggingBinary for normal binary data (event_field_format_hex_bytes). +Use TraceLoggingBinaryEx to specify a custom format. + +The pValue parameter is treated as a const void* so that any kind of data can +be provided. The cbValue parameter is the data size in bytes (0 to 65535). + +The name, description, and tag parameters are optional. + +If provided, the name parameter must be a char string literal (not a variable) +and must not contain any ';' or '\0' characters. If the name is not provided, +the value parameter is used to generate a name. Name is treated as utf-8. + +If provided, the description parameter must be a string literal. +Field description has no effect and functions as a comment. + +If provided, the tag parameter must be a 16-bit integer value. + +Examples: +- TraceLoggingBinary(pObj, sizeof(*pObj)) // field name = "pObj", description = unset, tag = 0. +- TraceLoggingBinary(pObj, sizeof(*pObj), "name") // field name = "name", description = unset, tag = 0. +- TraceLoggingBinary(pObj, sizeof(*pObj), "name", "desc") // field name = "name", description = "desc", tag = 0. +- TraceLoggingBinary(pObj, sizeof(*pObj), "name", "desc", 0x4) // field name = "name", description = "desc", tag = 0x4. +*/ +#define TraceLoggingBinary(pValue, cbValue, ...) _tlgArgBin(void, pValue, cbValue, event_field_encoding_string_length16_char8, (event_field_format_hex_bytes), _tlgNdt(TraceLoggingBinary, pValue, ##__VA_ARGS__)) +#define TraceLoggingBinaryEx(pValue, cbValue, format, ...) _tlgArgBin(void, pValue, cbValue, event_field_encoding_string_length16_char8, (format), _tlgNdt(TraceLoggingBinaryEx, pValue, ##__VA_ARGS__)) + +/* +Wrapper macro for event fields with IPv4 address values. +Usage: TraceLoggingIPv4Address(value, "name", "description", tag). + +The value parameter must be a UINT32-encoded IPv4 address in +network byte order (e.g. pSock->sin_addr.s_addr). + +The name, description, and tag parameters are optional. + +If provided, the name parameter must be a char string literal (not a variable) +and must not contain any ';' or '\0' characters. If the name is not provided, +the value parameter is used to generate a name. Name is treated as utf-8. + +If provided, the description parameter must be a string literal. +Field description has no effect and functions as a comment. + +If provided, the tag parameter must be a 16-bit integer value. + +Example: +- TraceLoggingIPv4Address(pSockAddr->sin_addr.s_addr, "name"). +*/ +#define TraceLoggingIPv4Address(value, ...) _tlgArgValue(uint32_t, value, event_field_encoding_value32, (event_field_format_ipv4), _tlgNdt(TraceLoggingIPv4Address, value, ##__VA_ARGS__)) + +/* +Wrapper macro for event fields with IPv6 address values. +Usage: TraceLoggingIPv6Address(pValue, "name", "description", tag). + +The pValue parameter must not be NULL and must point at a 16-byte buffer +(e.g. use &pSock->sin6_addr). + +The name, description, and tag parameters are optional. + +If provided, the name parameter must be a char string literal (not a variable) +and must not contain any ';' or '\0' characters. If the name is not provided, +the value parameter is used to generate a name. Name is treated as utf-8. + +If provided, the description parameter must be a string literal. +Field description has no effect and functions as a comment. + +If provided, the tag parameter must be a 16-bit integer value. + +Example: +- TraceLoggingIPv6Address(&pSockAddr->sin6_addr, "name"). +*/ +#define TraceLoggingIPv6Address(pValue, ...) _tlgArgPackedField(void, pValue, 16, event_field_encoding_value128, (event_field_format_ipv6), _tlgNdt(TraceLoggingIPv6Address, pValue, ##__VA_ARGS__)) + +/* +Wrapper macros for event fields with values that are fixed-length arrays. +Usage: TraceLoggingInt32FixedArray(pVals, cVals, "name", "description", tag). + +The pVals parameter must be a pointer to cVals items of the specified type. + +The cVals parameter must be a compile-time constant element count 1..65535. + +The name, description, and tag parameters are optional. + +If provided, the name parameter must be a char string literal (not a variable) +and must not contain any ';' or '\0' characters. If the name is not provided, +the value parameter is used to generate a name. Name is treated as utf-8. + +If provided, the description parameter must be a string literal. +Field description has no effect and functions as a comment. + +If provided, the tag parameter must be a 16-bit integer value. + +Examples: +- TraceLoggingUInt8FixedArray(pbX1, 32) // field name = "pbX1", description = unset, tag = 0. +- TraceLoggingUInt8FixedArray(pbX1, 32, "name") // field name = "name", description = unset, tag = 0. +- TraceLoggingUInt8FixedArray(pbX1, 32, "name", "desc") // field name = "name", description = "desc", tag = 0. +- TraceLoggingUInt8FixedArray(pbX1, 32, "name", "desc", 0x4) // field name = "name", description = "desc", tag = 0x4. +*/ +#define TraceLoggingInt8FixedArray(pValues, cValues, ...) _tlgArgCArray(int8_t, pValues, cValues, event_field_encoding_value8, (event_field_format_signed_int), _tlgNdt(TraceLoggingInt8FixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingUInt8FixedArray(pValues, cValues, ...) _tlgArgCArray(uint8_t, pValues, cValues, event_field_encoding_value8, (), _tlgNdt(TraceLoggingUInt8FixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingInt16FixedArray(pValues, cValues, ...) _tlgArgCArray(int16_t, pValues, cValues, event_field_encoding_value16, (event_field_format_signed_int), _tlgNdt(TraceLoggingInt16FixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingUInt16FixedArray(pValues, cValues, ...) _tlgArgCArray(uint16_t, pValues, cValues, event_field_encoding_value16, (), _tlgNdt(TraceLoggingUInt16FixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingInt32FixedArray(pValues, cValues, ...) _tlgArgCArray(int32_t, pValues, cValues, event_field_encoding_value32, (event_field_format_signed_int), _tlgNdt(TraceLoggingInt32FixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingUInt32FixedArray(pValues, cValues, ...) _tlgArgCArray(uint32_t, pValues, cValues, event_field_encoding_value32, (), _tlgNdt(TraceLoggingUInt32FixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingInt64FixedArray(pValues, cValues, ...) _tlgArgCArray(int64_t, pValues, cValues, event_field_encoding_value64, (event_field_format_signed_int), _tlgNdt(TraceLoggingInt64FixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingUInt64FixedArray(pValues, cValues, ...) _tlgArgCArray(uint64_t, pValues, cValues, event_field_encoding_value64, (), _tlgNdt(TraceLoggingUInt64FixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingIntPtrFixedArray(pValues, cValues, ...) _tlgArgCArray(intptr_t, pValues, cValues, event_field_encoding_value_ptr,(event_field_format_signed_int), _tlgNdt(TraceLoggingIntPtrFixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingUIntPtrFixedArray(pValues, cValues, ...) _tlgArgCArray(uintptr_t, pValues, cValues, event_field_encoding_value_ptr,(), _tlgNdt(TraceLoggingUIntPtrFixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingLongFixedArray(pValues, cValues, ...) _tlgArgCArray(signed long, pValues, cValues, event_field_encoding_value_long,(event_field_format_signed_int),_tlgNdt(TraceLoggingLongFixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingULongFixedArray(pValues, cValues, ...) _tlgArgCArray(unsigned long,pValues,cValues, event_field_encoding_value_long,(), _tlgNdt(TraceLoggingULongFixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingHexInt8FixedArray(pValues, cValues, ...) _tlgArgCArray(int8_t, pValues, cValues, event_field_encoding_value8, (event_field_format_hex_int), _tlgNdt(TraceLoggingHexInt8FixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingHexUInt8FixedArray(pValues, cValues, ...) _tlgArgCArray(uint8_t, pValues, cValues, event_field_encoding_value8, (event_field_format_hex_int), _tlgNdt(TraceLoggingHexUInt8FixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingHexInt16FixedArray(pValues, cValues, ...) _tlgArgCArray(int16_t, pValues, cValues, event_field_encoding_value16, (event_field_format_hex_int), _tlgNdt(TraceLoggingHexInt16FixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingHexUInt16FixedArray(pValues, cValues, ...) _tlgArgCArray(uint16_t, pValues, cValues, event_field_encoding_value16, (event_field_format_hex_int), _tlgNdt(TraceLoggingHexUInt16FixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingHexInt32FixedArray(pValues, cValues, ...) _tlgArgCArray(int32_t, pValues, cValues, event_field_encoding_value32, (event_field_format_hex_int), _tlgNdt(TraceLoggingHexInt32FixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingHexUInt32FixedArray(pValues, cValues, ...) _tlgArgCArray(uint32_t, pValues, cValues, event_field_encoding_value32, (event_field_format_hex_int), _tlgNdt(TraceLoggingHexUInt32FixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingHexInt64FixedArray(pValues, cValues, ...) _tlgArgCArray(int64_t, pValues, cValues, event_field_encoding_value64, (event_field_format_hex_int), _tlgNdt(TraceLoggingHexInt64FixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingHexUInt64FixedArray(pValues, cValues, ...) _tlgArgCArray(uint64_t, pValues, cValues, event_field_encoding_value64, (event_field_format_hex_int), _tlgNdt(TraceLoggingHexUInt64FixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingHexIntPtrFixedArray(pValues, cValues, ...) _tlgArgCArray(intptr_t, pValues, cValues, event_field_encoding_value_ptr, (event_field_format_hex_int), _tlgNdt(TraceLoggingHexIntPtrFixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingHexUIntPtrFixedArray(pValues, cValues, ...) _tlgArgCArray(uintptr_t, pValues, cValues, event_field_encoding_value_ptr, (event_field_format_hex_int), _tlgNdt(TraceLoggingHexUIntPtrFixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingHexLongFixedArray(pValues, cValues, ...) _tlgArgCArray(signed long, pValues, cValues, event_field_encoding_value_long,(event_field_format_hex_int), _tlgNdt(TraceLoggingHexLongFixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingHexULongFixedArray(pValues, cValues, ...) _tlgArgCArray(unsigned long,pValues,cValues, event_field_encoding_value_long,(event_field_format_hex_int), _tlgNdt(TraceLoggingHexULongFixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingFloat32FixedArray(pValues, cValues, ...) _tlgArgCArray(float, pValues, cValues, event_field_encoding_value_float,(event_field_format_float), _tlgNdt(TraceLoggingFloat32FixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingFloat64FixedArray(pValues, cValues, ...) _tlgArgCArray(double, pValues, cValues, event_field_encoding_value_double,(event_field_format_float), _tlgNdt(TraceLoggingFloat64FixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingBooleanFixedArray(pValues, cValues, ...) _tlgArgCArray(uint8_t, pValues, cValues, event_field_encoding_value8, (event_field_format_boolean), _tlgNdt(TraceLoggingBooleanFixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingBoolFixedArray(pValues, cValues, ...) _tlgArgCArray(int32_t, pValues, cValues, event_field_encoding_value32, (event_field_format_boolean), _tlgNdt(TraceLoggingBoolFixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingCharFixedArray(pValues, cValues, ...) _tlgArgCArray(char, pValues, cValues, event_field_encoding_value8, (event_field_format_string8), _tlgNdt(TraceLoggingCharFixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingChar16FixedArray(pValues, cValues, ...) _tlgArgCArray(char16_t, pValues, cValues, event_field_encoding_value16, (event_field_format_string_utf), _tlgNdt(TraceLoggingChar16FixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingChar32FixedArray(pValues, cValues, ...) _tlgArgCArray(char32_t, pValues, cValues, event_field_encoding_value32, (event_field_format_string_utf), _tlgNdt(TraceLoggingChar32FixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingWCharFixedArray(pValues, cValues, ...) _tlgArgCArray(wchar_t, pValues, cValues, event_field_encoding_value_wchar,(event_field_format_string_utf),_tlgNdt(TraceLoggingWCharFixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingPointerFixedArray(pValues, cValues, ...) _tlgArgCArray(void const*, pValues, cValues, event_field_encoding_value_ptr, (event_field_format_hex_int), _tlgNdt(TraceLoggingPointerFixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingPidFixedArray(pValues, cValues, ...) _tlgArgCArray(int32_t, pValues, cValues, event_field_encoding_value32, (event_field_format_pid), _tlgNdt(TraceLoggingPidFixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingPortFixedArray(pValues, cValues, ...) _tlgArgCArray(uint16_t, pValues, cValues, event_field_encoding_value16, (event_field_format_port), _tlgNdt(TraceLoggingPortFixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingErrnoFixedArray(pValues, cValues, ...) _tlgArgCArray(int32_t, pValues, cValues, event_field_encoding_value32, (event_field_format_errno), _tlgNdt(TraceLoggingErrnoFixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingTime32FixedArray(pValues, cValues, ...) _tlgArgCArray(int32_t, pValues, cValues, event_field_encoding_value32, (event_field_format_time), _tlgNdt(TraceLoggingTime32FixedArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingTime64FixedArray(pValues, cValues, ...) _tlgArgCArray(int64_t, pValues, cValues, event_field_encoding_value64, (event_field_format_time), _tlgNdt(TraceLoggingTime64FixedArray, pValues, ##__VA_ARGS__)) + +/* +Wrapper macros for event fields with values that are variable-length arrays. +Usage: TraceLoggingInt32Array(pVals, cVals, "name", "description", tag). + +The pVals parameter must be a pointer to cVals items of the specified type. + +The cVals parameter must be an element count 0..65535. + +The name, description, and tag parameters are optional. + +If provided, the name parameter must be a char string literal (not a variable) +and must not contain any ';' or '\0' characters. If the name is not provided, +the value parameter is used to generate a name. Name is treated as utf-8. + +If provided, the description parameter must be a string literal. +Field description has no effect and functions as a comment. + +If provided, the tag parameter must be a 16-bit integer value. + +Examples: +- TraceLoggingUInt8Array(pbX1, cbX1) // field name = "pbX1", description = unset, tag = 0. +- TraceLoggingUInt8Array(pbX1, cbX1, "name") // field name = "name", description = unset, tag = 0. +- TraceLoggingUInt8Array(pbX1, cbX1, "name", "desc") // field name = "name", description = "desc", tag = 0. +- TraceLoggingUInt8Array(pbX1, cbX1, "name", "desc", 0x4) // field name = "name", description = "desc", tag = 0x4. +*/ +#define TraceLoggingInt8Array(pValues, cValues, ...) _tlgArgVArray(int8_t, pValues, cValues, event_field_encoding_value8, (event_field_format_signed_int), _tlgNdt(TraceLoggingInt8Array, pValues, ##__VA_ARGS__)) +#define TraceLoggingUInt8Array(pValues, cValues, ...) _tlgArgVArray(uint8_t, pValues, cValues, event_field_encoding_value8, (), _tlgNdt(TraceLoggingUInt8Array, pValues, ##__VA_ARGS__)) +#define TraceLoggingInt16Array(pValues, cValues, ...) _tlgArgVArray(int16_t, pValues, cValues, event_field_encoding_value16, (event_field_format_signed_int), _tlgNdt(TraceLoggingInt16Array, pValues, ##__VA_ARGS__)) +#define TraceLoggingUInt16Array(pValues, cValues, ...) _tlgArgVArray(uint16_t, pValues, cValues, event_field_encoding_value16, (), _tlgNdt(TraceLoggingUInt16Array, pValues, ##__VA_ARGS__)) +#define TraceLoggingInt32Array(pValues, cValues, ...) _tlgArgVArray(int32_t, pValues, cValues, event_field_encoding_value32, (event_field_format_signed_int), _tlgNdt(TraceLoggingInt32Array, pValues, ##__VA_ARGS__)) +#define TraceLoggingUInt32Array(pValues, cValues, ...) _tlgArgVArray(uint32_t, pValues, cValues, event_field_encoding_value32, (), _tlgNdt(TraceLoggingUInt32Array, pValues, ##__VA_ARGS__)) +#define TraceLoggingInt64Array(pValues, cValues, ...) _tlgArgVArray(int64_t, pValues, cValues, event_field_encoding_value64, (event_field_format_signed_int), _tlgNdt(TraceLoggingInt64Array, pValues, ##__VA_ARGS__)) +#define TraceLoggingUInt64Array(pValues, cValues, ...) _tlgArgVArray(uint64_t, pValues, cValues, event_field_encoding_value64, (), _tlgNdt(TraceLoggingUInt64Array, pValues, ##__VA_ARGS__)) +#define TraceLoggingIntPtrArray(pValues, cValues, ...) _tlgArgVArray(intptr_t, pValues, cValues, event_field_encoding_value_ptr,(event_field_format_signed_int), _tlgNdt(TraceLoggingIntPtrArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingUIntPtrArray(pValues, cValues, ...) _tlgArgVArray(uintptr_t, pValues, cValues, event_field_encoding_value_ptr,(), _tlgNdt(TraceLoggingUIntPtrArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingLongArray(pValues, cValues, ...) _tlgArgVArray(signed long, pValues, cValues, event_field_encoding_value_long,(event_field_format_signed_int),_tlgNdt(TraceLoggingLongArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingULongArray(pValues, cValues, ...) _tlgArgVArray(unsigned long,pValues,cValues, event_field_encoding_value_long,(), _tlgNdt(TraceLoggingULongArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingHexInt8Array(pValues, cValues, ...) _tlgArgVArray(int8_t, pValues, cValues, event_field_encoding_value8, (event_field_format_hex_int), _tlgNdt(TraceLoggingHexInt8Array, pValues, ##__VA_ARGS__)) +#define TraceLoggingHexUInt8Array(pValues, cValues, ...) _tlgArgVArray(uint8_t, pValues, cValues, event_field_encoding_value8, (event_field_format_hex_int), _tlgNdt(TraceLoggingHexUInt8Array, pValues, ##__VA_ARGS__)) +#define TraceLoggingHexInt16Array(pValues, cValues, ...) _tlgArgVArray(int16_t, pValues, cValues, event_field_encoding_value16, (event_field_format_hex_int), _tlgNdt(TraceLoggingHexInt16Array, pValues, ##__VA_ARGS__)) +#define TraceLoggingHexUInt16Array(pValues, cValues, ...) _tlgArgVArray(uint16_t, pValues, cValues, event_field_encoding_value16, (event_field_format_hex_int), _tlgNdt(TraceLoggingHexUInt16Array, pValues, ##__VA_ARGS__)) +#define TraceLoggingHexInt32Array(pValues, cValues, ...) _tlgArgVArray(int32_t, pValues, cValues, event_field_encoding_value32, (event_field_format_hex_int), _tlgNdt(TraceLoggingHexInt32Array, pValues, ##__VA_ARGS__)) +#define TraceLoggingHexUInt32Array(pValues, cValues, ...) _tlgArgVArray(uint32_t, pValues, cValues, event_field_encoding_value32, (event_field_format_hex_int), _tlgNdt(TraceLoggingHexUInt32Array, pValues, ##__VA_ARGS__)) +#define TraceLoggingHexInt64Array(pValues, cValues, ...) _tlgArgVArray(int64_t, pValues, cValues, event_field_encoding_value64, (event_field_format_hex_int), _tlgNdt(TraceLoggingHexInt64Array, pValues, ##__VA_ARGS__)) +#define TraceLoggingHexUInt64Array(pValues, cValues, ...) _tlgArgVArray(uint64_t, pValues, cValues, event_field_encoding_value64, (event_field_format_hex_int), _tlgNdt(TraceLoggingHexUInt64Array, pValues, ##__VA_ARGS__)) +#define TraceLoggingHexIntPtrArray(pValues, cValues, ...) _tlgArgVArray(intptr_t, pValues, cValues, event_field_encoding_value_ptr,(event_field_format_hex_int), _tlgNdt(TraceLoggingHexIntPtrArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingHexUIntPtrArray(pValues, cValues, ...) _tlgArgVArray(uintptr_t, pValues, cValues, event_field_encoding_value_ptr,(event_field_format_hex_int), _tlgNdt(TraceLoggingHexUIntPtrArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingHexLongArray(pValues, cValues, ...) _tlgArgVArray(signed long, pValues, cValues, event_field_encoding_value_long,(event_field_format_hex_int), _tlgNdt(TraceLoggingHexLongArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingHexULongArray(pValues, cValues, ...) _tlgArgVArray(unsigned long,pValues,cValues, event_field_encoding_value_long,(event_field_format_hex_int), _tlgNdt(TraceLoggingHexULongArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingFloat32Array(pValues, cValues, ...) _tlgArgVArray(float, pValues, cValues, event_field_encoding_value_float,(event_field_format_float), _tlgNdt(TraceLoggingFloat32Array, pValues, ##__VA_ARGS__)) +#define TraceLoggingFloat64Array(pValues, cValues, ...) _tlgArgVArray(double, pValues, cValues, event_field_encoding_value_double,(event_field_format_float), _tlgNdt(TraceLoggingFloat64Array, pValues, ##__VA_ARGS__)) +#define TraceLoggingBooleanArray(pValues, cValues, ...) _tlgArgVArray(uint8_t, pValues, cValues, event_field_encoding_value8, (event_field_format_boolean), _tlgNdt(TraceLoggingBooleanArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingBoolArray(pValues, cValues, ...) _tlgArgVArray(int32_t, pValues, cValues, event_field_encoding_value32, (event_field_format_boolean), _tlgNdt(TraceLoggingBoolArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingCharArray(pValues, cValues, ...) _tlgArgVArray(char, pValues, cValues, event_field_encoding_value8, (event_field_format_string8), _tlgNdt(TraceLoggingCharArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingChar16Array(pValues, cValues, ...) _tlgArgVArray(char16_t, pValues, cValues, event_field_encoding_value16, (event_field_format_string_utf), _tlgNdt(TraceLoggingChar16Array, pValues, ##__VA_ARGS__)) +#define TraceLoggingChar32Array(pValues, cValues, ...) _tlgArgVArray(char32_t, pValues, cValues, event_field_encoding_value32, (event_field_format_string_utf), _tlgNdt(TraceLoggingChar32Array, pValues, ##__VA_ARGS__)) +#define TraceLoggingWCharArray(pValues, cValues, ...) _tlgArgVArray(wchar_t, pValues, cValues, event_field_encoding_value_wchar,(event_field_format_string_utf),_tlgNdt(TraceLoggingWCharArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingPointerArray(pValues, cValues, ...) _tlgArgVArray(void const*, pValues, cValues, event_field_encoding_value_ptr,(event_field_format_hex_int), _tlgNdt(TraceLoggingPointerArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingPidArray(pValues, cValues, ...) _tlgArgVArray(int32_t, pValues, cValues, event_field_encoding_value32, (event_field_format_pid), _tlgNdt(TraceLoggingPidArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingPortArray(pValues, cValues, ...) _tlgArgVArray(uint16_t, pValues, cValues, event_field_encoding_value16, (event_field_format_port), _tlgNdt(TraceLoggingPortArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingErrnoArray(pValues, cValues, ...) _tlgArgVArray(int32_t, pValues, cValues, event_field_encoding_value32, (event_field_format_errno), _tlgNdt(TraceLoggingErrnoArray, pValues, ##__VA_ARGS__)) +#define TraceLoggingTime32Array(pValues, cValues, ...) _tlgArgVArray(int32_t, pValues, cValues, event_field_encoding_value32, (event_field_format_time), _tlgNdt(TraceLoggingTime32Array, pValues, ##__VA_ARGS__)) +#define TraceLoggingTime64Array(pValues, cValues, ...) _tlgArgVArray(int64_t, pValues, cValues, event_field_encoding_value64, (event_field_format_time), _tlgNdt(TraceLoggingTime64Array, pValues, ##__VA_ARGS__)) + +/* +Wrapper macros for manually-packed fields (advanced scenarios). +These macros support custom serialization of fields for use in creating events +that would otherwise be inexpressible through TraceLoggingProvider.h. For +example, these macros can be used to write fields containing arrays of strings +or arrays of structures. That the correct use of these macros requires an +understanding of how TraceLogging encodes events. If used incorrectly, these +macros will generate events that do not decode correctly. Note that to write +arrays of strings or arrays of structures, you will usually need to do +additional work such as manually marshaling the data into a buffer before +invoking TraceLoggingWrite. + +TraceLoggingPackedField(pValue, cbValue, encoding, "name", "description", tag) +TraceLoggingPackedFieldEx(pValue, cbValue, encoding, format, "name", "description", tag) +TraceLoggingPackedMetadata(encoding, "name", "description", tag) +TraceLoggingPackedMetadataEx(encoding, format, "name", "description", tag) +TraceLoggingPackedStruct(fieldCount, "name", "description", tag) +TraceLoggingPackedStructArray(fieldCount, "name", "description", tag) +TraceLoggingPackedData(pValue, cbValue) + +The name parameter must be a char string literal (not a variable) and must not +contain any ';' or '\0' characters. Name is treated as utf-8. For +TraceLoggingPackedField and TraceLoggingPackedFieldEx, the name parameter is +optional. If the name is not provided, the TraceLoggingPackedField and +TraceLoggingPackedFieldEx macros will use the pValue parameter to automatically +generate a field name. + +If provided, the description parameter must be a string literal. +Field description has no effect and functions as a comment. + +If provided, the tag parameter must be a 16-bit integer value. + +A TraceLogging event contains metadata and data. The metadata is the list of +fields, each with a name and a type. The data is the payload - an array of +raw bytes that contains the values of the event fields. The metadata is +composed of compile-time-constant data, while the data can be different each +time the event is generated. The metadata is used to decode the data, so the +metadata and the data need to be coordinated. The other wrapper macros +(TraceLoggingInt32, TraceLoggingString, etc.) automatically keep the metadata +and data coordinated, but the TraceLoggingPacked macros allow direct control +over the metadata and data so incorrect use of them can result in events that +do not decode correctly. + +The TraceLoggingPackedField macro adds both metadata and data. It adds an +arbitrary field to the event's type and adds arbitrary data to the event's +payload, with field format set to Default. The TraceLoggingPackedFieldEx macro +does the same, but includes a byte for the field's format in the field +descriptor so that a non-default format can be specified. + +The TraceLoggingPackedMetadata macro adds only metadata. It adds a field to the +event's type without adding any data to the event's payload, with field format +set to Default. The TraceLoggingPackedMetadataEx macro does the same, but +includes a byte for the field's format in the field descriptor so that a +non-default format can be specified. + +The TraceLoggingPackedStruct macro adds only metadata (a struct declaration +never contains data -- the struct's data is provided by its fields). It begins +a structure in the event. The logical fields that follow the start +of the structure are considered to be part of the structure, and they will form +one logical field. (Structures can nest, and a nested structure counts as one +logical field in the parent structure.) The TraceLoggingPackedStructArray does +the same, but it begins an array of structures (which also counts as one +logical field). + +The TraceLoggingPackedData macro adds data directly into the event payload +without adding a field to the event's type. + +These macros can be combined in various ways to express TraceLogging field +structures not otherwise possible. Possible scenarios include: + +* Write a simple field with a specific encoding/format combination that is + not supported by the core TraceLogging macros. + + For example, to write a nul-terminated wide string that is tagged as + containing a JSON string: + + TraceLoggingWrite( + g_hProvider, + "MyEventWithJsonData", + TraceLoggingInt32(otherData1), + TraceLoggingPackedFieldEx( + szJson, + (wcslen(szJson) + 1) * sizeof(wchar_t), + event_field_encoding_zstring_charWide, + event_field_format_string_json, + "MyJsonFieldName"), + TraceLoggingInt32(otherData2)); + +* Write a complex field that requires marshalling data into a temporary + buffer. + + For example, to write an array of nul-terminated ANSI strings: + + // This scenario requires manually marshaling data. + // Don't spend time marshaling data if the event is disabled. + if (TraceLoggingProviderEnabled(g_hProvider, myevent_level, myEventKeyword)) + { + // This example assumes that the strings will fit into 100 bytes. + // Your production code will need to do additional error checking, or + // perhaps use std::vector and do a buf.push_back(val) instead of + // buf[iBuf++] = val. + uint8_t buf[100]; + unsigned iBuf = 0; + + // Packed arrays start with a uint16_t value indicating the number of + // elements in the array. + buf[iBuf++] = (uint8_t)cStrings; // Low byte of the element count (assuming little-endian) + buf[iBuf++] = (uint8_t)(cStrings >> 8); // High byte of the element count + + // Then we need to add the content of each array element. + for (UINT i = 0; i != cStrings; i++) + { + for (LPCSTR pString = pStrings[i]; *pString != 0; pString++) + { + buf[iBuf++] = *pString; + } + buf[iBuf++] = 0; // nul-terminate + } + + TraceLoggingWrite( + g_hProvider, + "MyEventWithArrayOfStrings", + TraceLoggingLevel(myevent_level), + TraceLoggingKeyword(myEventKeyword), + TraceLoggingInt32(otherData1), + TraceLoggingPackedField( + buf, + iBuf, + event_field_encoding_zstring_char8 | event_field_encoding_varray_flag, + "MyArrayOfStringsFieldName"), + TraceLoggingInt32(otherData2)); + } + +* Write a structure directly as a single entity instead of as a series of + fields. + + This can be a minor performance optimization in some cases (it can reduce + per-event CPU and reduce stack usage), since it reduces the number of + iovec structures that need to be created and initialized when generating + the event. Note that structures can only be written directly if the structure + contains no internal padding or non-blittable fields. If the structure + contains padding or non-blittable fields, you would need to buffer and repack + the data before using this technique, in which case it would have been more + efficient to use the normal methods for logging structures (i.e. using a + normal TraceLoggingStruct followed by the appropriate TraceLoggingValue for + each field). + + Overview: provide the data for the struct using TraceLoggingPackedData; + provide the number of fields and the name of the structure with + TraceLoggingPackedStruct; provide the names and types of the fields using + TraceLoggingPackedMetadata. + + Note that while the order of metadata is important and the ordering of data is + important, the ordering between metadata and data is not important. In the + example below, the TraceLoggingPackedData macro could appear anywhere between + otherData1 and otherData2 without changing the result. However, it could not + appear before otherData1 or after otherData2, since each of those also emit + data, and the data from TraceLoggingPackedData must appear after otherData1 + and before otherData2. + + TraceLoggingWrite( + g_hProvider, + "MyEventWithRect", + TraceLoggingInt32(otherData1), + TraceLoggingPackedData(&rect, sizeof(RECT)), // Data for all 4 fields + TraceLoggingPackedStruct(4, "RectangleFieldName"), // Metadata: Structure with 4 fields + TraceLoggingPackedMetadata(event_field_encoding_value32, "left"), + TraceLoggingPackedMetadata(event_field_encoding_value32, "top"), + TraceLoggingPackedMetadata(event_field_encoding_value32, "right"), + TraceLoggingPackedMetadata(event_field_encoding_value32, "bottom"), + TraceLoggingInt32(otherData2)); + +* Write an array of structures. + + Overview: Provide the data for the array (the array count and the array + content) using TraceLoggingPackedData; provide the number of fields and the + name of the structure with TraceLoggingPackedStructArray; provide the names + and types of the fields using TraceLoggingPackedMetadata. + + In the example below, the array contains no padding and no non-blittable data + (i.e. no variable-length data, out-of-line data like pointers to strings, + etc.), so we can provide a pointer directly to the array content. If the + array contained padding or contained non-blittable data, you would need to + allocate a buffer and re-pack the data, inlining any non-blittable elements + and omitting any padding. The example below needs to provide the array + element count (uint16_t) as well as the array content, so it uses + TraceLoggingPackedData twice. + + TraceLoggingWrite( + g_hProvider, + "MyEventWithArrayOfRectangles", + TraceLoggingInt32(otherData1), + TraceLoggingPackedData(&cRectangles, sizeof(uint16_t)), // Data for the array count + TraceLoggingPackedData(pRectangles, cRectangles * sizeof(RECT)), // Data for the array content + TraceLoggingPackedStructArray(4, "RectangleArrayFieldName"), // Structure with 4 fields + TraceLoggingPackedMetadata(event_field_encoding_value32, "left"), + TraceLoggingPackedMetadata(event_field_encoding_value32, "top"), + TraceLoggingPackedMetadata(event_field_encoding_value32, "right"), + TraceLoggingPackedMetadata(event_field_encoding_value32, "bottom"), + TraceLoggingInt32(otherData2)); + +Notes on serializing data: + +- When the decoder receives the event, it sees the event payload as a single + block of bytes. It does not see any boundaries between chunks of data in the + payload. If I use TraceLoggingPackedMetadata to add an Int32 field but + provide 5 bytes of data, the decoder will not be able to correctly decode the + remaining fields of the event. The developer must take care that the data + written matches up with the field definitions. On the other hand, this allows + flexibility in the way the data is encoded. For example, I might write the + data for several fields using a single TraceLoggingPackedData macro (more + efficient if the data is already contiguous in memory), or I might use + multiple TraceLoggingPackedData macros to gather bits of a single field's + value from multiple locations in memory (more efficient than recopying the + data to make it contiguous). +- Encoding/decoding behavior only uses the encoding. The format is only a + formatting hint and might be ignored by the decoder. +- Form an array by adding event_field_encoding_varray_flag to the encoding. For + example, an encoding of event_field_encoding_zstring_char8 will result in a + field that stores a single string, but an encoding of + event_field_encoding_zstring_char8|event_field_encoding_varray_flag will result + in a field that stores a uint16_t count followed by a sequence of strings. +- Arrays are serialized as a uint16_t element-count followed by the elements. + The elements in an array are serialized exactly as if they were not in an + array, even if the element has a variable length. For example, on a + little-endian system, the payload corrsponding to the 3-element array + { "ABC", "DE", "F" } would be: + uint8_t a[] = { '\3', '\0', 'A', 'B', 'C', '\0', 'D', 'E', '\0', 'F', '\0' }; +*/ +#define TraceLoggingPackedField(pValue, cbValue, encoding, ...) _tlgArgPackedField(void, pValue, cbValue, encoding, (), _tlgNdt(TraceLoggingPackedField, pValue, ##__VA_ARGS__)) +#define TraceLoggingPackedFieldEx(pValue, cbValue, encoding, format, ...) _tlgArgPackedField(void, pValue, cbValue, encoding, (format), _tlgNdt(TraceLoggingPackedFieldEx, pValue, ##__VA_ARGS__)) +#define TraceLoggingPackedMetadata(encoding, name, ...) _tlgArgPackedMeta( encoding, (), _tlgNdt(TraceLoggingPackedMetadata, value, name, ##__VA_ARGS__)) +#define TraceLoggingPackedMetadataEx(encoding, format, name, ...) _tlgArgPackedMeta( encoding, (format), _tlgNdt(TraceLoggingPackedMetadataEx, value, name, ##__VA_ARGS__)) +#define TraceLoggingPackedStruct(fieldCount, name, ...) _tlgArgStruct(fieldCount, event_field_encoding_struct, _tlgNdt(TraceLoggingPackedStruct, value, name, ##__VA_ARGS__)) +#define TraceLoggingPackedStructArray(fieldCount, name, ...) _tlgArgStruct(fieldCount, event_field_encoding_struct|event_field_encoding_varray_flag, _tlgNdt(TraceLoggingPackedStructArray, value, name, ##__VA_ARGS__)) +#define TraceLoggingPackedData(pValue, cbValue) _tlgArgPackedData( void, pValue, cbValue) + +#ifdef __EDG__ +#pragma endregion +#endif + +#ifdef __EDG__ +#pragma region Internal_utility macros (for internal use only) +#endif + +#ifndef _tlg_ASSERT +#define _tlg_ASSERT(x) assert(x) +#endif // _tlg_ASSERT + +#ifndef _tlg_NOEXCEPT +#ifdef __cplusplus +#define _tlg_NOEXCEPT noexcept +#else // __cplusplus +#define _tlg_NOEXCEPT +#endif // __cplusplus +#endif // _tlg_NOEXCEPT + +#ifndef _tlg_WEAK_ATTRIBUTES +#define _tlg_WEAK_ATTRIBUTES __attribute__((weak, visibility("hidden"))) +#endif // _tlg_WEAK_ATTRIBUTES + +#ifndef _tlg_INLINE_ATTRIBUTES +#define _tlg_INLINE_ATTRIBUTES +#endif // _tlg_INLINE_ATTRIBUTES + +#ifndef _tlg_NULL +#ifdef __cplusplus +#define _tlg_NULL nullptr +#else // __cplusplus +#define _tlg_NULL NULL +#endif // __cplusplus +#endif // _tlg_NULL + +#ifdef __cplusplus +#define _tlg_EXTERN_C extern "C" +#define _tlg_EXTERN_C_CONST extern "C" const +#else // __cplusplus +#define _tlg_EXTERN_C extern // In C, linkage is already "C". +#define _tlg_EXTERN_C_CONST const // In C, extern with initializer is wrong. +#endif // __cplusplus + +// Internal implementation detail: Not for use outside of TraceLoggingProvider.h. +#define _tlg_PASTE2(a, b) _tlg_PASTE2_imp(a, b) +#define _tlg_PASTE2_imp(a, b) a##b + +// Internal implementation detail: Not for use outside of TraceLoggingProvider.h. +#define _tlg_PASTE3(a, b, c) _tlg_PASTE3_imp(a, b, c) +#define _tlg_PASTE3_imp(a, b, c) a##b##c + +// Internal implementation detail: Not for use outside of TraceLoggingProvider.h. +#define _tlg_FLATTEN(...) __VA_ARGS__ + +// Internal implementation detail: Not for use outside of TraceLoggingProvider.h. +#define _tlg_PARENTHESIZE(...) (__VA_ARGS__) + +// Internal implementation detail: Not for use outside of TraceLoggingProvider.h. +#define _tlg_STRINGIZE(x) _tlg_STRINGIZE_imp(x) +#define _tlg_STRINGIZE_imp(x) #x + +// Internal implementation detail: Not for use outside of TraceLoggingProvider.h. +#define _tlg_CAT(a, ...) _tlg_CAT_imp(a, __VA_ARGS__) +#define _tlg_CAT_imp(a, ...) a##__VA_ARGS__ + +// Internal implementation detail: Not for use outside of TraceLoggingProvider.h. +#define _tlg_SPLIT(cond, ...) _tlg_SPLIT_imp(cond, (__VA_ARGS__)) +#define _tlg_SPLIT_imp(cond, args) _tlg_PASTE2(_tlg_SPLIT_imp, cond) args +#define _tlg_SPLIT_imp0(false_val, ...) false_val +#define _tlg_SPLIT_imp1(false_val, ...) __VA_ARGS__ + +// Internal implementation detail: Not for use outside of TraceLoggingProvider.h. +#define _tlg_IS_PARENTHESIZED(...) \ + _tlg_SPLIT(0, _tlg_CAT(_tlg_IS_PARENTHESIZED_imp, _tlg_IS_PARENTHESIZED_imp0 __VA_ARGS__)) +#define _tlg_IS_PARENTHESIZED_imp_tlg_IS_PARENTHESIZED_imp0 0, +#define _tlg_IS_PARENTHESIZED_imp0(...) 1 +#define _tlg_IS_PARENTHESIZED_imp1 1, + +// Internal implementation detail: Not for use outside of TraceLoggingProvider.h. +#define _tlg_IS_EMPTY(...) _tlg_SPLIT( \ + _tlg_IS_PARENTHESIZED(__VA_ARGS__), \ + _tlg_IS_PARENTHESIZED(_tlg_PARENTHESIZE __VA_ARGS__()), \ + 0) + +// Internal implementation detail: Not for use outside of TraceLoggingProvider.h. +#define _tlg_NARGS(...) _tlg_NARGS_imp(_tlg_IS_EMPTY(__VA_ARGS__), (__VA_ARGS__)) +#define _tlg_NARGS_imp(is_empty, args) _tlg_PASTE2(_tlg_NARGS_imp, is_empty) args +#define _tlg_NARGS_imp0(...) _tlg_PASTE2(_tlg_NARGS_imp2( \ + __VA_ARGS__, \ + 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, \ + 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, \ + 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, \ + 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, \ + 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, \ + 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, \ + 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, \ + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, \ + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, \ + 9, 8, 7, 6, 5, 4, 3, 2, 1, ), ) +#define _tlg_NARGS_imp1() 0 +#define _tlg_NARGS_imp2( \ + a1, a2, a3, a4, a5, a6, a7, a8, a9, \ + a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, \ + a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, \ + a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, \ + a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, \ + a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, \ + a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, \ + a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, \ + a80, a81, a82, a83, a84, a85, a86, a87, a88, a89, \ + a90, a91, a92, a93, a94, a95, a96, a97, a98, a99, \ + size, ...) size + +#ifdef __EDG__ +#pragma endregion +#endif + +#ifdef __EDG__ +#pragma region Internal_foreach macro (for internal use only) +#endif + +// Internal implementation detail: Not for use outside of TraceLoggingProvider.h. +#define _tlg_FOREACH(macro, ...) _tlg_FOR_imp(_tlg_NARGS(__VA_ARGS__), (macro, __VA_ARGS__)) +#define _tlg_FOR_imp(n, macroAndArgs) _tlg_PASTE2(_tlg_FOR_imp, n) macroAndArgs +#define _tlg_FOR_imp0(f, ...) +#define _tlg_FOR_imp1(f, a0) f(0, a0) +#define _tlg_FOR_imp2(f, a0, a1) f(0, a0) f(1, a1) +#define _tlg_FOR_imp3(f, a0, a1, a2) f(0, a0) f(1, a1) f(2, a2) +#define _tlg_FOR_imp4(f, a0, a1, a2, a3) f(0, a0) f(1, a1) f(2, a2) f(3, a3) +#define _tlg_FOR_imp5(f, a0, a1, a2, a3, a4) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) +#define _tlg_FOR_imp6(f, a0, a1, a2, a3, a4, a5) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) +#define _tlg_FOR_imp7(f, a0, a1, a2, a3, a4, a5, a6) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) +#define _tlg_FOR_imp8(f, a0, a1, a2, a3, a4, a5, a6, a7) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) +#define _tlg_FOR_imp9(f, a0, a1, a2, a3, a4, a5, a6, a7, a8) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) +#define _tlg_FOR_imp10(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) +#define _tlg_FOR_imp11(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) +#define _tlg_FOR_imp12(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) +#define _tlg_FOR_imp13(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) +#define _tlg_FOR_imp14(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) +#define _tlg_FOR_imp15(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) +#define _tlg_FOR_imp16(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) +#define _tlg_FOR_imp17(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) +#define _tlg_FOR_imp18(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) +#define _tlg_FOR_imp19(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) +#define _tlg_FOR_imp20(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) +#define _tlg_FOR_imp21(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) +#define _tlg_FOR_imp22(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) +#define _tlg_FOR_imp23(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) +#define _tlg_FOR_imp24(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) +#define _tlg_FOR_imp25(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) +#define _tlg_FOR_imp26(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) +#define _tlg_FOR_imp27(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) +#define _tlg_FOR_imp28(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) +#define _tlg_FOR_imp29(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) +#define _tlg_FOR_imp30(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) +#define _tlg_FOR_imp31(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) +#define _tlg_FOR_imp32(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) +#define _tlg_FOR_imp33(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) +#define _tlg_FOR_imp34(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) +#define _tlg_FOR_imp35(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) +#define _tlg_FOR_imp36(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) +#define _tlg_FOR_imp37(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) +#define _tlg_FOR_imp38(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) +#define _tlg_FOR_imp39(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) +#define _tlg_FOR_imp40(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) +#define _tlg_FOR_imp41(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) +#define _tlg_FOR_imp42(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) +#define _tlg_FOR_imp43(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) +#define _tlg_FOR_imp44(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) +#define _tlg_FOR_imp45(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) +#define _tlg_FOR_imp46(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) +#define _tlg_FOR_imp47(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) +#define _tlg_FOR_imp48(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) +#define _tlg_FOR_imp49(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) +#define _tlg_FOR_imp50(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) +#define _tlg_FOR_imp51(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) +#define _tlg_FOR_imp52(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) +#define _tlg_FOR_imp53(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) +#define _tlg_FOR_imp54(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) +#define _tlg_FOR_imp55(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) +#define _tlg_FOR_imp56(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) +#define _tlg_FOR_imp57(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) +#define _tlg_FOR_imp58(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) +#define _tlg_FOR_imp59(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) +#define _tlg_FOR_imp60(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) +#define _tlg_FOR_imp61(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) +#define _tlg_FOR_imp62(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) +#define _tlg_FOR_imp63(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) +#define _tlg_FOR_imp64(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) +#define _tlg_FOR_imp65(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) +#define _tlg_FOR_imp66(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) +#define _tlg_FOR_imp67(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) +#define _tlg_FOR_imp68(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) +#define _tlg_FOR_imp69(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) +#define _tlg_FOR_imp70(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) +#define _tlg_FOR_imp71(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) +#define _tlg_FOR_imp72(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) +#define _tlg_FOR_imp73(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) +#define _tlg_FOR_imp74(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) +#define _tlg_FOR_imp75(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) +#define _tlg_FOR_imp76(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) +#define _tlg_FOR_imp77(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) +#define _tlg_FOR_imp78(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) +#define _tlg_FOR_imp79(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) +#define _tlg_FOR_imp80(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) +#define _tlg_FOR_imp81(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) +#define _tlg_FOR_imp82(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) +#define _tlg_FOR_imp83(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) +#define _tlg_FOR_imp84(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) +#define _tlg_FOR_imp85(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) +#define _tlg_FOR_imp86(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) f(85, a85) +#define _tlg_FOR_imp87(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) f(85, a85) f(86, a86) +#define _tlg_FOR_imp88(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) f(85, a85) f(86, a86) f(87, a87) +#define _tlg_FOR_imp89(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87, a88) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) f(85, a85) f(86, a86) f(87, a87) f(88, a88) +#define _tlg_FOR_imp90(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87, a88, a89) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) f(85, a85) f(86, a86) f(87, a87) f(88, a88) f(89, a89) +#define _tlg_FOR_imp91(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87, a88, a89, a90) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) f(85, a85) f(86, a86) f(87, a87) f(88, a88) f(89, a89) f(90, a90) +#define _tlg_FOR_imp92(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87, a88, a89, a90, a91) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) f(85, a85) f(86, a86) f(87, a87) f(88, a88) f(89, a89) f(90, a90) f(91, a91) +#define _tlg_FOR_imp93(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87, a88, a89, a90, a91, a92) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) f(85, a85) f(86, a86) f(87, a87) f(88, a88) f(89, a89) f(90, a90) f(91, a91) f(92, a92) +#define _tlg_FOR_imp94(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87, a88, a89, a90, a91, a92, a93) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) f(85, a85) f(86, a86) f(87, a87) f(88, a88) f(89, a89) f(90, a90) f(91, a91) f(92, a92) f(93, a93) +#define _tlg_FOR_imp95(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87, a88, a89, a90, a91, a92, a93, a94) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) f(85, a85) f(86, a86) f(87, a87) f(88, a88) f(89, a89) f(90, a90) f(91, a91) f(92, a92) f(93, a93) f(94, a94) +#define _tlg_FOR_imp96(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87, a88, a89, a90, a91, a92, a93, a94, a95) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) f(85, a85) f(86, a86) f(87, a87) f(88, a88) f(89, a89) f(90, a90) f(91, a91) f(92, a92) f(93, a93) f(94, a94) f(95, a95) +#define _tlg_FOR_imp97(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87, a88, a89, a90, a91, a92, a93, a94, a95, a96) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) f(85, a85) f(86, a86) f(87, a87) f(88, a88) f(89, a89) f(90, a90) f(91, a91) f(92, a92) f(93, a93) f(94, a94) f(95, a95) f(96, a96) +#define _tlg_FOR_imp98(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87, a88, a89, a90, a91, a92, a93, a94, a95, a96, a97) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) f(85, a85) f(86, a86) f(87, a87) f(88, a88) f(89, a89) f(90, a90) f(91, a91) f(92, a92) f(93, a93) f(94, a94) f(95, a95) f(96, a96) f(97, a97) +#define _tlg_FOR_imp99(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87, a88, a89, a90, a91, a92, a93, a94, a95, a96, a97, a98) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) f(85, a85) f(86, a86) f(87, a87) f(88, a88) f(89, a89) f(90, a90) f(91, a91) f(92, a92) f(93, a93) f(94, a94) f(95, a95) f(96, a96) f(97, a97) f(98, a98) + +#ifdef __EDG__ +#pragma endregion +#endif + +#ifdef __EDG__ +#pragma region Internal_functions (for internal use only) +#endif + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + static inline void + _tlgCreate1Vec(struct iovec* pVec, void const* pb, size_t cb) _tlg_NOEXCEPT _tlg_INLINE_ATTRIBUTES; + static inline void + _tlgCreate1Vec(struct iovec* pVec, void const* pb, size_t cb) _tlg_NOEXCEPT + { + pVec[0].iov_base = (void*)pb; + pVec[0].iov_len = cb * sizeof(char); + } + +#ifndef __cplusplus + + static inline void + _tlgCreate1Sz_char(struct iovec* pVec, char const* sz) _tlg_NOEXCEPT _tlg_INLINE_ATTRIBUTES; + static inline void + _tlgCreate1Sz_char(struct iovec* pVec, char const* sz) _tlg_NOEXCEPT + { + char const* pch = sz ? sz : ""; + size_t cch; + for (cch = 0; pch[cch] != 0; cch += 1) {} + cch += 1; // nul-termination + _tlgCreate1Vec(pVec, pch, cch * sizeof(char)); + } + + static inline void + _tlgCreate1Sz_wchar_t(struct iovec* pVec, wchar_t const* sz) _tlg_NOEXCEPT _tlg_INLINE_ATTRIBUTES; + static inline void + _tlgCreate1Sz_wchar_t(struct iovec* pVec, wchar_t const* sz) _tlg_NOEXCEPT + { + wchar_t const* pch = sz ? sz : L""; + size_t cch; + for (cch = 0; pch[cch] != 0; cch += 1) {} + cch += 1; // nul-termination + _tlgCreate1Vec(pVec, pch, cch * sizeof(wchar_t)); + } + + static inline void + _tlgCreate1Sz_char16_t(struct iovec* pVec, void const* sz) _tlg_NOEXCEPT _tlg_INLINE_ATTRIBUTES; + static inline void + _tlgCreate1Sz_char16_t(struct iovec* pVec, void const* sz) _tlg_NOEXCEPT + { + static uint16_t const Zero = 0; + uint16_t const* pch = sz ? (uint16_t const*)sz : &Zero; + size_t cch; + for (cch = 0; pch[cch] != 0; cch += 1) {} + cch += 1; // nul-termination + _tlgCreate1Vec(pVec, pch, cch * sizeof(uint16_t)); + } + + static inline void + _tlgCreate1Sz_char32_t(struct iovec* pVec, void const* sz) _tlg_NOEXCEPT _tlg_INLINE_ATTRIBUTES; + static inline void + _tlgCreate1Sz_char32_t(struct iovec* pVec, void const* sz) _tlg_NOEXCEPT + { + static uint32_t const Zero = 0; + uint32_t const* pch = sz ? (uint32_t const*)sz : &Zero; + size_t cch; + for (cch = 0; pch[cch] != 0; cch += 1) {} + cch += 1; // nul-termination + _tlgCreate1Vec(pVec, pch, cch * sizeof(uint32_t)); + } + +#else // __cplusplus +} // extern "C" + +template +inline void _tlgCppCreate1Val(struct iovec* pVec, ctype const& value) _tlg_NOEXCEPT _tlg_INLINE_ATTRIBUTES; +template +inline void _tlgCppCreate1Val(struct iovec* pVec, ctype const& value) _tlg_NOEXCEPT +{ + _tlgCreate1Vec(&pVec[0], &value, sizeof(ctype)); +} + +template +inline void _tlgCppCreate1SizeVals(struct iovec* pVec, ctype const* pValues, uint16_t cbValues) _tlg_NOEXCEPT _tlg_INLINE_ATTRIBUTES; +template +inline void _tlgCppCreate1SizeVals(struct iovec* pVec, ctype const* pValues, uint16_t cbValues) _tlg_NOEXCEPT +{ + _tlgCreate1Vec(&pVec[0], pValues, cbValues); +} + +template +inline void _tlgCppCreate1ValsNul(struct iovec* pVec, ctype const* pszValue) _tlg_NOEXCEPT _tlg_INLINE_ATTRIBUTES; +template +inline void _tlgCppCreate1ValsNul(struct iovec* pVec, ctype const* pszValue) _tlg_NOEXCEPT +{ + static ctype const Zero = 0; + ctype const* pch = pszValue ? pszValue : &Zero; + size_t cch; + for (cch = 0; pch[cch] != 0; cch += 1) {} + cch += 1; // nul-termination + _tlgCreate1Vec(&pVec[0], pch, cch * sizeof(ctype)); +} + +template +inline void _tlgCppCreate2CountVals(struct iovec* pVec, ctype const* pValues, uint16_t const& cValues) _tlg_NOEXCEPT _tlg_INLINE_ATTRIBUTES; +template +inline void _tlgCppCreate2CountVals(struct iovec* pVec, ctype const* pValues, uint16_t const& cValues) _tlg_NOEXCEPT +{ + _tlgCreate1Vec(&pVec[0], &cValues, sizeof(cValues)); + _tlgCreate1Vec(&pVec[1], pValues, cValues * sizeof(ctype)); +} + +template +inline void _tlgCppCreate2SizeVals(struct iovec* pVec, ctype const* pValues, uint16_t const& cbValues) _tlg_NOEXCEPT _tlg_INLINE_ATTRIBUTES; +template +inline void _tlgCppCreate2SizeVals(struct iovec* pVec, ctype const* pValues, uint16_t const& cbValues) _tlg_NOEXCEPT +{ + _tlgCreate1Vec(&pVec[0], &cbValues, sizeof(cbValues)); + _tlgCreate1Vec(&pVec[1], pValues, cbValues); +} + +// TraceLoggingValue support (implicit type detection) + +// Remove reference +template +struct _tlgRemoveReference +{ + typedef T type; +}; +template +struct _tlgRemoveReference +{ + typedef T type; +}; +template +struct _tlgRemoveReference +{ + typedef T type; +}; + +// Remove const/volatile +template +struct _tlgRemoveCV +{ + typedef T type; +}; +template +struct _tlgRemoveCV +{ + typedef T type; +}; +template +struct _tlgRemoveCV +{ + typedef T type; +}; +template +struct _tlgRemoveCV +{ + typedef T type; +}; + +// Given non-ref type, remove const/volatile. +template +struct _tlgDecay_impl +{ + typedef typename _tlgRemoveCV::type type; +}; +template +struct _tlgDecay_impl +{ + typedef T* type; +}; +template +struct _tlgDecay_impl +{ + typedef T* type; +}; + +// Remove reference, remove const/volatile, arrays decay to pointers. +template +struct _tlgDecay +{ + typedef typename _tlgDecay_impl::type>::type type; +}; + +/* +Convert a type into encoding + format. +*/ +template struct _tlgTypeMapBase +{ + static_assert(sizeof(T) == 0, "The type is not supported by TraceLoggingValue."); +}; + +template struct _tlgTypeMap + : _tlgTypeMapBase::type> { }; + +template +struct _tlgTypeMapVal { +#if __BYTE_ORDER == __LITTLE_ENDIAN + static uint16_t const value = + ((encoding | 0x80) << 0) | + ((format | (hasTag ? 0x80 : 0x00)) << 8); +#else + static uint16_t const value = + ((encoding | 0x80) << 8) | + ((format | (hasTag ? 0x80 : 0x00)) << 0); +#endif +}; + +// _tlgTypeMapBaseDecl: format is 0. +#define _tlgTypeMapBaseDecl(simple, ctype, encoding) \ + template<> struct _tlgTypeMapBase \ + { \ + typedef uint8_t _tlgTypeType0; /* No field tag: Don't need to store format. */ \ + typedef uint16_t _tlgTypeType1; /* Yes field tag: Need to store format = 0. */ \ + static bool const _tlgIsSimple = simple; \ + static _tlgTypeType0 const _tlgType0 = encoding | 0x00; \ + static _tlgTypeType1 const _tlgType1 = _tlgTypeMapVal::value; \ + } + +// _tlgTypeMapBaseDeclFmt: format is not 0. +#define _tlgTypeMapBaseDeclFmt(simple, ctype, encoding, format) \ + template<> struct _tlgTypeMapBase \ + { \ + typedef uint16_t _tlgTypeType0; /* Need to store format+encoding. */ \ + typedef uint16_t _tlgTypeType1; /* Need to store format+encoding. */ \ + static bool const _tlgIsSimple = simple; \ + static _tlgTypeType0 const _tlgType0 = _tlgTypeMapVal::value; \ + static _tlgTypeType1 const _tlgType1 = _tlgTypeMapVal::value; \ + } + +// _tlgCppCreate1Auto normal case (where we want to write sizeof(T) bytes of data): + +template +inline void _tlgCppCreate1Auto(struct iovec* pVec, ctype const& value) _tlg_NOEXCEPT _tlg_INLINE_ATTRIBUTES; +template +inline void _tlgCppCreate1Auto(struct iovec* pVec, ctype const& value) _tlg_NOEXCEPT +{ + static_assert(_tlgTypeMap::_tlgIsSimple, "Missing _tlgCppCreate1Auto overload"); + _tlgCreate1Vec(&pVec[0], &value, sizeof(ctype)); +} + +#define _tlgTypeMapBaseDecl0(ctype, encoding ) _tlgTypeMapBaseDecl( true, ctype, encoding) +#define _tlgTypeMapBaseDecl1(ctype, encoding, format) _tlgTypeMapBaseDeclFmt(true, ctype, encoding, format) +static_assert(sizeof(bool) == 1, "TraceLoggingValue implementation incomplete for bool."); +static_assert(sizeof(char) == 1, "TraceLoggingValue implementation incomplete for char."); +static_assert(sizeof(short) == 2, "TraceLoggingValue implementation incomplete for short."); +static_assert(sizeof(int) == 4, "TraceLoggingValue implementation incomplete for int."); +static_assert(sizeof(long long) == 8, "TraceLoggingValue implementation incomplete for long long."); +_tlgTypeMapBaseDecl1(bool, event_field_encoding_value8, event_field_format_boolean); +_tlgTypeMapBaseDecl1(char, event_field_encoding_value8, event_field_format_string8); +_tlgTypeMapBaseDecl1(char16_t, event_field_encoding_value16, event_field_format_string_utf); +_tlgTypeMapBaseDecl1(char32_t, event_field_encoding_value32, event_field_format_string_utf); +_tlgTypeMapBaseDecl1(wchar_t, event_field_encoding_value_wchar, event_field_format_string_utf); +_tlgTypeMapBaseDecl1(signed char, event_field_encoding_value8, event_field_format_signed_int); +_tlgTypeMapBaseDecl0(unsigned char, event_field_encoding_value8); +_tlgTypeMapBaseDecl1(signed short, event_field_encoding_value16, event_field_format_signed_int); +_tlgTypeMapBaseDecl0(unsigned short, event_field_encoding_value16); +_tlgTypeMapBaseDecl1(signed int, event_field_encoding_value32, event_field_format_signed_int); +_tlgTypeMapBaseDecl0(unsigned int, event_field_encoding_value32); +_tlgTypeMapBaseDecl1(signed long, event_field_encoding_value_long, event_field_format_signed_int); +_tlgTypeMapBaseDecl0(unsigned long, event_field_encoding_value_long); +_tlgTypeMapBaseDecl1(signed long long, event_field_encoding_value64, event_field_format_signed_int); +_tlgTypeMapBaseDecl0(unsigned long long, event_field_encoding_value64); +_tlgTypeMapBaseDecl1(float, event_field_encoding_value_float, event_field_format_float); +_tlgTypeMapBaseDecl1(double, event_field_encoding_value_double, event_field_format_float); +_tlgTypeMapBaseDecl1(void*, event_field_encoding_value_ptr, event_field_format_hex_int); +_tlgTypeMapBaseDecl1(void const*, event_field_encoding_value_ptr, event_field_format_hex_int); +#undef _tlgTypeMapBaseDecl0 +#undef _tlgTypeMapBaseDecl1 + +// _tlgCppCreate1Auto special cases (not writing sizeof(T) bytes of data): + +#define _tlgCppCreateAutoDecl(CHAR) \ + inline void _tlgCppCreate1Auto(struct iovec* pVec, CHAR* sz) _tlg_NOEXCEPT _tlg_INLINE_ATTRIBUTES; \ + inline void _tlgCppCreate1Auto(struct iovec* pVec, CHAR* sz) _tlg_NOEXCEPT \ + { \ + _tlgCppCreate1ValsNul(&pVec[0], sz); \ + } +_tlgCppCreateAutoDecl(char); +_tlgCppCreateAutoDecl(char const); +_tlgCppCreateAutoDecl(char16_t); +_tlgCppCreateAutoDecl(char16_t const); +_tlgCppCreateAutoDecl(char32_t); +_tlgCppCreateAutoDecl(char32_t const); +_tlgCppCreateAutoDecl(wchar_t); +_tlgCppCreateAutoDecl(wchar_t const); + +_tlgTypeMapBaseDeclFmt(false, char*, event_field_encoding_zstring_char8, event_field_format_string8); +_tlgTypeMapBaseDeclFmt(false, char const*, event_field_encoding_zstring_char8, event_field_format_string8); +_tlgTypeMapBaseDecl(false, char16_t*, event_field_encoding_zstring_char16); +_tlgTypeMapBaseDecl(false, char16_t const*, event_field_encoding_zstring_char16); +_tlgTypeMapBaseDecl(false, char32_t*, event_field_encoding_zstring_char32); +_tlgTypeMapBaseDecl(false, char32_t const*, event_field_encoding_zstring_char32); +_tlgTypeMapBaseDecl(false, wchar_t*, event_field_encoding_zstring_wchar); +_tlgTypeMapBaseDecl(false, wchar_t const*, event_field_encoding_zstring_wchar); + +// _tlgCppCreate1Auto special cases + +#endif // __cplusplus + +// ********** NO FUNCTION DEFINITIONS BELOW THIS POINT *********************** + +#ifdef __EDG__ +#pragma endregion +#endif + +#ifdef __EDG__ +#pragma region Internal_implementation macros (for internal use only) +#endif + +#define _tlgParseProviderId(...) \ + _tlgParseProviderId_impN(_tlg_NARGS(__VA_ARGS__), __VA_ARGS__) +#define _tlgParseProviderId_impN(n, providerId) \ + _tlg_PASTE2(_tlgParseProviderId_imp, n)(providerId) +#define _tlgParseProviderId_imp0(...) /* parameter not provided - error case */ \ + static_assert(0, "TRACELOGGING_DEFINE_PROVIDER providerId must be specified as eleven integers, e.g. (1,2,3,4,5,6,7,8,9,10,11)."); +#define _tlgParseProviderId_imp1(providerId) \ + _tracelogging_SyntaxError_ProviderIdMustBeEnclosedInParentheses providerId +#define _tracelogging_SyntaxError_ProviderIdMustBeEnclosedInParentheses(...) \ + static_assert(_tlg_NARGS(__VA_ARGS__) == 11, "TRACELOGGING_DEFINE_PROVIDER providerId must be eleven integers, e.g. (1,2,3,4,5,6,7,8,9,10,11)."); \ + static_assert(1 _tlg_FOREACH(_tlgParseProviderId_CheckInt, __VA_ARGS__), "TRACELOGGING_DEFINE_PROVIDER providerId must be eleven integers, e.g. (1,2,3,4,5,6,7,8,9,10,11)."); +#define _tlgParseProviderId_CheckInt(n, val) +(val) + +#define _tlgProviderOptions(...) _tlgProviderOptions_impA(_tlg_NARGS(__VA_ARGS__), __VA_ARGS__) +#define _tlgProviderOptions_impA(nargs, ...) _tlgProviderOptions_impB(_tlg_PASTE2(_tlgProviderOptions_imp, nargs), (__VA_ARGS__)) +#define _tlgProviderOptions_impB(macro, args) macro args +#define _tlgProviderOptions_imp0(...) "" +#define _tlgProviderOptions_imp1(option) _tlg_TraceLogging_Unrecognized_provider_option_##option +#define _tlg_TraceLogging_Unrecognized_provider_option_TraceLoggingOptionGroupName(groupName) "G" groupName + +/* +_tlgExpandType(typeParam) --> typeParam, hasType // typeParam should be a parenthesized value +_tlgExpandType(()) --> (0), 0 +_tlgExpandType((typeVal)) --> (typeVal), 1 +*/ +#define _tlgExpandType(typeParam) _tlgExpandType_impA(_tlg_NARGS typeParam, typeParam) +#define _tlgExpandType_impA(n, typeParam) _tlgExpandType_impB(_tlg_PASTE2(_tlgExpandType_imp, n), typeParam) +#define _tlgExpandType_impB(macro, args) macro args +#define _tlgExpandType_imp0() (0), 0 +#define _tlgExpandType_imp1(typeVal) (typeVal), 1 + +/* +_tlgNdt: Extracts Name/Description/Tag from varargs of wrapper macro with optional name. +_tlgNdt(macroname, value, __VA_ARGS__) --> "fieldName", L"description", tag, hasTag +*/ +#define _tlgNdt(macroname, value, ...) _tlgNdt_impA(_tlg_NARGS(__VA_ARGS__), (macroname, value, __VA_ARGS__)) +#define _tlgNdt_impA(n, args) _tlgNdt_impB(_tlg_PASTE2(_tlgNdt_imp, n), args) +#define _tlgNdt_impB(macro, args) macro args +#define _tlgNdt_imp0(macroname, value, ...) (#value, , , 0) +#define _tlgNdt_imp1(macroname, value, name) (name , , , 0) +#define _tlgNdt_imp2(macroname, value, name, desc) (name, L##desc, , 0) +#define _tlgNdt_imp3(macroname, value, name, desc, ftag) (name, L##desc, ftag, 1) +#define _tlgNdt_imp4(macroname, ...) (too_many_values_passed_to_##macroname, , , 0) +#define _tlgNdt_imp5(macroname, ...) (too_many_values_passed_to_##macroname, , , 0) + +#define _tlgNdtName(ndt) _tlgNdtName_imp ndt +#define _tlgNdtName_imp(name, desc, ftag, hasTag) name +#define _tlgNdtFtag(ndt) _tlgNdtFtag_imp ndt +#define _tlgNdtFtag_imp(name, desc, ftag, hasTag) ftag +#define _tlgNdtHasTag(ndt) _tlgNdtHasTag_imp ndt +#define _tlgNdtHasTag_imp(name, desc, ftag, hasTag) hasTag + +/* +_tlgApplyArgs and _tlgApplyArgsN: Macro dispatchers. +_tlgApplyArgs( macro, (handler, ...)) --> macro##handler(...) +_tlgApplyArgsN(macro, n, (handler, ...)) --> macro##handler(n, ...) +*/ +#define _tlgApplyArgs(macro, args) _tlgApplyArgs_impA((macro, _tlgApplyArgs_UNWRAP args)) +#define _tlgApplyArgs_impA(args) _tlgApplyArgs_impB args +#define _tlgApplyArgs_impB(macro, handler, ...) _tlgApplyArgs_CALL(macro, handler, (__VA_ARGS__)) +#define _tlgApplyArgs_UNWRAP(...) __VA_ARGS__ +#define _tlgApplyArgs_CALL(macro, handler, args) macro##handler args +#define _tlgApplyArgsN(macro, n, args) _tlgApplyArgsN_impA((macro, n, _tlgApplyArgs_UNWRAP args)) +#define _tlgApplyArgsN_impA(args) _tlgApplyArgsN_impB args +#define _tlgApplyArgsN_impB(macro, n, handler, ...) _tlgApplyArgs_CALL(macro, handler, (n, __VA_ARGS__)) + +// Internal implementation details: Not for use outside of TraceLoggingProvider.h. +#define _tlgArgIgnored() /* for TraceLoggingDescription, etc. */ \ + (_tlgIgnored) +#define _tlgArgKeyword( eventKeyword) /* for TraceLoggingKeyword. */ \ + (_tlgKeyword, eventKeyword) +#define _tlgArgOpcode( eventOpcode) /* for TraceLoggingOpcode. */ \ + (_tlgOpcode, eventOpcode) +#define _tlgArgEventTag( eventTag) /* for TraceLoggingEventTag. */ \ + (_tlgEventTag, eventTag) +#define _tlgArgIdVersion( eventId, eventVersion) /* for TraceLoggingIdVersion. */ \ + (_tlgIdVersion, eventId, eventVersion) +#define _tlgArgLevel( eventLevel) /* for TraceLoggingLevel. */ \ + (_tlgLevel, eventLevel) +#define _tlgArgAuto( value, ndt) /* for TraceLoggingValue. */ \ + (_tlgAuto, value, ndt) +#define _tlgArgValue( ctype, value, encoding, format, ndt) /* for by-val scalar. */ \ + (_tlgValue, ctype, value, encoding, _tlgExpandType(format), ndt) +#define _tlgArgStrNul( ctype, pszValue, encoding, format, ndt) /* for zero-terminated string. */ \ + (_tlgStrNul, ctype, pszValue, encoding, _tlgExpandType(format), ndt) +#define _tlgArgStrCch( ctype, pchValue, cchValue, encoding, format, ndt) /* for counted strings. */ \ + (_tlgStrCch, ctype, pchValue, cchValue, encoding, _tlgExpandType(format), ndt) +#define _tlgArgBin( ctype, pValue, cbValue, encoding, format, ndt) /* for binary data. */ \ + (_tlgBin, ctype, pValue, cbValue, encoding, _tlgExpandType(format), ndt) +#define _tlgArgVArray( ctype, pValues, cValues, encoding, format, ndt) /* for variable-length array with count of elements. */ \ + (_tlgVArray, ctype, pValues, cValues, encoding, _tlgExpandType(format), ndt) +#define _tlgArgCArray( ctype, pValues, cValues, encoding, format, ndt) /* for fixed-length array with count of elements. */ \ + (_tlgCArray, ctype, pValues, cValues, encoding, _tlgExpandType(format), ndt) +#define _tlgArgPackedField(ctype, pValue, cbValue, encoding, format, ndt) /* for user-marshalled data and metadata. */ \ + (_tlgPackedField,ctype, pValue, cbValue, encoding, _tlgExpandType(format), ndt) +#define _tlgArgPackedMeta( encoding, format, ndt) /* for user-marshalled metadata. */ \ + (_tlgPackedMeta, encoding, _tlgExpandType(format), ndt) +#define _tlgArgPackedData( ctype, pValue, cbValue ) /* for user-marshalled data. */ \ + (_tlgPackedData, ctype, pValue, cbValue) +#define _tlgArgStruct( fieldCount, encoding, ndt) /* for struct and array of struct. */ \ + (_tlgStruct, fieldCount, encoding, ndt) + +// Extract TraceLoggingKeyword (OR'ed together). +#define _tlgKeywordVal(n, args) _tlgApplyArgs(_tlgKeywordVal, args) +#define _tlgKeywordVal_tlgIgnored( ... ) +#define _tlgKeywordVal_tlgKeyword( eventKeyword ) | (eventKeyword) +#define _tlgKeywordVal_tlgOpcode( eventOpcode ) +#define _tlgKeywordVal_tlgEventTag( eventTag ) +#define _tlgKeywordVal_tlgIdVersion( eventId, eventVersion ) +#define _tlgKeywordVal_tlgLevel( eventLevel ) +#define _tlgKeywordVal_tlgAuto( value, ndt) +#define _tlgKeywordVal_tlgValue( ctype, value, encoding, format, hasFmt, ndt) +#define _tlgKeywordVal_tlgStrNul( ctype, pszValue, encoding, format, hasFmt, ndt) +#define _tlgKeywordVal_tlgStrCch( ctype, pchValue, cchValue, encoding, format, hasFmt, ndt) +#define _tlgKeywordVal_tlgBin( ctype, pValue, cbValue, encoding, format, hasFmt, ndt) +#define _tlgKeywordVal_tlgVArray( ctype, pValues, cValues, encoding, format, hasFmt, ndt) +#define _tlgKeywordVal_tlgCArray( ctype, pValues, cValues, encoding, format, hasFmt, ndt) +#define _tlgKeywordVal_tlgPackedField(ctype, pValue, cbValue, encoding, format, hasFmt, ndt) +#define _tlgKeywordVal_tlgPackedMeta( encoding, format, hasFmt, ndt) +#define _tlgKeywordVal_tlgPackedData( ctype, pValue, cbValue ) +#define _tlgKeywordVal_tlgStruct( fieldCount, encoding, ndt) + +// Extract TraceLoggingOpcode into a constant (last one wins). +#define _tlgOpcodeVal(n, args) _tlgApplyArgs(_tlgOpcodeVal, args) +#define _tlgOpcodeVal_tlgIgnored( ... ) +#define _tlgOpcodeVal_tlgKeyword( eventKeyword ) +#define _tlgOpcodeVal_tlgOpcode( eventOpcode ) & 0u) | ((eventOpcode) +#define _tlgOpcodeVal_tlgEventTag( eventTag ) +#define _tlgOpcodeVal_tlgIdVersion( eventId, eventVersion ) +#define _tlgOpcodeVal_tlgLevel( eventLevel ) +#define _tlgOpcodeVal_tlgAuto( value, ndt) +#define _tlgOpcodeVal_tlgValue( ctype, value, encoding, format, hasFmt, ndt) +#define _tlgOpcodeVal_tlgStrNul( ctype, pszValue, encoding, format, hasFmt, ndt) +#define _tlgOpcodeVal_tlgStrCch( ctype, pchValue, cchValue, encoding, format, hasFmt, ndt) +#define _tlgOpcodeVal_tlgBin( ctype, pValue, cbValue, encoding, format, hasFmt, ndt) +#define _tlgOpcodeVal_tlgVArray( ctype, pValues, cValues, encoding, format, hasFmt, ndt) +#define _tlgOpcodeVal_tlgCArray( ctype, pValues, cValues, encoding, format, hasFmt, ndt) +#define _tlgOpcodeVal_tlgPackedField(ctype, pValue, cbValue, encoding, format, hasFmt, ndt) +#define _tlgOpcodeVal_tlgPackedMeta( encoding, format, hasFmt, ndt) +#define _tlgOpcodeVal_tlgPackedData( ctype, pValue, cbValue ) +#define _tlgOpcodeVal_tlgStruct( fieldCount, encoding, ndt) + +// Extract TraceLoggingEventTag into a constant (last one wins). +#define _tlgEventTagVal(n, args) _tlgApplyArgs(_tlgEventTagVal, args) +#define _tlgEventTagVal_tlgIgnored( ... ) +#define _tlgEventTagVal_tlgKeyword( eventKeyword ) +#define _tlgEventTagVal_tlgOpcode( eventOpcode ) +#define _tlgEventTagVal_tlgEventTag( eventTag ) & 0u) | ((eventTag) +#define _tlgEventTagVal_tlgIdVersion( eventId, eventVersion ) +#define _tlgEventTagVal_tlgLevel( eventLevel ) +#define _tlgEventTagVal_tlgAuto( value, ndt) +#define _tlgEventTagVal_tlgValue( ctype, value, encoding, format, hasFmt, ndt) +#define _tlgEventTagVal_tlgStrNul( ctype, pszValue, encoding, format, hasFmt, ndt) +#define _tlgEventTagVal_tlgStrCch( ctype, pchValue, cchValue, encoding, format, hasFmt, ndt) +#define _tlgEventTagVal_tlgBin( ctype, pValue, cbValue, encoding, format, hasFmt, ndt) +#define _tlgEventTagVal_tlgVArray( ctype, pValues, cValues, encoding, format, hasFmt, ndt) +#define _tlgEventTagVal_tlgCArray( ctype, pValues, cValues, encoding, format, hasFmt, ndt) +#define _tlgEventTagVal_tlgPackedField(ctype, pValue, cbValue, encoding, format, hasFmt, ndt) +#define _tlgEventTagVal_tlgPackedMeta( encoding, format, hasFmt, ndt) +#define _tlgEventTagVal_tlgPackedData( ctype, pValue, cbValue ) +#define _tlgEventTagVal_tlgStruct( fieldCount, encoding, ndt) + +// Extract TraceLoggingIdVersion into a constant (last one wins). +#define _tlgIdVersionVal(n, args) _tlgApplyArgs(_tlgIdVersionVal, args) +#define _tlgIdVersionVal_tlgIgnored( ... ) +#define _tlgIdVersionVal_tlgKeyword( eventKeyword ) +#define _tlgIdVersionVal_tlgOpcode( eventOpcode ) +#define _tlgIdVersionVal_tlgEventTag( eventTag ) +#define _tlgIdVersionVal_tlgIdVersion( eventId, eventVersion ) & 0u) | (((eventId) | ((uint64_t)(eventVersion) << 32)) +#define _tlgIdVersionVal_tlgLevel( eventLevel ) +#define _tlgIdVersionVal_tlgAuto( value, ndt) +#define _tlgIdVersionVal_tlgValue( ctype, value, encoding, format, hasFmt, ndt) +#define _tlgIdVersionVal_tlgStrNul( ctype, pszValue, encoding, format, hasFmt, ndt) +#define _tlgIdVersionVal_tlgStrCch( ctype, pchValue, cchValue, encoding, format, hasFmt, ndt) +#define _tlgIdVersionVal_tlgBin( ctype, pValue, cbValue, encoding, format, hasFmt, ndt) +#define _tlgIdVersionVal_tlgVArray( ctype, pValues, cValues, encoding, format, hasFmt, ndt) +#define _tlgIdVersionVal_tlgCArray( ctype, pValues, cValues, encoding, format, hasFmt, ndt) +#define _tlgIdVersionVal_tlgPackedField(ctype, pValue, cbValue, encoding, format, hasFmt, ndt) +#define _tlgIdVersionVal_tlgPackedMeta( encoding, format, hasFmt, ndt) +#define _tlgIdVersionVal_tlgPackedData( ctype, pValue, cbValue ) +#define _tlgIdVersionVal_tlgStruct( fieldCount, encoding, ndt) + +// Extract TraceLoggingLevel into a constant (last one wins). +#define _tlgLevelVal(n, args) _tlgApplyArgs(_tlgLevelVal, args) +#define _tlgLevelVal_tlgIgnored( ... ) +#define _tlgLevelVal_tlgKeyword( eventKeyword ) +#define _tlgLevelVal_tlgOpcode( eventOpcode ) +#define _tlgLevelVal_tlgEventTag( eventTag ) +#define _tlgLevelVal_tlgIdVersion( eventId, eventVersion ) +#define _tlgLevelVal_tlgLevel( eventLevel ) & 0u) | ((eventLevel) +#define _tlgLevelVal_tlgAuto( value, ndt) +#define _tlgLevelVal_tlgValue( ctype, value, encoding, format, hasFmt, ndt) +#define _tlgLevelVal_tlgStrNul( ctype, pszValue, encoding, format, hasFmt, ndt) +#define _tlgLevelVal_tlgStrCch( ctype, pchValue, cchValue, encoding, format, hasFmt, ndt) +#define _tlgLevelVal_tlgBin( ctype, pValue, cbValue, encoding, format, hasFmt, ndt) +#define _tlgLevelVal_tlgVArray( ctype, pValues, cValues, encoding, format, hasFmt, ndt) +#define _tlgLevelVal_tlgCArray( ctype, pValues, cValues, encoding, format, hasFmt, ndt) +#define _tlgLevelVal_tlgPackedField(ctype, pValue, cbValue, encoding, format, hasFmt, ndt) +#define _tlgLevelVal_tlgPackedMeta( encoding, format, hasFmt, ndt) +#define _tlgLevelVal_tlgPackedData( ctype, pValue, cbValue ) +#define _tlgLevelVal_tlgStruct( fieldCount, encoding, ndt) + +// Declare struct fields needed for event field metadata. +#define _tlgInfoVars(n, args) _tlgApplyArgsN(_tlgInfoVars, n, args) +#define _tlgInfoVars_tlgIgnored( n, ... ) +#define _tlgInfoVars_tlgKeyword( n, eventKeyword ) +#define _tlgInfoVars_tlgOpcode( n, eventOpcode ) +#define _tlgInfoVars_tlgEventTag( n, eventTag ) +#define _tlgInfoVars_tlgIdVersion( n, eventId, eventVersion ) +#define _tlgInfoVars_tlgLevel( n, eventLevel ) +#define _tlgInfoVars_tlgAuto( n, value, ndt) char _tlgName##n[sizeof(_tlgNdtName(ndt))]; typename _tlgTypeMap::_tlg_PASTE2(_tlgTypeType, _tlgNdtHasTag(ndt)) _tlgTy##n; _tlg_PASTE2(_tlgInfoVars_, _tlgNdtHasTag(ndt))(n) +#define _tlgInfoVars_tlgValue( n, ctype, value, encoding, format, hasFmt, ndt) char _tlgName##n[sizeof(_tlgNdtName(ndt))]; uint8_t _tlgEnc##n; _tlg_PASTE3(_tlgInfoVars_, hasFmt, _tlgNdtHasTag(ndt))(n) +#define _tlgInfoVars_tlgStrNul( n, ctype, pszValue, encoding, format, hasFmt, ndt) char _tlgName##n[sizeof(_tlgNdtName(ndt))]; uint8_t _tlgEnc##n; _tlg_PASTE3(_tlgInfoVars_, hasFmt, _tlgNdtHasTag(ndt))(n) +#define _tlgInfoVars_tlgStrCch( n, ctype, pchValue, cchValue, encoding, format, hasFmt, ndt) char _tlgName##n[sizeof(_tlgNdtName(ndt))]; uint8_t _tlgEnc##n; _tlg_PASTE3(_tlgInfoVars_, hasFmt, _tlgNdtHasTag(ndt))(n) +#define _tlgInfoVars_tlgBin( n, ctype, pValue, cbValue, encoding, format, hasFmt, ndt) char _tlgName##n[sizeof(_tlgNdtName(ndt))]; uint8_t _tlgEnc##n; _tlg_PASTE3(_tlgInfoVars_, hasFmt, _tlgNdtHasTag(ndt))(n) +#define _tlgInfoVars_tlgVArray( n, ctype, pValues, cValues, encoding, format, hasFmt, ndt) char _tlgName##n[sizeof(_tlgNdtName(ndt))]; uint8_t _tlgEnc##n; _tlg_PASTE3(_tlgInfoVars_, hasFmt, _tlgNdtHasTag(ndt))(n) +#define _tlgInfoVars_tlgCArray( n, ctype, pValues, cValues, encoding, format, hasFmt, ndt) char _tlgName##n[sizeof(_tlgNdtName(ndt))]; uint8_t _tlgEnc##n; _tlg_PASTE3(_tlgInfoVars_, hasFmt, _tlgNdtHasTag(ndt))(n) uint16_t _tlgValCount##n; \ + static_assert((cValues) > 0, "TraceLoggingFixedArray count must be greater than 0."); +#define _tlgInfoVars_tlgPackedField(n, ctype, pValue, cbValue, encoding, format, hasFmt, ndt) char _tlgName##n[sizeof(_tlgNdtName(ndt))]; uint8_t _tlgEnc##n; _tlg_PASTE3(_tlgInfoVars_, hasFmt, _tlgNdtHasTag(ndt))(n) \ + _tlg_AssertValidPackedMetadataEncoding(encoding); +#define _tlgInfoVars_tlgPackedMeta( n, encoding, format, hasFmt, ndt) char _tlgName##n[sizeof(_tlgNdtName(ndt))]; uint8_t _tlgEnc##n; _tlg_PASTE3(_tlgInfoVars_, hasFmt, _tlgNdtHasTag(ndt))(n) \ + _tlg_AssertValidPackedMetadataEncoding(encoding); +#define _tlgInfoVars_tlgPackedData( n, ctype, pValue, cbValue ) +#define _tlgInfoVars_tlgStruct( n, fieldCount, encoding, ndt) char _tlgName##n[sizeof(_tlgNdtName(ndt))]; uint8_t _tlgEnc##n; _tlg_PASTE3(_tlgInfoVars_, 1, _tlgNdtHasTag(ndt))(n) \ + static_assert((fieldCount) > 0, "TraceLoggingStruct fieldCount must be greater than 0."); \ + static_assert((fieldCount) < 128, "TraceLoggingStruct fieldCount must be less than 128."); +#define _tlgInfoVars_0(n) +#define _tlgInfoVars_1(n) uint16_t _tlgTag##n; +#define _tlgInfoVars_00(n) +#define _tlgInfoVars_10(n) uint8_t _tlgFmt##n; +#define _tlgInfoVars_01(n) uint8_t _tlgFmt##n;uint16_t _tlgTag##n; +#define _tlgInfoVars_11(n) uint8_t _tlgFmt##n;uint16_t _tlgTag##n; + +// Declare struct field initializers needed for event field metadata. +#define _tlgInfoVals(n, args) _tlgApplyArgsN(_tlgInfoVals, n, args) +#define _tlgInfoVals_tlgIgnored( n, ... ) +#define _tlgInfoVals_tlgKeyword( n, eventKeyword ) +#define _tlgInfoVals_tlgOpcode( n, eventOpcode ) +#define _tlgInfoVals_tlgEventTag( n, eventTag ) +#define _tlgInfoVals_tlgIdVersion( n, eventId, eventVersion ) +#define _tlgInfoVals_tlgLevel( n, eventLevel ) +#define _tlgInfoVals_tlgAuto( n, value, ndt) , (_tlgNdtName(ndt)), _tlgTypeMap::_tlg_PASTE2(_tlgType, _tlgNdtHasTag(ndt)) _tlg_PASTE2(_tlgInfoVals_, _tlgNdtHasTag(ndt))(_tlgNdtFtag(ndt)) +#define _tlgInfoVals_tlgValue( n, ctype, value, encoding, format, hasFmt, ndt) , (_tlgNdtName(ndt)), encoding _tlg_PASTE3(_tlgInfoVals_, hasFmt, _tlgNdtHasTag(ndt))(format, _tlgNdtFtag(ndt)) +#define _tlgInfoVals_tlgStrNul( n, ctype, pszValue, encoding, format, hasFmt, ndt) , (_tlgNdtName(ndt)), encoding _tlg_PASTE3(_tlgInfoVals_, hasFmt, _tlgNdtHasTag(ndt))(format, _tlgNdtFtag(ndt)) +#define _tlgInfoVals_tlgStrCch( n, ctype, pchValue, cchValue, encoding, format, hasFmt, ndt) , (_tlgNdtName(ndt)), encoding _tlg_PASTE3(_tlgInfoVals_, hasFmt, _tlgNdtHasTag(ndt))(format, _tlgNdtFtag(ndt)) +#define _tlgInfoVals_tlgBin( n, ctype, pValue, cbValue, encoding, format, hasFmt, ndt) , (_tlgNdtName(ndt)), encoding _tlg_PASTE3(_tlgInfoVals_, hasFmt, _tlgNdtHasTag(ndt))(format, _tlgNdtFtag(ndt)) +#define _tlgInfoVals_tlgVArray( n, ctype, pValues, cValues, encoding, format, hasFmt, ndt) , (_tlgNdtName(ndt)), encoding|event_field_encoding_varray_flag _tlg_PASTE3(_tlgInfoVals_, hasFmt, _tlgNdtHasTag(ndt))(format, _tlgNdtFtag(ndt)) +#define _tlgInfoVals_tlgCArray( n, ctype, pValues, cValues, encoding, format, hasFmt, ndt) , (_tlgNdtName(ndt)), encoding|event_field_encoding_carray_flag _tlg_PASTE3(_tlgInfoVals_, hasFmt, _tlgNdtHasTag(ndt))(format, _tlgNdtFtag(ndt)), (cValues) +#define _tlgInfoVals_tlgPackedField(n, ctype, pValue, cbValue, encoding, format, hasFmt, ndt) , (_tlgNdtName(ndt)), encoding _tlg_PASTE3(_tlgInfoVals_, hasFmt, _tlgNdtHasTag(ndt))(format, _tlgNdtFtag(ndt)) +#define _tlgInfoVals_tlgPackedMeta( n, encoding, format, hasFmt, ndt) , (_tlgNdtName(ndt)), encoding _tlg_PASTE3(_tlgInfoVals_, hasFmt, _tlgNdtHasTag(ndt))(format, _tlgNdtFtag(ndt)) +#define _tlgInfoVals_tlgPackedData( n, ctype, pValue, cbValue ) +#define _tlgInfoVals_tlgStruct( n, fieldCount, encoding, ndt) , (_tlgNdtName(ndt)), encoding _tlg_PASTE3(_tlgInfoVals_, 1, _tlgNdtHasTag(ndt))(fieldCount, _tlgNdtFtag(ndt)) +#define _tlgInfoVals_0(...) +#define _tlgInfoVals_1(ftag) , (ftag) +#define _tlgInfoVals_00(format, ftag) +#define _tlgInfoVals_10(format, ftag) |128, format +#define _tlgInfoVals_01(format, ftag) |128, 128, (ftag) +#define _tlgInfoVals_11(format, ftag) |128, format|128, (ftag) + +// Count the iovecs needed for event field data. +#define _tlgDataDescCount(n, args) _tlgApplyArgs(_tlgDataDescCount, args) +#define _tlgDataDescCount_tlgIgnored( ... ) +#define _tlgDataDescCount_tlgKeyword( eventKeyword ) +#define _tlgDataDescCount_tlgOpcode( eventOpcode ) +#define _tlgDataDescCount_tlgEventTag( eventTag ) +#define _tlgDataDescCount_tlgIdVersion( eventId, eventVersion ) +#define _tlgDataDescCount_tlgLevel( eventLevel ) +#define _tlgDataDescCount_tlgAuto( value, ndt) +1 +#define _tlgDataDescCount_tlgValue( ctype, value, encoding, format, hasFmt, ndt) +1 +#define _tlgDataDescCount_tlgStrNul( ctype, pszValue, encoding, format, hasFmt, ndt) +1 +#define _tlgDataDescCount_tlgStrCch( ctype, pchValue, cchValue, encoding, format, hasFmt, ndt) +2 +#define _tlgDataDescCount_tlgBin( ctype, pValue, cbValue, encoding, format, hasFmt, ndt) +2 +#define _tlgDataDescCount_tlgVArray( ctype, pValues, cValues, encoding, format, hasFmt, ndt) +2 +#define _tlgDataDescCount_tlgCArray( ctype, pValues, cValues, encoding, format, hasFmt, ndt) +1 +#define _tlgDataDescCount_tlgPackedField(ctype, pValue, cbValue, encoding, format, hasFmt, ndt) +1 +#define _tlgDataDescCount_tlgPackedMeta( encoding, format, hasFmt, ndt) +#define _tlgDataDescCount_tlgPackedData( ctype, pValue, cbValue ) +1 +#define _tlgDataDescCount_tlgStruct( fieldCount, encoding, ndt) + +// Populate the iovecs needed for event field data. +#ifdef __cplusplus + +// For C++, we populate the iovecs in one expression. +// Temporary values are stashed in function parameters. +// This supports things like TraceLoggingString(ReturnStdString().c_str()). + +#define _tlgBeginCppEval ( +#define _tlgEndCppEval ) + +#define _tlgDataDescCreate(n, args) _tlgApplyArgsN(_tlgDataDescCreate, n, args) +#define _tlgDataDescCreate_tlgIgnored( n, ... ) +#define _tlgDataDescCreate_tlgKeyword( n, eventKeyword ) +#define _tlgDataDescCreate_tlgOpcode( n, eventOpcode ) +#define _tlgDataDescCreate_tlgEventTag( n, eventTag ) +#define _tlgDataDescCreate_tlgIdVersion( n, eventId, eventVersion ) +#define _tlgDataDescCreate_tlgLevel( n, eventLevel ) +#define _tlgDataDescCreate_tlgAuto( n, value, ndt) \ + _tlgCppCreate1Auto(&_tlgVecs[_tlgIdx++], (value)), +#define _tlgDataDescCreate_tlgValue( n, ctype, value, encoding, format, hasFmt, ndt) \ + _tlgCppCreate1Val(&_tlgVecs[_tlgIdx++], (value)), +#define _tlgDataDescCreate_tlgStrNul( n, ctype, pszValue, encoding, format, hasFmt, ndt) \ + _tlgCppCreate1ValsNul(&_tlgVecs[_tlgIdx++], (pszValue)), +#define _tlgDataDescCreate_tlgStrCch( n, ctype, pchValue, cchValue, encoding, format, hasFmt, ndt) \ + _tlgCppCreate2CountVals(&_tlgVecs[_tlgIdx], (pchValue), (cchValue)), \ + _tlgIdx += 2, +#define _tlgDataDescCreate_tlgBin( n, ctype, pValue, cbValue, encoding, format, hasFmt, ndt) \ + _tlgCppCreate2SizeVals(&_tlgVecs[_tlgIdx], (pValue), (cbValue)), \ + _tlgIdx += 2, +#define _tlgDataDescCreate_tlgVArray( n, ctype, pValues, cValues, encoding, format, hasFmt, ndt) \ + _tlgCppCreate2CountVals(&_tlgVecs[_tlgIdx], (pValues), (cValues)), \ + _tlgIdx += 2, +#define _tlgDataDescCreate_tlgCArray( n, ctype, pValues, cValues, encoding, format, hasFmt, ndt) \ + _tlgCppCreate1SizeVals(&_tlgVecs[_tlgIdx++], (pValues), (cValues)*sizeof(ctype)), +#define _tlgDataDescCreate_tlgPackedField(n, ctype, pValue, cbValue, encoding, format, hasFmt, ndt) \ + _tlgCppCreate1SizeVals(&_tlgVecs[_tlgIdx++], (pValue), (cbValue)*sizeof(char)), +#define _tlgDataDescCreate_tlgPackedMeta( n, encoding, format, hasFmt, ndt) \ + /* Nothing here. */ +#define _tlgDataDescCreate_tlgPackedData( n, ctype, pValue, cbValue ) \ + _tlgCppCreate1SizeVals(&_tlgVecs[_tlgIdx++], (pValue), (cbValue)*sizeof(char)), +#define _tlgDataDescCreate_tlgStruct( n, fieldCount, encoding, ndt) \ + /* Nothing here. */ + +#else // __cplusplus + +// For C, we populate the iovecs in a series of statements. +// Temporary values are stashed in named variables. +// C has no destructors so this is safe. + +#define _tlgBeginCppEval +#define _tlgEndCppEval + +#define _tlgDataDescCreate(n, args) _tlgApplyArgsN(_tlgDataDescCreate, n, args) +#define _tlgDataDescCreate_tlgIgnored( n, ... ) +#define _tlgDataDescCreate_tlgKeyword( n, eventKeyword ) +#define _tlgDataDescCreate_tlgOpcode( n, eventOpcode ) +#define _tlgDataDescCreate_tlgEventTag( n, eventTag ) +#define _tlgDataDescCreate_tlgIdVersion( n, eventId, eventVersion ) +#define _tlgDataDescCreate_tlgLevel( n, eventLevel ) +#define _tlgDataDescCreate_tlgValue( n, ctype, value, encoding, format, hasFmt, ndt) \ + ctype const _tlgTemp##n = (value); \ + _tlgCreate1Vec(&_tlgVecs[_tlgIdx], &_tlgTemp##n, sizeof(ctype)); \ + _tlgIdx += 1; +#define _tlgDataDescCreate_tlgStrNul( n, ctype, pszValue, encoding, format, hasFmt, ndt) \ + ctype const* const _tlgTemp##n = (pszValue); \ + _tlgCreate1Sz_##ctype(&_tlgVecs[_tlgIdx], _tlgTemp##n); \ + _tlgIdx += 1; +#define _tlgDataDescCreate_tlgStrCch( n, ctype, pchValue, cchValue, encoding, format, hasFmt, ndt) \ + ctype const* const _tlgTemp##n = (pchValue); \ + uint16_t const _tlgCount##n = (cchValue); \ + _tlgCreate1Vec(&_tlgVecs[_tlgIdx+0], &_tlgCount##n, 2); \ + _tlgCreate1Vec(&_tlgVecs[_tlgIdx+1], _tlgTemp##n, _tlgCount##n * sizeof(ctype)); \ + _tlgIdx += 2; +#define _tlgDataDescCreate_tlgBin( n, ctype, pValue, cbValue, encoding, format, hasFmt, ndt) \ + ctype const* const _tlgTemp##n = (pValue); \ + uint16_t const _tlgCount##n = (cbValue); \ + _tlgCreate1Vec(&_tlgVecs[_tlgIdx+0], &_tlgCount##n, 2); \ + _tlgCreate1Vec(&_tlgVecs[_tlgIdx+1], _tlgTemp##n, _tlgCount##n); \ + _tlgIdx += 2; +#define _tlgDataDescCreate_tlgVArray( n, ctype, pValues, cValues, encoding, format, hasFmt, ndt) \ + ctype const* const _tlgTemp##n = (pValues); \ + uint16_t const _tlgCount##n = (cValues); \ + _tlgCreate1Vec(&_tlgVecs[_tlgIdx+0], &_tlgCount##n, 2); \ + _tlgCreate1Vec(&_tlgVecs[_tlgIdx+1], _tlgTemp##n, _tlgCount##n * sizeof(ctype)); \ + _tlgIdx += 2; +#define _tlgDataDescCreate_tlgCArray( n, ctype, pValues, cValues, encoding, format, hasFmt, ndt) \ + ctype const* const _tlgTemp##n = (pValues); \ + _tlgCreate1Vec(&_tlgVecs[_tlgIdx], _tlgTemp##n, (cValues) * sizeof(ctype)); \ + _tlgIdx += 1; +#define _tlgDataDescCreate_tlgPackedField(n, ctype, pValue, cbValue, encoding, format, hasFmt, ndt) \ + ctype const* const _tlgTemp##n = (pValue); \ + _tlgCreate1Vec(&_tlgVecs[_tlgIdx], _tlgTemp##n, (cbValue)); \ + _tlgIdx += 1; +#define _tlgDataDescCreate_tlgPackedMeta( n, encoding, format, hasFmt, ndt) \ + /* Nothing here. */ +#define _tlgDataDescCreate_tlgPackedData( n, ctype, pValue, cbValue ) \ + ctype const* const _tlgTemp##n = (pValue); \ + _tlgCreate1Vec(&_tlgVecs[_tlgIdx], _tlgTemp##n, (cbValue)); \ + _tlgIdx += 1; +#define _tlgDataDescCreate_tlgStruct( n, fieldCount, encoding, ndt) \ + /* Nothing here. */ + +#endif // __cplusplus + +#define _tlg_AssertValidPackedMetadataEncoding(encoding) \ + static_assert( \ + ((encoding)&event_field_encoding_value_mask) > event_field_encoding_struct && \ + ((encoding)&event_field_encoding_value_mask) < event_field_encoding_max && \ + ((encoding)|event_field_encoding_value_mask|event_field_encoding_varray_flag) == (event_field_encoding_value_mask|event_field_encoding_varray_flag), \ + "Invalid packed metadata encoding: " #encoding) + +#define _tlgWriteImp(providerSymbol, eventName, pActivityId, pRelatedActivityId, ...) ({ \ + enum { \ + _tlgKeywordVal = (uint64_t)(0u _tlg_FOREACH(_tlgKeywordVal, __VA_ARGS__)), \ + _tlgOpcodeVal = (uint64_t)(0u _tlg_FOREACH(_tlgOpcodeVal, __VA_ARGS__)), \ + _tlgEventTagVal = (uint64_t)(0u _tlg_FOREACH(_tlgEventTagVal, __VA_ARGS__)), \ + _tlgIdVersionVal = (uint64_t)(0u _tlg_FOREACH(_tlgIdVersionVal, __VA_ARGS__)), \ + _tlgLevelVal = (uint64_t)(5u _tlg_FOREACH(_tlgLevelVal, __VA_ARGS__)) \ + }; \ + static tracepoint_state _tlgEvtState = TRACEPOINT_STATE_INIT; \ + static struct { \ + eventheader_extension _tlgExt; \ + struct { \ + char _tlgName[sizeof(eventName)]; \ + _tlg_FOREACH(_tlgInfoVars, __VA_ARGS__) \ + } __attribute__((packed)) _tlgDat; \ + } const _tlgMeta = { \ + { sizeof(_tlgMeta._tlgDat), eventheader_extension_kind_metadata }, \ + { (eventName) _tlg_FOREACH(_tlgInfoVals, __VA_ARGS__) } \ + }; \ + static eventheader_tracepoint const _tlgEvt = { \ + &_tlgEvtState, \ + &_tlgMeta._tlgExt, \ + { \ + eventheader_flag_default, \ + (uint64_t)(_tlgIdVersionVal) >> 32, \ + _tlgIdVersionVal & 0xFFFFFFFF, \ + _tlgEventTagVal, \ + _tlgOpcodeVal, \ + _tlgLevelVal \ + }, \ + _tlgKeywordVal \ + }; \ + static eventheader_tracepoint const* const _tlgEvtPtr \ + __attribute__((section("_tlgEventPtrs_" _tlg_STRINGIZE(providerSymbol)), used)) \ + = &_tlgEvt; \ + int _tlgWriteErr = 9 /*EBADF*/; \ + if (TRACEPOINT_ENABLED(&_tlgEvtState)) { \ + struct iovec _tlgVecs[EVENTHEADER_PREFIX_DATAVEC_COUNT _tlg_FOREACH(_tlgDataDescCount, __VA_ARGS__)]; \ + unsigned _tlgIdx = EVENTHEADER_PREFIX_DATAVEC_COUNT; \ + _tlgBeginCppEval /* For C++, ensure no semicolons until after Write. */ \ + _tlg_FOREACH(_tlgDataDescCreate, __VA_ARGS__) \ + _tlgWriteErr = eventheader_write( \ + &_tlgEvt, \ + (pActivityId), (pRelatedActivityId), _tlgIdx, _tlgVecs) \ + _tlgEndCppEval; \ + } \ + _tlgWriteErr; }) + +#ifdef __EDG__ +#pragma endregion +#endif + +#endif // _included_TraceLoggingProvider_h diff --git a/src/native/external/LinuxTracepoints/libeventheader-tracepoint/include/eventheader/eventheader-tracepoint.h b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/include/eventheader/eventheader-tracepoint.h new file mode 100644 index 0000000000000..145faae583924 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/include/eventheader/eventheader-tracepoint.h @@ -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 +#include // struct iovec +#include + +/* +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 diff --git a/src/native/external/LinuxTracepoints/libeventheader-tracepoint/include/eventheader/eventheader.h b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/include/eventheader/eventheader.h new file mode 100644 index 0000000000000..3786be6587d5b --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/include/eventheader/eventheader.h @@ -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 +#include + +#ifdef _WIN32 +#define EVENTHEADER_LITTLE_ENDIAN 1 +#else // _WIN32 +#include +#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 , #include +*/ +#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 , #include +*/ +#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 diff --git a/src/native/external/LinuxTracepoints/libeventheader-tracepoint/samples/CMakeLists.txt b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/samples/CMakeLists.txt new file mode 100644 index 0000000000000..fba44961b9182 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/samples/CMakeLists.txt @@ -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) diff --git a/src/native/external/LinuxTracepoints/libeventheader-tracepoint/samples/TestProviderC.c b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/samples/TestProviderC.c new file mode 100644 index 0000000000000..b51475d3dbf2e --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/samples/TestProviderC.c @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include +#include +#include + +#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 + +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; +} diff --git a/src/native/external/LinuxTracepoints/libeventheader-tracepoint/samples/TestProviderCommon.h b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/samples/TestProviderCommon.h new file mode 100644 index 0000000000000..cc3bf5603fd64 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/samples/TestProviderCommon.h @@ -0,0 +1,504 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include +#include + +#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; +} diff --git a/src/native/external/LinuxTracepoints/libeventheader-tracepoint/samples/TestProviderCpp.cpp b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/samples/TestProviderCpp.cpp new file mode 100644 index 0000000000000..f8bcdf457cf98 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/samples/TestProviderCpp.cpp @@ -0,0 +1,124 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include + +#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 + +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; +} diff --git a/src/native/external/LinuxTracepoints/libeventheader-tracepoint/samples/dynamic-sample.cpp b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/samples/dynamic-sample.cpp new file mode 100644 index 0000000000000..07286bd21e605 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/samples/dynamic-sample.cpp @@ -0,0 +1,171 @@ +#include +#include + +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 on + success or nullptr if out of memory. + - Get a previously-created event set by calling provider.FindSet(), + which returns shared_ptr, or nullptr if not found. + + The shared_ptr 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 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 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("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("StringRange", &CharStrings[0], &CharStrings[2], event_field_format_default); + eb.AddNulTerminatedStringRange("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("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("StringRange", &CharStrings[0], &CharStrings[2], event_field_format_default) + .AddNulTerminatedStringRange("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 +static_assert(EBADF == 9, "EBADF != 9"); diff --git a/src/native/external/LinuxTracepoints/libeventheader-tracepoint/samples/interceptor-sample.cpp b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/samples/interceptor-sample.cpp new file mode 100644 index 0000000000000..8ae09e9918f89 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/samples/interceptor-sample.cpp @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include +#include +#include +#include + +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; +} diff --git a/src/native/external/LinuxTracepoints/libeventheader-tracepoint/samples/sample.cpp b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/samples/sample.cpp new file mode 100644 index 0000000000000..7fa27405cc452 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/samples/sample.cpp @@ -0,0 +1,94 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include + +#include +#include +#include +#include + +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; +} diff --git a/src/native/external/LinuxTracepoints/libeventheader-tracepoint/samples/tracepoint-file.cpp b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/samples/tracepoint-file.cpp new file mode 100644 index 0000000000000..9d0d3e2e7d10b --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/samples/tracepoint-file.cpp @@ -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 +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +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 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(__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(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(__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; +} diff --git a/src/native/external/LinuxTracepoints/libeventheader-tracepoint/samples/tracepoint-sample.cpp b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/samples/tracepoint-sample.cpp new file mode 100644 index 0000000000000..95b923d307f77 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/samples/tracepoint-sample.cpp @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include +#include +#include +#include + +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; +} diff --git a/src/native/external/LinuxTracepoints/libeventheader-tracepoint/src/CMakeLists.txt b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/src/CMakeLists.txt new file mode 100644 index 0000000000000..a90cc8c8a08c4 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/src/CMakeLists.txt @@ -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") diff --git a/src/native/external/LinuxTracepoints/libeventheader-tracepoint/src/eventheader-tracepoint.c b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/src/eventheader-tracepoint.c new file mode 100644 index 0000000000000..ce6310b8cfcc6 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/src/eventheader-tracepoint.c @@ -0,0 +1,193 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 diff --git a/src/native/external/LinuxTracepoints/libeventheader-tracepoint/src/eventheader-tracepointConfig.cmake.in b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/src/eventheader-tracepointConfig.cmake.in new file mode 100644 index 0000000000000..e5c3eececfce7 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libeventheader-tracepoint/src/eventheader-tracepointConfig.cmake.in @@ -0,0 +1,2 @@ +@PACKAGE_INIT@ +include("${CMAKE_CURRENT_LIST_DIR}/eventheader-tracepointTargets.cmake") diff --git a/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/CMakeLists.txt b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/CMakeLists.txt new file mode 100644 index 0000000000000..ac3e94a6c6557 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/CMakeLists.txt @@ -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() diff --git a/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/README.md b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/README.md new file mode 100644 index 0000000000000..ecb34bb73965a --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/README.md @@ -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. diff --git a/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/include/tracepoint/TracepointCache.h b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/include/tracepoint/TracepointCache.h new file mode 100644 index 0000000000000..7d2a7642846dd --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/include/tracepoint/TracepointCache.h @@ -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 +#include +#include +#include +#include + +#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 SystemAndFormat; // = "SystemName\nFormatFileContents" + tracepoint_decode::PerfEventMetadata Metadata; // Points into SystemAndFormat + std::unique_ptr Registration; + + CacheVal(CacheVal const&) = delete; + void operator=(CacheVal const&) = delete; + ~CacheVal(); + + CacheVal( + std::vector&& systemAndFormat, + tracepoint_decode::PerfEventMetadata&& metadata, + std::unique_ptr 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&& systemAndFormat, + size_t systemNameSize, + bool longSize64, + std::unique_ptr registration) noexcept; + + std::unordered_map m_byId; + std::unordered_map m_byName; + int8_t m_commonTypeOffset; // -1 = unset + uint8_t m_commonTypeSize; // 0 = unset + }; +} +// namespace tracepoint_control + +#endif // _included_TracepointCache_h diff --git a/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/include/tracepoint/TracepointName.h b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/include/tracepoint/TracepointName.h new file mode 100644 index 0000000000000..675fa1739b023 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/include/tracepoint/TracepointName.h @@ -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 + +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 diff --git a/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/include/tracepoint/TracepointPath.h b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/include/tracepoint/TracepointPath.h new file mode 100644 index 0000000000000..218c63031c0df --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/include/tracepoint/TracepointPath.h @@ -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 +#include + +#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& 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& dest, + std::string_view systemName, + std::string_view eventName) noexcept; +} +// namespace tracepoint_control + +#endif // _included_TracepointPath_h diff --git a/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/include/tracepoint/TracepointSession.h b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/include/tracepoint/TracepointSession.h new file mode 100644 index 0000000000000..65645d99423a1 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/include/tracepoint/TracepointSession.h @@ -0,0 +1,1410 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +/* +TracepointSession class that manages a tracepoint collection session. +*/ + +#pragma once +#ifndef _included_TracepointSession_h +#define _included_TracepointSession_h + +#include "TracepointName.h" +#include +#include +#include +#include +#include + +#include +#include +#include + +#include // sigset_t + +#ifndef _In_opt_ +#define _In_opt_ +#endif +#ifndef _In_z_ +#define _In_z_ +#endif +#ifndef _In_reads_ +#define _In_reads_(size) +#endif +#ifndef _In_reads_opt_ +#define _In_reads_opt_(size) +#endif +#ifndef _Inout_ +#define _Inout_ +#endif +#ifndef _Out_opt_ +#define _Out_opt_ +#endif +#ifndef _Out_writes_ +#define _Out_writes_(size) +#endif + +// Forward declarations: +struct pollfd; // From poll.h +struct timespec; // From time.h +namespace tracepoint_decode +{ + class PerfDataFileWriter; // From tracepoint/PerfDataFileWriter.h. +} + +namespace tracepoint_control +{ + /* + Mode to use for a tracepoint collection session: + + - Circular: Used for "flight recorder" scenarios. Events are collected + into fixed-size buffers (one buffer per CPU). When a buffer is full, new + events overwrite old events. At any point, you can pause collection, + enumerate the contents of the buffer, and resume collection. Events + received while collection is paused will be lost. + + For example, you can record information about what is happening on the + system into memory, and then if a program crashes, you save the data to + disk so you can discover what was happening on the system in the period + leading up to the crash. + + - RealTime: Used for logging/tracing scenarios. Events are collected into + fixed-size buffers (one buffer per CPU). When a buffer is full, events + will be lost. At any point, you can enumerate events from the buffer, + consuming them to make room for new events (no pause required). + */ + enum class TracepointSessionMode : unsigned char + { + /* + Buffers will be managed as circular: + + - If buffer is full, new events will overwrite old events. + - Natural event enumeration order is newest-to-oldest (per buffer). + - Procedure for reading data: pause buffer, enumerate events, unpause. + (Events arriving while buffer is paused will be lost.) + - Cannot be notified when data becomes available. + */ + Circular, + + /* + Buffers will be managed as realtime: + + - If buffer is full, new events will be lost. + - Natural event enumeration order is oldest-to-newest (per buffer). + - Procedure for reading data: enumerate events, marking the events as + consumed to make room for new events. + - Can use WaitForWakeup() or poll() to wait for data to become available. + */ + RealTime, + }; + + /* + Enablement status of a tracepoint that has been added to a session. + */ + enum class TracepointEnableState : unsigned char + { + /* + An error occurred while trying to enable/disable the tracepoint. + Actual status is unknown. + */ + Unknown, + + /* + Tracepoint is enabled. + */ + Enabled, + + /* + Tracepoint is disabled. + */ + Disabled, + }; + + /* + Configuration settings for a tracepoint collection session. + + Required settings are specified as constructor parameters. + Optional settings are set by calling methods. + + Example: + + TracepointCache cache; + TracepointSession session( + cache, + TracepointSessionOptions(TracepointSessionMode::RealTime, 65536) // Required + .WakeupWatermark(32768) // Optional + ); + */ + class TracepointSessionOptions + { + friend class TracepointSession; + + public: + + /* + The flags that are set in the default value of the SampleType property: + + | PERF_SAMPLE_IDENTIFIER + | PERF_SAMPLE_TID + | PERF_SAMPLE_TIME + | PERF_SAMPLE_CPU + | PERF_SAMPLE_RAW + */ + static constexpr auto SampleTypeDefault = 0x10486u; + + /* + The flags that are supported for use with the SampleType property: + + | PERF_SAMPLE_IDENTIFIER + | PERF_SAMPLE_IP + | PERF_SAMPLE_TID + | PERF_SAMPLE_TIME + | PERF_SAMPLE_ADDR + | PERF_SAMPLE_ID + | PERF_SAMPLE_STREAM_ID + | PERF_SAMPLE_CPU + | PERF_SAMPLE_PERIOD + | PERF_SAMPLE_CALLCHAIN + | PERF_SAMPLE_RAW + */ + static constexpr auto SampleTypeSupported = 0x107EFu; + + /* + Initializes a TracepointSessionOptions to configure a session with the + specified mode and buffer size. + + - mode: controls whether the buffer is managed as Circular or RealTime. + + - perCpuBufferSize: specifies the size of each buffer in bytes. This value will + be rounded up to a power of 2 that is equal to or greater than the page size. + Size may not exceed 2GB. + Note that the session will allocate one buffer for each CPU. + */ + constexpr + TracepointSessionOptions( + TracepointSessionMode mode, + uint32_t perCpuBufferSize) noexcept + : m_cpuBufferSizes(nullptr) + , m_cpuBufferSizesCount(UINT32_MAX) + , m_perCpuBufferSize(perCpuBufferSize) + , m_mode(mode) + , m_wakeupUseWatermark(true) + , m_wakeupValue(0) + , m_sampleType(SampleTypeDefault) + { + return; + } + + /* + Advanced scenarios: Initializes a TracepointSessionOptions to configure a + session with the specified mode, using a specified buffer size for each CPU. + + - mode: controls whether the buffer is managed as Circular or RealTime. + + - cpuBufferSizes: Specifies the sizes for the buffers, in bytes. This must not be + NULL. If a size is 0, no collection will be performed on the corresponding CPU. + Other values will be rounded up to a power of 2 that is equal to or greater + than the page size. Size may not exceed 2GB. + + - cpuBufferSizesCount: The number of values provided in cpuBufferSizes. This must + be greater than 0. If this is less than the number of CPUs, no collection will + be performed on the remaining CPUs. + */ + constexpr + TracepointSessionOptions( + TracepointSessionMode mode, + _In_reads_(cpuBufferSizesCount) uint32_t const* cpuBufferSizes, + uint32_t cpuBufferSizesCount) noexcept + : m_cpuBufferSizes(cpuBufferSizes) + , m_cpuBufferSizesCount(cpuBufferSizesCount) + , m_perCpuBufferSize(0) + , m_mode(mode) + , m_wakeupUseWatermark(true) + , m_wakeupValue(0) + , m_sampleType(SampleTypeDefault) + { + return; + } + + /* + Flags indicating what information should be recorded for each tracepoint. + + Flags use the perf_event_sample_format values defined in + or . + + The following flags are enabled by default (SampleTypeDefault): + + | PERF_SAMPLE_IDENTIFIER + | PERF_SAMPLE_TID + | PERF_SAMPLE_TIME + | PERF_SAMPLE_CPU + | PERF_SAMPLE_RAW + + The following flags are supported (SampleTypeSupported): + + | PERF_SAMPLE_IDENTIFIER + | PERF_SAMPLE_IP + | PERF_SAMPLE_TID + | PERF_SAMPLE_TIME + | PERF_SAMPLE_ADDR + | PERF_SAMPLE_ID + | PERF_SAMPLE_STREAM_ID + | PERF_SAMPLE_CPU + | PERF_SAMPLE_PERIOD + | PERF_SAMPLE_CALLCHAIN + | PERF_SAMPLE_RAW + + Note that you'll almost always want to include PERF_SAMPLE_RAW since that + is the event's raw data (the event field values). + */ + constexpr TracepointSessionOptions& + SampleType(uint32_t sampleType) noexcept + { + m_sampleType = sampleType & SampleTypeSupported; + return *this; + } + + /* + For realtime sessions only: sets the number of bytes of unconsumed event + data (counting both SAMPLE and non-SAMPLE events) that a realtime buffer + must contain to trigger wakeup (see WaitForWakeup). + + The default value is WakeupWatermark(0). + + Note that wakeup conditions are evaluated per-buffer. For example, if 3 + buffers each contain 32760 bytes of pending data, none of them would + trigger a WakeupWatermark(32768) condition. + */ + constexpr TracepointSessionOptions& + WakeupWatermark(uint32_t wakeupWatermark) noexcept + { + m_wakeupUseWatermark = true; + m_wakeupValue = wakeupWatermark; + return *this; + } + + private: + + uint32_t const* m_cpuBufferSizes; + uint32_t const m_cpuBufferSizesCount; + uint32_t const m_perCpuBufferSize; + TracepointSessionMode const m_mode; + bool m_wakeupUseWatermark; + uint32_t m_wakeupValue; + uint32_t m_sampleType; + }; + + /* + Records a range of timestamps. If Last > First, the range is invalid. + Default-initializes to an invalid range (First = UINT64_MAX, Last = 0). + */ + struct TracepointTimestampRange + { + uint64_t First = UINT64_MAX; + uint64_t Last = 0; + }; + + /* + Configuration settings for TracepointSession::SavePerfDataFile. + + Example: + + error = session.SavePerfDataFile( + "perf.data", + TracepointSavePerfDataFileOptions().OpenMode(S_IRUSR | S_IWUSR)); + */ + class TracepointSavePerfDataFileOptions + { + friend class TracepointSession; + + public: + + /* + Initializes a TracepointSavePerfDataFileOptions to use the default settings. + + - OpenMode = -1 (use default file permissions based on process umask). + - TimestampFilter = 0..MAX_UINT64 (no timestamp filtering). + - TimestampWrittenRange = nullptr (do not return timestamp range). + */ + constexpr + TracepointSavePerfDataFileOptions() noexcept + : m_openMode(-1) + , m_filterRange{ 0, UINT64_MAX } + , m_timestampWrittenRange(nullptr) + { + return; + } + + /* + Sets the permissions mode to use when creating the perf.data file. The file will + be created as: open(perfDataFileName, O_CREAT|O_WRONLY|O_TRUNC|O_CLOEXEC, OpenMode). + + Default value is -1 (use default file permissions based on process umask). + This can be one or more of S_IRUSR, S_IWUSR, S_IRGRP, S_IWGRP, etc. + */ + constexpr TracepointSavePerfDataFileOptions& + OpenMode(int openMode) noexcept + { + m_openMode = openMode; + return *this; + } + + /* + Sets the timestamp filter. Only sample events where + timeMin <= event.timestamp <= timeMax will be written to the file. (Timestamp on + non-sample events will be ignored.) + + Default value is 0..UINT64_MAX (no timestamp filter). + + For example, to write only events since the last Save: + + uint64_t lastTimestampWritten = 0; + + // First save does not filter-out any events based on timestamp, + // and records the timestamp of the last event for use in next save: + session.SavePerfDataFile("perf.data.0", TracepointSavePerfDataFileOptions() + .TimestampFilter(lastTimestampWritten) // = 0, so no timestamp filter. + .TimestampWrittenRange(nullptr, &lastTimestampWritten)); + + ... + + // Subsequent saves use last event timestamp from previous save, and + // update that timestamp for use in subsequent saves: + session.SavePerfDataFile("perf.data.1", TracepointSavePerfDataFileOptions() + .TimestampFilter(lastTimestampWritten) // filter out old events + .TimestampWrittenRange(nullptr, &lastTimestampWritten)); + + Note that in this pattern, the last event saved to file N will also be included + in file N+1. If you want to avoid that, use + TimestampFilter(lastTimestampWritten + 1), though that risks missing new events + with timestamp exactly equal to lastTimestampWritten. + */ + constexpr TracepointSavePerfDataFileOptions& + TimestampFilter(uint64_t filterMin, uint64_t filterMax = UINT64_MAX) noexcept + { + m_filterRange = { filterMin, filterMax }; + return *this; + } + + /* + Sets the variable that will receive the timestamp range of the events that were + written to the file. + + Default value is nullptr (do not return timestamp range). + */ + constexpr TracepointSavePerfDataFileOptions& + TimestampWrittenRange(_Out_opt_ TracepointTimestampRange* range = nullptr) noexcept + { + m_timestampWrittenRange = range; + return *this; + } + + private: + + int m_openMode; + TracepointTimestampRange m_filterRange; + TracepointTimestampRange* m_timestampWrittenRange; + }; + + /* + Information about a tracepoint that has been added to a session. + */ + class TracepointInfo + { + // Note: Implemented as a pimpl. + // - I want the constructor to be private on the type that the user sees. + // - Constructor on concrete type needs to be public so that it can be + // constructed by a container's emplace method. + // - Therefore the concrete type needs to be a private type with a public + // constructor. + + friend class TracepointSession; + + ~TracepointInfo(); + TracepointInfo() noexcept; + + public: + + TracepointInfo(TracepointInfo const&) = delete; + void operator=(TracepointInfo const&) = delete; + + tracepoint_decode::PerfEventMetadata const& + Metadata() const noexcept; + + tracepoint_decode::PerfEventDesc const& + EventDesc() const noexcept; + + TracepointEnableState + EnableState() const noexcept; + + _Success_(return == 0) int + GetEventCount(_Out_ uint64_t* value) const noexcept; + }; + + /* + Manages a tracepoint collection session. + + Basic usage: + + TracepointCache cache; // May be shared by multiple sessions. + TracepointSession session( + cache, // The metadata cache to use for this session. + TracepointSessionMode::RealTime, // Collection mode: RealTime or Circular. + 65536); // Size of each buffer (one buffer per CPU). + + error = session.EnableTracepoint(TracepointName("user_events", "MyFirstTracepoint")); + if (error != 0) abort(); // TODO: handle error. + + error = session.EnableTracepoint(TracepointName("user_events:MySecondTracepoint")); + if (error != 0) abort(); // TODO: handle error. + + for (;;) + { + // Wait until one or more of the buffers reaches 32768 bytes of event data. + error = session.WaitForWakeup(); + if (error != 0) abort(); // TODO: handle error. (Don't get into a busy loop if waiting fails!) + + error = session.EnumerateSampleEventsUnordered( + [](PerfSampleEventInfo const& event) + { + // This code will run once for each SAMPLE event. + // It should record or process the event's data. + return 0; // If we return an error, enumeration will stop. + }); + if (error != 0) abort(); // TODO: handle error. + } + */ + class TracepointSession + { + friend class TracepointInfo; + + struct ReadFormat; // Forward declaration + + class unique_fd + { + int m_fd; + public: + ~unique_fd(); + unique_fd() noexcept; + explicit unique_fd(int fd) noexcept; + unique_fd(unique_fd&&) noexcept; + unique_fd& operator=(unique_fd&&) noexcept; + explicit operator bool() const noexcept; + void reset() noexcept; + void reset(int fd) noexcept; + int get() const noexcept; + }; + + class unique_mmap + { + void* m_addr; + size_t m_size; + public: + ~unique_mmap(); + unique_mmap() noexcept; + unique_mmap(void* addr, size_t size) noexcept; + unique_mmap(unique_mmap&&) noexcept; + unique_mmap& operator=(unique_mmap&&) noexcept; + explicit operator bool() const noexcept; + void reset() noexcept; + void reset(void* addr, size_t size) noexcept; + void* get() const noexcept; + size_t get_size() const noexcept; + }; + + struct TracepointInfoImpl : TracepointInfo + { + tracepoint_decode::PerfEventDesc const m_eventDesc; + std::unique_ptr const m_eventDescStorage; + std::unique_ptr const m_bufferFiles; // size is BufferFilesCount + unsigned const m_bufferFilesCount; + TracepointEnableState m_enableState; + + TracepointInfoImpl(TracepointInfoImpl const&) = delete; + void operator=(TracepointInfoImpl const&) = delete; + ~TracepointInfoImpl(); + TracepointInfoImpl( + tracepoint_decode::PerfEventDesc const& eventDesc, + std::unique_ptr eventDescStorage, + std::unique_ptr bufferFiles, + unsigned bufferFilesCount) noexcept; + + // read(m_bufferFiles[i], data, sizeof(ReadFormat)). + _Success_(return == 0) int + Read(unsigned index, _Out_ ReadFormat* data) const noexcept; + + // Calls read() on each file, returns sum of the value fields. + _Success_(return == 0) int + GetEventCountImpl(_Out_ uint64_t* value) const noexcept; + }; + + struct BufferInfo + { + unique_mmap Mmap; // When non-empty: Mmap.get_size() = Size + PAGE_SIZE + uint32_t Size; // Set whether or not Mmap is empty. + uint8_t const* Data; // NULL if Mmap is empty, else Mmap.ptr + PAGE_SIZE. + size_t DataPos; + size_t DataTail; + uint64_t DataHead64; + + BufferInfo(BufferInfo const&) = delete; + void operator=(BufferInfo const&) = delete; + ~BufferInfo(); + BufferInfo() noexcept; + }; + + struct TracepointBookmark + { + uint64_t Timestamp; + uint16_t BufferIndex; + uint16_t RecordSize; + uint32_t RecordBufferPos; + + TracepointBookmark( + uint64_t timestamp, + uint16_t bufferIndex, + uint16_t recordSize, + uint32_t recordBufferPos) noexcept; + }; + + class UnorderedEnumerator + { + TracepointSession& m_session; + uint32_t const m_bufferIndex; + + public: + + UnorderedEnumerator(UnorderedEnumerator const&) = delete; + void operator=(UnorderedEnumerator const&) = delete; + ~UnorderedEnumerator(); + + UnorderedEnumerator( + TracepointSession& session, + uint32_t bufferIndex) noexcept; + + bool + MoveNext() noexcept; + }; + + class OrderedEnumerator + { + TracepointSession& m_session; + bool m_needsCleanup; + size_t m_index; + + public: + + OrderedEnumerator(OrderedEnumerator const&) = delete; + void operator=(OrderedEnumerator const&) = delete; + ~OrderedEnumerator(); + + explicit + OrderedEnumerator(TracepointSession& session) noexcept; + + _Success_(return == 0) int + LoadAndSort() noexcept; + + bool + MoveNext() noexcept; + }; + + public: + + class TracepointInfoRange; // Forward declaration + + class TracepointInfoIterator + { + friend class TracepointSession; + friend class TracepointInfoRange; + using InnerItTy = std::unordered_map::const_iterator; + InnerItTy m_it; + + explicit + TracepointInfoIterator(InnerItTy it) noexcept; + + public: + + using difference_type = std::ptrdiff_t; + using value_type = TracepointInfo; + using pointer = TracepointInfo const*; + using reference = TracepointInfo const&; + using iterator_category = std::forward_iterator_tag; + + TracepointInfoIterator() noexcept; + TracepointInfoIterator& operator++() noexcept; + TracepointInfoIterator operator++(int) noexcept; + pointer operator->() const noexcept; + reference operator*() const noexcept; + bool operator==(TracepointInfoIterator other) const noexcept; + bool operator!=(TracepointInfoIterator other) const noexcept; + }; + + class TracepointInfoRange + { + friend class TracepointSession; + using RangeTy = std::unordered_map; + RangeTy const& m_range; + + explicit + TracepointInfoRange(RangeTy const& range) noexcept; + + public: + + TracepointInfoIterator begin() const noexcept; + TracepointInfoIterator end() const noexcept; + }; + + TracepointSession(TracepointSession const&) = delete; + void operator=(TracepointSession const&) = delete; + ~TracepointSession(); + + /* + Constructs a session using defaults for advanced options. + May throw std::bad_alloc. + + - cache: The TracepointCache that this session will use to locate metadata + (format) information about tracepoints. Multiple sessions may share a + cache. + + - mode: controls whether the buffer is managed as Circular or RealTime. + + - perCpuBufferSize: specifies the size of each buffer in bytes. This value will + be rounded up to a power of 2 that is equal to or greater than the page size. + Size may not exceed 2GB. + Note that the session will allocate one buffer for each CPU. + + Example: + + TracepointCache cache; + TracepointSession session( + cache, // The metadata cache to use for this session. + TracepointSessionMode::RealTime, // Collection mode: RealTime or Circular. + 65536); // Size of each buffer (one buffer per CPU). + */ + TracepointSession( + TracepointCache& cache, + TracepointSessionMode mode, + uint32_t perCpuBufferSize) noexcept(false); + + /* + Constructs a session using TracepointSessionOptions to set advanced options. + May throw std::bad_alloc. + + - cache: The TracepointCache that this session will use to locate metadata + (format) information about tracepoints. Multiple sessions may share a + cache. + + - options: Configuration settings that this session will use. + + Example: + + TracepointCache cache; + TracepointSession session( + cache, // The metadata cache to use for this session. + TracepointSessionOptions(TracepointSessionMode::RealTime, 65536) // Required settings + .SampleType(PERF_SAMPLE_TIME | PERF_SAMPLE_RAW) // Optional setting + .WakeupWatermark(32768)); // Optional setting + */ + TracepointSession( + TracepointCache& cache, + TracepointSessionOptions const& options) noexcept(false); + + /* + Returns the tracepoint cache associated with this session. + */ + TracepointCache& + Cache() const noexcept; + + /* + Returns the mode that was specified at construction. + */ + TracepointSessionMode + Mode() const noexcept; + + /* + Returns session information, e.g. clockid and clock offset. + At present, clockid is CLOCK_MONOTONIC_RAW and the timestamp offsets are + captured when this TracepointSession object is constructed + */ + tracepoint_decode::PerfEventSessionInfo const& + SessionInfo() const noexcept; + + /* + Returns true if Mode() == Realtime, false if Mode() == Circular. + */ + bool + IsRealtime() const noexcept; + + /* + Returns the size (in bytes) of the specified buffer. + Returns 0 if collection is disabled for the specified buffer. + Requires: bufferIndex < BufferCount(). + */ + uint32_t + BufferSize(unsigned bufferIndex = 0) const noexcept; + + /* + Returns the number of buffers used for the session. + Usually this is the number of CPUs. + */ + uint32_t + BufferCount() const noexcept; + + /* + Returns the number of SAMPLE events that have been enumerated by this + session. + */ + uint64_t + SampleEventCount() const noexcept; + + /* + Returns the number of lost events that have been enumerated by this + session. Events can be lost due to: + + - Memory allocation failure during buffer enumeration. + - Event received while session is paused (circular mode only). + - Event received while buffer is full (realtime mode only). + */ + uint64_t + LostEventCount() const noexcept; + + /* + Returns the number of corrupt events that have been enumerated by this + session. An event is detected as corrupt if the event's size is too + small for the event's expected SampleType. + */ + uint64_t + CorruptEventCount() const noexcept; + + /* + Returns the number of times buffer corruption has been detected by this + session. The buffer is detected as corrupt if the buffer header has + invalid values or if an event's size is invalid. Buffer corruption + generally causes the buffer's remaining contents to be skipped. + */ + uint64_t + CorruptBufferCount() const noexcept; + + /* + Clears the list of tracepoints we are listening to. + Frees all buffers. + */ + void + Clear() noexcept; + + /* + Disables collection of the specified tracepoint. + + Note that ID is from the event's common_type field and is not the PERF_SAMPLE_ID + or PERF_SAMPLE_IDENTIFIER value. + + - Uses Cache().FindById(id) to look up the specified tracepoint. + - If that succeeds and the specified tracepoint is in the list of session + tracepoints, disables the tracepoint. + + Note that the tracepoint remains in the list of session tracepoints, but is set + to the "disabled" state. + + Returns 0 for success, errno for error. + + Errors include but are not limited to: + - ENOENT: tracefs metadata not found (tracepoint may not be registered yet) + or tracepoint is not in the list of session tracepoints. + - ENOTSUP: unable to find tracefs mount point. + - EPERM: access denied to tracefs metadata. + - ENODATA: unable to parse tracefs metadata. + - ENOMEM: memory allocation failed. + */ + _Success_(return == 0) int + DisableTracepoint(unsigned id) noexcept; + + /* + Disables collection of the specified tracepoint. + + - Uses Cache().FindOrAddFromSystem(name) to look up the specified tracepoint. + - If that succeeds and the specified tracepoint is in the list of session + tracepoints, disables the tracepoint. + + Note that the tracepoint remains in the list of session tracepoints, but is set + to the "disabled" state. + + Returns 0 for success, errno for error. + + Errors include but are not limited to: + - ENOENT: tracefs metadata not found (tracepoint may not be registered yet). + - ENOTSUP: unable to find tracefs mount point. + - EPERM: access denied to tracefs metadata. + - ENODATA: unable to parse tracefs metadata. + - ENOMEM: memory allocation failed. + */ + _Success_(return == 0) int + DisableTracepoint(TracepointName name) noexcept; + + /* + Enables collection of the specified tracepoint. + + Note that ID is from the event's common_type field and is not the PERF_SAMPLE_ID + or PERF_SAMPLE_IDENTIFIER value. + + - Uses Cache().FindById(name) to look up the specified tracepoint. + - If that succeeds, enables the tracepoint (adding it to the list of session + tracepoints if it is not already in the list). + + Returns 0 for success, errno for error. + Errors include but are not limited to: + - ENOENT: tracefs metadata not found (tracepoint may not be registered yet). + - ENOTSUP: unable to find tracefs mount point. + - EPERM: access denied to tracefs metadata. + - ENODATA: unable to parse tracefs metadata. + - ENOMEM: memory allocation failed. + */ + _Success_(return == 0) int + EnableTracepoint(unsigned id) noexcept; + + /* + Enables collection of the specified tracepoint. + + - Uses Cache().FindOrAddFromSystem(name) to look up the specified tracepoint. + - If that succeeds, enables the tracepoint (adding it to the list of session + tracepoints if it is not already in the list). + + Returns 0 for success, errno for error. + Errors include but are not limited to: + - ENOENT: tracefs metadata not found (tracepoint may not be registered yet). + - ENOTSUP: unable to find tracefs mount point. + - EPERM: access denied to tracefs metadata. + - ENODATA: unable to parse tracefs metadata. + - ENOMEM: memory allocation failed. + */ + _Success_(return == 0) int + EnableTracepoint(TracepointName name) noexcept; + + /* + Returns a range for enumerating the tracepoints in the session (includes + both enabled and disabled tracepoints). Returned range is equivalent to + TracepointInfoBegin()..TracepointInfoEnd(). + */ + TracepointInfoRange + TracepointInfos() const noexcept; + + /* + Returns the begin iterator of a range for enumerating the tracepoints in + the session. + */ + TracepointInfoIterator + TracepointInfosBegin() const noexcept; + + /* + Returns the end iterator of a range for enumerating the tracepoints in + the session. + */ + TracepointInfoIterator + TracepointInfosEnd() const noexcept; + + /* + Returns an iterator referencing a tracepoint in this session. Returns + TracepointInfoEnd() if the specified tracepoint is not in this session. + + Note that ID is from the event's common_type field and is not the PERF_SAMPLE_ID + or PERF_SAMPLE_IDENTIFIER value. + */ + TracepointInfoIterator + FindTracepointInfo(unsigned id) const noexcept; + + /* + Returns an iterator referencing a tracepoint in this session. Returns + TracepointInfoEnd() if the specified tracepoint is not in this session. + */ + TracepointInfoIterator + FindTracepointInfo(TracepointName name) const noexcept; + + /* + For realtime sessions only: Waits for the wakeup condition using + ppoll(bufferFiles, bufferCount, timeout, sigmask). The wakeup condition + is configured by calling WakeupWatermark on a config before passing the + config to the session's constructor. + + - timeout: Maximum time to wait. NULL means wait forever. + - sigmask: Signal mask to apply before waiting. NULL means don't mask. + - activeCount: On success, receives the number of buffers that meet the + wakeup condition, or 0 if wait ended due to a timeout or a signal. + + Returns EPERM if the session is not realtime. + + Returns EPERM if the session is inactive. After construction and after + Clear(), the session will be inactive until a tracepoint is added. + + Note that wakeup conditions are evaluated per-buffer. For example, if 3 + buffers each contain 32760 bytes of pending data, none of them would + trigger a WakeupWatermark(32768) condition. + */ + _Success_(return == 0) int + WaitForWakeup( + timespec const* timeout = nullptr, + sigset_t const* sigmask = nullptr, + _Out_opt_ int* pActiveCount = nullptr) noexcept; + + /* + Advanced scenarios: Returns the file descriptors used for the buffers + of the session. The returned file descriptors may be used for poll() + but should not be read-from, written-to, closed, etc. This may be + useful if you want to use a single thread to poll for events from + multiple sessions or to poll for both events and some other condition. + + Returns EPERM if the session is inactive. After construction and after + Clear(), the session will be inactive until a tracepoint is added. + + Most users should use WaitForWakeup() instead of GetBufferFiles(). + + Note that if a buffer is disabled (buffer size was set to 0), the + corresponding file descriptor will be -1. + */ + _Success_(return == 0) int + GetBufferFiles( + _Out_writes_(BufferCount()) int* pBufferFiles) const noexcept; + + /* + Creates a perf.data-format file and writes all pending data from the + current session's buffers to the file. This can be done for all session + types but is normally only used with circular sessions. + + This method does the following: + + - Open a new file. + - Write a PERF_RECORD_FINISHED_INIT record to the file. + - Flush buffers to the file, adding EventDesc records as needed. + - Set system information headers as if by SetWriterHeaders(). + - Close the file. + + File is created as: + + open(perfDataFileName, O_CREAT|O_WRONLY|O_TRUNC|O_CLOEXEC, options.OpenMode()); + + Returns: int error code (errno), or 0 for success. + + *** Circular session flush behavior *** + + For each buffer (usually one per CPU): + + - Pause collection into the buffer. + - Write buffer's data to the file. + - Unpause the buffer. + + Note that events are lost if they arrive while the buffer is paused. The lost + event count indicates how many events were lost during previous pauses that would + have been part of a enumeration if there had been no pauses. It does not include + the count of events that were lost due to the current enumeration's pause (those + will show up after a subsequent enumeration). + + *** Realtime session flush behavior *** + + For each buffer (usually one per CPU): + + - Write buffer's pending (unconsumed) events to the file. + - Mark the enumerated events as consumed, making room for subsequent events. + + Note that SavePerfDataFile() is not normally used for realtime sessions because + it only flushes the data in the buffers at the time you call SavePerfDataFile(). + Instead, you would normally want to use FlushToWriter() to flush the buffers + multiple times over a long collection period. + + Note that events are lost if they arrive while the buffer is full. The lost + event count indicates how many events were lost during previous periods when + the buffer was full. It does not include the count of events that were lost + due to the buffer being full at the start of the current enumeration (those will + show up after a subsequent enumeration). + */ + _Success_(return == 0) int + SavePerfDataFile( + _In_z_ char const* perfDataFileName, + TracepointSavePerfDataFileOptions const& options = TracepointSavePerfDataFileOptions()) noexcept; + + /* + Writes all pending data from the current session's buffers to the specified + writer. Expands writtenRange to reflect the range of the timestamps seen. + + This can be done for all session types but is normally only used with realtime + sessions. + + Typical usage: + + - Create a range to track timestamp: TracepointTimestampRange writtenRange{}; + - Create a writer: PerfDataFileWriter writer; + - Call writer.Create(...) to open the file. + - Write system-state events, if any (e.g. non-sample events like + PERF_RECORD_MMAP, PERF_RECORD_COMM, PERF_RECORD_ID_INDEX, + PERF_RECORD_THREAD_MAP, PERF_RECORD_CPU_MAP). + - Call writer.WriteFinishedInit() to write a PERF_RECORD_FINISHED_INIT record. + - Call FlushToWriter(writer, &writtenRange) to write the sample events as they + arrive (e.g. each time session.WaitForWakeup() returns) and call + writer.WriteFinishedRound() each time flush is complete. + - Call SetWriterHeaders(writer, &writtenRange) to write system information headers. + - Call writer.FinalizeAndClose() to close the file. + + Returns: int error code (errno), or 0 for success. + + *** Circular session behavior *** + + For each buffer (usually one per CPU): + + - Pause collection into the buffer. + - Write buffer's data to the file. + - Unpause the buffer. + + Note that events are lost if they arrive while the buffer is paused. The lost + event count indicates how many events were lost during previous pauses that would + have been part of a enumeration if there had been no pauses. It does not include + the count of events that were lost due to the current enumeration's pause (those + will show up after a subsequent enumeration). + + *** Realtime session behavior *** + + For each buffer (usually one per CPU): + + - Write buffer's pending (unconsumed) events to the file. + - Mark the enumerated events as consumed, making room for subsequent events. + + Note that events are lost if they arrive while the buffer is full. The lost + event count indicates how many events were lost during previous periods when + the buffer was full. It does not include the count of events that were lost + due to the buffer being full at the start of the current enumeration (those will + show up after a subsequent enumeration). + */ + _Success_(return == 0) int + FlushToWriter( + tracepoint_decode::PerfDataFileWriter& writer, + _Inout_ TracepointTimestampRange* writtenRange, + TracepointTimestampRange filterRange = { 0, UINT64_MAX }) noexcept; + + /* + Sets the headers in the specified writer based on the session's configuration. + At present, this sets the following headers: + + - SetUtsNameHeaders() from uname. + - SetNrCpusHeader() from sysconf _SC_NPROCESSORS_CONF and _SC_NPROCESSORS_ONLN. + - SetSessionInfoHeaders() using data from SessionInfo(). + - SetSampleTimeHeader() if writtenRange is non-null. + */ + _Success_(return == 0) int + SetWriterHeaders( + tracepoint_decode::PerfDataFileWriter& writer, + _In_opt_ TracepointTimestampRange const* writtenRange) noexcept; + + /* + For each PERF_RECORD_SAMPLE record in the session's buffers, in timestamp + order, invoke: + + int error = eventInfoCallback(eventInfo, args...); + + - eventInfoCallback: Callable object (e.g. a function pointer or a lambda) + to invoke for each event. + + This callback should return an int (0 for success, errno for error). If + eventInfoCallback returns a nonzero value then EnumerateEventsUnordered + will immediately stop and return the specified error value. + + This callback should take a PerfSampleEventInfo const& as its first + parameter. + + The args... (if any) are from the args... of the call to + EnumerateEventsUnordered(eventInfoCallback, args...). + + - args...: optional additional parameters to be passed to eventInfoCallback. + + Returns: int error code (errno), or 0 for success. + + Requires: + + - The session's SampleType() must include PERF_SAMPLE_TIME so that the events + can be sorted based on timestamp. + + Examples: + + // Use a callback function pointer and callback context: + error = session.EnumerateSampleEvents(functionPointer, functionContext); + + // Use a lambda: + error = session.EnumerateSampleEvents( + [&](PerfSampleEventInfo const& event) -> int + { + ... + return 0; + }); + + Events will be sorted based on timestamp before invoking the callback. If your + callback does not need events to be sorted based on timestamp, use + EnumerateSampleEventsUnordered to avoid the sorting overhead. + + Note that the eventInfo provided to eventInfoCallback will contain pointers + into the trace buffers. The pointers will become invalid after eventInfoCallback + returns. Any data that you need to use after that point must be copied. + + Note that this method does not throw any of its own exceptions, but it may + exit via exception if your eventInfoCallback(...) throws an exception. + + *** Circular session behavior *** + + - Pause collection into all buffers. + - Scan all buffers to find events. + - Sort the events based on timestamp. + - Invoke eventInfoCallback(...) for each event. + - Unpause all buffers. + + Note that events are lost if they arrive while the buffer is paused. The lost + event count indicates how many events were lost during previous pauses that would + have been part of an enumeration if there had been no pauses. It does not include + the count of events that were lost due to the current enumeration's pause (those + will show up after a subsequent enumeration). + + *** Realtime session behavior *** + + - Scan all buffers to find events. + - Sort the events based on timestamp. + - Invoke eventInfoCallback for each event. + - Mark the enumerated events as consumed, making room for subsequent events. + + Note that events are lost if they arrive while the buffer is full. The lost + event count indicates how many events were lost during previous periods when + the buffer was full. It does not include the count of events that were lost + due to the buffer being full at the start of the current enumeration (those will + show up after a subsequent enumeration). + + Note that if eventInfoCallback throws or returns a nonzero value, all events will + be marked as consumed. + */ + template + _Success_(return == 0) int + EnumerateSampleEvents( + EventInfoCallbackTy&& eventInfoCallback, // int eventInfoCallback(PerfSampleEventInfo const&, args...) + ArgTys&&... args // optional parameters to be passed to eventInfoCallback + ) noexcept(noexcept(eventInfoCallback( // Throws exceptions if and only if eventInfoCallback throws. + std::declval(), + args...))) + { + int error = 0; + + if (m_bufferLeaderFiles != nullptr) + { + OrderedEnumerator enumerator(*this); + error = enumerator.LoadAndSort(); + while (error == 0 && enumerator.MoveNext()) + { + error = eventInfoCallback(m_enumEventInfo, args...); + } + } + + return error; + } + + /* + For each PERF_RECORD_SAMPLE record in the session's buffers, in unspecified + order, invoke: + + int error = eventInfoCallback(eventInfo, args...); + + - eventInfoCallback: Callable object (e.g. a function pointer or a lambda) + to invoke for each event. + + This callback should return an int (0 for success, errno for error). If + eventInfoCallback returns a nonzero value then EnumerateEventsUnordered + will immediately stop and return the specified error value. + + This callback should take a PerfSampleEventInfo const& as its first + parameter. + + The args... (if any) are from the args... of the call to + EnumerateEventsUnordered(eventInfoCallback, args...). + + - args...: optional additional parameters to be passed to eventInfoCallback. + + Returns: int error code (errno), or 0 for success. + + Examples: + + // Use a callback function pointer and callback context: + error = session.EnumerateSampleEventsUnordered(functionPointer, functionContext); + + // Use a lambda: + error = session.EnumerateSampleEventsUnordered( + [&](PerfSampleEventInfo const& event) -> int + { + ... + return 0; + }); + + For efficiency, events will be provided in a natural enumeration order. This + is usually not the same as event timestamp order, so you need to be able to + accept the events out-of-order. If you need the events to be provided in + timestamp order, use EnumerateSampleEvents. + + Note that the eventInfo provided to eventInfoCallback will contain pointers + into the trace buffers. The pointers will become invalid after eventInfoCallback + returns. Any data that you need to use after that point must be copied. + + Note that this method does not throw any of its own exceptions, but it may + exit via exception if your eventInfoCallback(...) throws an exception. + + *** Circular session behavior *** + + For each buffer (usually one per CPU): + + - Pause collection into the buffer. + - Invoke eventInfoCallback(...) for each of the buffer's events, newest-to-oldest. + - Unpause the buffer. + + Note that events are lost if they arrive while the buffer is paused. The lost + event count indicates how many events were lost during previous pauses that would + have been part of an enumeration if there had been no pauses. It does not include + the count of events that were lost due to the current enumeration's pause (those + will show up after a subsequent enumeration). + + *** Realtime session behavior *** + + For each buffer (usually one per CPU): + + - Invoke eventInfoCallback(...) for each of the buffer's events, oldest-to-newest. + - Mark the enumerated events as consumed, making room for subsequent events. + + Note that events are lost if they arrive while the buffer is full. The lost + event count indicates how many events were lost during previous periods when + the buffer was full. It does not include the count of events that were lost + due to the buffer being full at the start of the current enumeration (those will + show up after a subsequent enumeration). + + Note that if eventInfoCallback throws or returns a nonzero value, events will be + marked consumed up to and including the event for which eventInfoCallback returned + an error. + */ + template + _Success_(return == 0) int + EnumerateSampleEventsUnordered( + EventInfoCallbackTy&& eventInfoCallback, // int eventInfoCallback(PerfSampleEventInfo const&, args...) + ArgTys&&... args // optional parameters to be passed to eventInfoCallback + ) noexcept(noexcept(eventInfoCallback( // Throws exceptions if and only if eventInfoCallback throws. + std::declval(), + args...))) + { + int error = 0; + + if (m_bufferLeaderFiles != nullptr) + { + for (uint32_t bufferIndex = 0; bufferIndex != m_bufferCount; bufferIndex += 1) + { + if (m_buffers[bufferIndex].Size == 0) + { + continue; + } + + UnorderedEnumerator enumerator(*this, bufferIndex); + while (enumerator.MoveNext()) + { + error = eventInfoCallback(m_enumEventInfo, args...); + if (error != 0) + { + break; + } + } + + if (error != 0) + { + break; + } + } + } + + return error; + } + + private: + + _Success_(return == 0) int + DisableTracepointImpl(tracepoint_decode::PerfEventMetadata const& metadata) noexcept; + + _Success_(return == 0) int + EnableTracepointImpl(tracepoint_decode::PerfEventMetadata const& metadata) noexcept; + + _Success_(return == 0) static int + IoctlForEachFile( + _In_reads_(filesCount) unique_fd const* files, + unsigned filesCount, + unsigned long request, + _In_reads_opt_(filesCount) unique_fd const* values) noexcept; + + bool + ParseSample( + BufferInfo const& buffer, + uint16_t recordSize, + uint32_t recordBufferPos) noexcept; + + void + EnumeratorEnd(uint32_t bufferIndex) const noexcept; + + void + EnumeratorBegin(uint32_t bufferIndex) noexcept; + + template + bool + EnumeratorMoveNext( + uint32_t bufferIndex, + RecordFn&& recordFn) noexcept(noexcept(recordFn(std::declval(), 0u, 0u))); + + _Success_(return == 0) int + SetTracepointEnableState( + TracepointInfoImpl& tpi, + bool enabled) noexcept; + + _Success_(return == 0) int + AddTracepoint( + tracepoint_decode::PerfEventMetadata const& metadata, + TracepointEnableState enableState) noexcept(false); + + static uint32_t + CalculateBufferCount(TracepointSessionOptions const& options) noexcept; + + static std::unique_ptr + MakeBufferInfos( + uint32_t bufferCount, + uint32_t pageSize, + TracepointSessionOptions const& options) noexcept(false); + + private: + + // Constant + + tracepoint_decode::PerfEventSessionInfo const m_sessionInfo; + TracepointCache& m_cache; + TracepointSessionMode const m_mode; + bool const m_wakeupUseWatermark; + uint32_t const m_wakeupValue; + uint32_t const m_sampleType; + uint32_t const m_bufferCount; + uint32_t const m_pageSize; + + // State + + std::unique_ptr const m_buffers; // size is m_bufferCount + std::unordered_map m_tracepointInfoByCommonType; + std::unordered_map m_tracepointInfoBySampleId; + unique_fd const* m_bufferLeaderFiles; // == m_tracepointInfoByCommonType[N].BufferFiles.get() for some N, size is m_bufferCount + + // Statistics + + uint64_t m_sampleEventCount; + uint64_t m_lostEventCount; + uint64_t m_corruptEventCount; + uint64_t m_corruptBufferCount; + + // Transient + + std::vector m_eventDataBuffer; // Double-buffer for events that wrap. + std::vector m_enumeratorBookmarks; + std::unique_ptr m_pollfd; + tracepoint_decode::PerfSampleEventInfo m_enumEventInfo; + }; + + using TracepointInfoRange = TracepointSession::TracepointInfoRange; + using TracepointInfoIterator = TracepointSession::TracepointInfoIterator; +} +// namespace tracepoint_control + +#endif // _included_TracepointSession_h diff --git a/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/include/tracepoint/TracepointSpec.h b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/include/tracepoint/TracepointSpec.h new file mode 100644 index 0000000000000..608fa46b5094e --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/include/tracepoint/TracepointSpec.h @@ -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 + +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 diff --git a/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/samples/CMakeLists.txt b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/samples/CMakeLists.txt new file mode 100644 index 0000000000000..c849883c3f315 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/samples/CMakeLists.txt @@ -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) diff --git a/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/samples/control-lookup.cpp b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/samples/control-lookup.cpp new file mode 100644 index 0000000000000..fd6ee0ee27852 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/samples/control-lookup.cpp @@ -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 +#include + +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; +} diff --git a/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/samples/control-session.cpp b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/samples/control-session.cpp new file mode 100644 index 0000000000000..9728a8a77598e --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/samples/control-session.cpp @@ -0,0 +1,135 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include +#include +#include +#include +#include + +#include + +#include + +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; +} diff --git a/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/samples/save-session.cpp b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/samples/save-session.cpp new file mode 100644 index 0000000000000..ad388025841e3 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/samples/save-session.cpp @@ -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 +#include +#include +#include +#include + +#include + +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; +} diff --git a/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/src/CMakeLists.txt b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/src/CMakeLists.txt new file mode 100644 index 0000000000000..5db1a522acd8e --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/src/CMakeLists.txt @@ -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 + "$" + "$") +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") diff --git a/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/src/TracepointCache.cpp b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/src/TracepointCache.cpp new file mode 100644 index 0000000000000..be07acc6da8a0 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/src/TracepointCache.cpp @@ -0,0 +1,606 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +//#include +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 +#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&& systemAndFormat, + PerfEventMetadata&& metadata, + std::unique_ptr 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 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(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 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 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 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(); + + 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)®istration->StatusWord; + reg.name_args = (uintptr_t)registerCommand; + + if (0 > ioctl(dataFile, DIAG_IOCSREG, ®)) + { + error = errno; + goto Done; + } + + assert(reg.write_index <= 0x7fffffff); + registration->DataFile = dataFile; + registration->WriteIndex = static_cast(reg.write_index); + + std::vector 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&& systemAndFormat, + size_t systemNameSize, + bool longSize64, + std::unique_ptr 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(field.Offset()); + commonTypeSize = static_cast(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; +} diff --git a/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/src/TracepointPath.cpp b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/src/TracepointPath.cpp new file mode 100644 index 0000000000000..7c3a8303d5360 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/src/TracepointPath.cpp @@ -0,0 +1,349 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +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& 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& 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; +} diff --git a/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/src/TracepointSession.cpp b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/src/TracepointSession.cpp new file mode 100644 index 0000000000000..ee808e1b7d5bd --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/src/TracepointSession.cpp @@ -0,0 +1,2017 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include // __NR_perf_event_open +#include + +#ifdef NDEBUG +#define DEBUG_PRINTF(...) ((void)0) +#else // NDEBUG +#include +#define DEBUG_PRINTF(...) printf(__VA_ARGS__) +#endif // NDEBUG + +using namespace std::string_view_literals; +using namespace tracepoint_control; +using namespace tracepoint_decode; + +// Helpers + +static long +perf_event_open( + struct perf_event_attr* pe, + pid_t pid, + int cpuIndex, + int groupFd, + unsigned long flags) noexcept +{ + return syscall(__NR_perf_event_open, pe, pid, cpuIndex, groupFd, flags); +} + +static perf_event_header const* +BufferDataPosToHeader( + uint8_t const* bufferData, + uint32_t recordBufferPos) noexcept +{ + assert(0 == (recordBufferPos & 7)); + return reinterpret_cast(bufferData + recordBufferPos); +} + +// Return the smallest power of 2 that is >= pageSize and >= bufferSize. +// Assumes pageSize is a power of 2. +static uint32_t +RoundUpBufferSize(uint32_t pageSize, uint32_t bufferSize) noexcept +{ + static constexpr uint32_t BufferSizeMax = 0x80000000; + + assert(0 != pageSize); + assert(0 == (pageSize & (pageSize - 1))); + assert(bufferSize <= BufferSizeMax); + + for (size_t roundedSize = pageSize; roundedSize != 0; roundedSize <<= 1) + { + if (roundedSize >= bufferSize) + { + return roundedSize; + } + } + + return BufferSizeMax; +} + +static tracepoint_decode::PerfEventSessionInfo +MakeSessionInfo(uint32_t clockid) noexcept +{ + static const auto Billion = 1000000000u; + + tracepoint_decode::PerfEventSessionInfo sessionInfo; + sessionInfo.SetClockid(clockid); + + timespec monotonic; + timespec realtime; + if (0 == clock_gettime(clockid, &monotonic) && + 0 == clock_gettime(CLOCK_REALTIME, &realtime)) + { + uint64_t monotonicNS, realtimeNS; + if (monotonic.tv_sec < realtime.tv_sec || + (monotonic.tv_sec == realtime.tv_sec && monotonic.tv_nsec < realtime.tv_nsec)) + { + monotonicNS = 0; + realtimeNS = static_cast(realtime.tv_sec - monotonic.tv_sec) * Billion + + realtime.tv_nsec - monotonic.tv_nsec; + } + else + { + realtimeNS = 0; + monotonicNS = static_cast(monotonic.tv_sec - realtime.tv_sec) * Billion + + monotonic.tv_nsec - realtime.tv_nsec; + } + + sessionInfo.SetClockData(clockid, realtimeNS, monotonicNS); + } + + return sessionInfo; +} + +namespace +{ + /* + Manages a scatter-gather list of chunks of memory that need to be written to + the perf.data file. This allows us to reduce the number of kernel calls. + Instead of calling write() once per event (or twice if the event wraps), we + call writev once for every 16 noncontinguous blocks of memory to be written. + */ + class IovecList + { + static constexpr unsigned Max = 16; + unsigned m_used = 0; + iovec m_vecs[Max]; + + public: + + unsigned + RoomLeft() const noexcept + { + return Max - m_used; + } + + void + Add(uint8_t const* p, size_t c) noexcept + { + assert(m_used < Max); // Caller is responsible for checking RoomLeft(). + auto const lastVec = m_used - 1; + if (0 != m_used && + p == static_cast(m_vecs[lastVec].iov_base) + m_vecs[lastVec].iov_len) + { + // This block immediately follows the last block. Merge the blocks. + m_vecs[lastVec].iov_len += c; + } + else + { + m_vecs[m_used].iov_base = const_cast(p); + m_vecs[m_used].iov_len = c; + m_used += 1; + } + } + + _Success_(return == 0) int + Flush(PerfDataFileWriter& output) noexcept + { + assert(m_used <= Max); + + int error; + if (m_used == 0) + { + error = 0; + } + else + { + size_t cbToWrite = 0; + for (unsigned i = 0; i != m_used; i += 1) + { + cbToWrite += m_vecs[i].iov_len; + if (cbToWrite < m_vecs[i].iov_len) + { + error = ERANGE; + goto Done; + } + } + + for (auto skip = 0u;;) + { + auto const cbWritten = output.WriteEventDataIovecs(m_vecs + skip, m_used - skip); + if (cbWritten < 0) + { + error = errno; + break; + } + else if (static_cast(cbWritten) == cbToWrite) + { + error = 0; + break; + } + + // Partial write. Skip what was written and try again. + + auto cbToSkip = static_cast(cbWritten); + assert(cbToWrite > cbToSkip); + cbToWrite -= cbToSkip; + + while (cbToSkip >= m_vecs[skip].iov_len) + { + cbToSkip -= m_vecs[skip].iov_len; + skip += 1; + } + + assert(skip < m_used); + m_vecs[skip].iov_base = static_cast(m_vecs[skip].iov_base) + cbToSkip; + m_vecs[skip].iov_len = m_vecs[skip].iov_len - cbToSkip; + } + + m_used = 0; + } + + Done: + + return error; + } + }; +} + +// TracepointInfo + +TracepointInfo::~TracepointInfo() +{ + return; +} + +TracepointInfo::TracepointInfo() noexcept +{ + return; +} + +tracepoint_decode::PerfEventMetadata const& +TracepointInfo::Metadata() const noexcept +{ + auto& self = *static_cast(this); + return *self.m_eventDesc.metadata; +} + +tracepoint_decode::PerfEventDesc const& +TracepointInfo::EventDesc() const noexcept +{ + auto& self = *static_cast(this); + return self.m_eventDesc; +} + +TracepointEnableState +TracepointInfo::EnableState() const noexcept +{ + auto& self = *static_cast(this); + return self.m_enableState; +} + +_Success_(return == 0) int +TracepointInfo::GetEventCount(_Out_ uint64_t* value) const noexcept +{ + auto& self = *static_cast(this); + return self.GetEventCountImpl(value); +} + +// ReadFormat + +struct TracepointSession::ReadFormat +{ + // This needs to match the attr.read_format used for tracepoints. + uint64_t value; + uint64_t id; +}; + +// TracepointInfoImpl + +TracepointSession::TracepointInfoImpl::~TracepointInfoImpl() +{ + return; +} + +TracepointSession::TracepointInfoImpl::TracepointInfoImpl( + tracepoint_decode::PerfEventDesc const& eventDesc, + std::unique_ptr eventDescStorage, + std::unique_ptr bufferFiles, + unsigned bufferFilesCount + ) noexcept + : m_eventDesc(eventDesc) + , m_eventDescStorage(std::move(eventDescStorage)) + , m_bufferFiles(std::move(bufferFiles)) + , m_bufferFilesCount(bufferFilesCount) + , m_enableState(TracepointEnableState::Unknown) +{ + return; +} + +_Success_(return == 0) int +TracepointSession::TracepointInfoImpl::Read(unsigned index, _Out_ ReadFormat* data) const noexcept +{ + assert(index < m_bufferFilesCount); + auto const size = read(m_bufferFiles[index].get(), data, sizeof(data[0])); + int const error = size == sizeof(data[0]) + ? 0 + : size < 0 + ? errno + : EPIPE; + return error; +} + +_Success_(return == 0) int +TracepointSession::TracepointInfoImpl::GetEventCountImpl(_Out_ uint64_t* value) const noexcept +{ + int error = 0; + uint64_t total = 0; + + for (unsigned i = 0; i != m_bufferFilesCount; i += 1) + { + if (!m_bufferFiles[i]) + { + continue; + } + + ReadFormat data; + error = Read(i, &data); + if (error != 0) + { + break; + } + + total += data.value; + } + + *value = total; + return error; +} + +// BufferInfo + +TracepointSession::BufferInfo::~BufferInfo() +{ + return; +} + +TracepointSession::BufferInfo::BufferInfo() noexcept + : Mmap() + , Size() + , Data() + , DataPos() + , DataTail() + , DataHead64() +{ + return; +} + +// TracepointBookmark + +TracepointSession::TracepointBookmark::TracepointBookmark( + uint64_t timestamp, + uint16_t bufferIndex, + uint16_t recordSize, + uint32_t recordBufferPos) noexcept + : Timestamp(timestamp) + , BufferIndex(bufferIndex) + , RecordSize(recordSize) + , RecordBufferPos(recordBufferPos) +{ + return; +} + +// UnorderedEnumerator + +TracepointSession::UnorderedEnumerator::~UnorderedEnumerator() +{ + m_session.EnumeratorEnd(m_bufferIndex); +} + +TracepointSession::UnorderedEnumerator::UnorderedEnumerator( + TracepointSession& session, + uint32_t bufferIndex) noexcept + : m_session(session) + , m_bufferIndex(bufferIndex) +{ + m_session.EnumeratorBegin(bufferIndex); +} + +bool +TracepointSession::UnorderedEnumerator::MoveNext() noexcept +{ + auto& session = m_session; + return session.EnumeratorMoveNext( + m_bufferIndex, + [&session]( + BufferInfo const& buffer, + uint16_t recordSize, + uint32_t recordBufferPos) + { + auto const headerType = BufferDataPosToHeader(buffer.Data, recordBufferPos)->type; + return headerType == PERF_RECORD_SAMPLE && + session.ParseSample(buffer, recordSize, recordBufferPos); + }); +} + +// OrderedEnumerator + +TracepointSession::OrderedEnumerator::~OrderedEnumerator() +{ + if (m_needsCleanup) + { + for (unsigned bufferIndex = 0; bufferIndex != m_session.m_bufferCount; bufferIndex += 1) + { + if (m_session.m_buffers[bufferIndex].Size != 0) + { + m_session.EnumeratorEnd(bufferIndex); + } + } + } +} + +TracepointSession::OrderedEnumerator::OrderedEnumerator(TracepointSession& session) noexcept + : m_session(session) + , m_needsCleanup(false) + , m_index(0) +{ + return; +} + +_Success_(return == 0) int +TracepointSession::OrderedEnumerator::LoadAndSort() noexcept +{ + int error; + auto& session = m_session; + + if (0 == (session.m_sampleType & PERF_SAMPLE_TIME)) + { + error = EPERM; + } + else try + { + auto const sampleType = session.m_sampleType; + unsigned const bytesBeforeTime = sizeof(uint64_t) * ( + 1u + // perf_event_header + (0 != (sampleType & PERF_SAMPLE_IDENTIFIER)) + + (0 != (sampleType & PERF_SAMPLE_IP)) + + (0 != (sampleType & PERF_SAMPLE_TID))); + + for (uint32_t bufferIndex = 0; bufferIndex != session.m_bufferCount; bufferIndex += 1) + { + if (session.m_buffers[bufferIndex].Size != 0) + { + session.EnumeratorBegin(bufferIndex); + } + } + + // Circular: If we throw an exception, we need to unpause during cleanup. + // Realtime: If we throw an exception, we don't update tail pointers during cleanup. + m_needsCleanup = !session.IsRealtime(); + + session.m_enumeratorBookmarks.clear(); + for (uint32_t bufferIndex = 0; bufferIndex != session.m_bufferCount; bufferIndex += 1) + { + if (session.m_buffers[bufferIndex].Size == 0) + { + continue; + } + + auto const startSize = session.m_enumeratorBookmarks.size(); + + // Only need to call EnumeratorMoveNext once per buffer - it will loop until a callback + // returns true or it reaches end of buffer, and our callback never returns true. + session.EnumeratorMoveNext( + bufferIndex, + [bufferIndex, bytesBeforeTime, &session]( + BufferInfo const& buffer, + uint16_t recordSize, + uint32_t recordBufferPos) + { + assert(0 == (recordSize & 7)); + assert(0 == (recordBufferPos & 7)); + + if (PERF_RECORD_SAMPLE != BufferDataPosToHeader(buffer.Data, recordBufferPos)->type) + { + return false; // Keep going. + } + + if (recordSize <= bytesBeforeTime) + { + session.m_corruptEventCount += 1; + return false; + } + + auto const timePos = (recordBufferPos + bytesBeforeTime) & (buffer.Size - 1); + auto const timestamp = *reinterpret_cast(buffer.Data + timePos); + session.m_enumeratorBookmarks.emplace_back( // May throw bad_alloc. + timestamp, + bufferIndex, + recordSize, + recordBufferPos); + return false; // Keep going. + }); + + if (!session.IsRealtime()) + { + // Circular buffers enumerate in reverse order. Fix that. + auto const endSize = session.m_enumeratorBookmarks.size(); + auto const bookmarksData = session.m_enumeratorBookmarks.data(); + std::reverse(bookmarksData + startSize, bookmarksData + endSize); + } + } + + auto const bookmarksData = session.m_enumeratorBookmarks.data(); + std::stable_sort( + bookmarksData, + bookmarksData + session.m_enumeratorBookmarks.size(), + [](TracepointBookmark const& a, TracepointBookmark const& b) noexcept + { + return a.Timestamp < b.Timestamp; + }); + + // From here on, do full cleanup. + m_needsCleanup = true; + error = 0; + } + catch (...) + { + error = ENOMEM; + } + + return error; +} + +bool +TracepointSession::OrderedEnumerator::MoveNext() noexcept +{ + auto& session = m_session; + auto const buffers = session.m_buffers.get(); + auto const enumeratorBookmarksData = session.m_enumeratorBookmarks.data(); + auto const enumeratorBookmarksSize = session.m_enumeratorBookmarks.size(); + while (m_index < enumeratorBookmarksSize) + { + auto const& item = enumeratorBookmarksData[m_index]; + m_index += 1; + + if (session.ParseSample( + buffers[item.BufferIndex], + item.RecordSize, + item.RecordBufferPos)) + { + return true; + } + } + + return false; +} + +// TracepointInfoIterator + +TracepointInfoIterator::TracepointInfoIterator(InnerItTy it) noexcept + : m_it(it) +{ + return; +} + +TracepointInfoIterator::TracepointInfoIterator() noexcept + : m_it() +{ + return; +} + +TracepointInfoIterator& +TracepointInfoIterator::operator++() noexcept +{ + ++m_it; + return *this; +} + +TracepointInfoIterator +TracepointInfoIterator::operator++(int) noexcept +{ + TracepointInfoIterator old(m_it); + ++m_it; + return old; +} + +TracepointInfoIterator::pointer +TracepointInfoIterator::operator->() const noexcept +{ + return &m_it->second; +} + +TracepointInfoIterator::reference +TracepointInfoIterator::operator*() const noexcept +{ + return m_it->second; +} + +bool +TracepointInfoIterator::operator==(TracepointInfoIterator other) const noexcept +{ + return m_it == other.m_it; +} + +bool +TracepointInfoIterator::operator!=(TracepointInfoIterator other) const noexcept +{ + return m_it != other.m_it; +} + +// TracepointInfoRange + +TracepointInfoRange::TracepointInfoRange(RangeTy const& range) noexcept + : m_range(range) +{ + return; +} + +TracepointInfoIterator +TracepointInfoRange::begin() const noexcept +{ + return TracepointInfoIterator(m_range.begin()); +} + +TracepointInfoIterator +TracepointInfoRange::end() const noexcept +{ + return TracepointInfoIterator(m_range.end()); +} + +// TracepointSession + +TracepointSession::~TracepointSession() +{ + return; +} + +TracepointSession::TracepointSession( + TracepointCache& cache, + TracepointSessionMode mode, + uint32_t perCpuBufferSize) noexcept(false) + : TracepointSession(cache, TracepointSessionOptions(mode, perCpuBufferSize)) +{ + return; +} + +TracepointSession::TracepointSession( + TracepointCache& cache, + TracepointSessionOptions const& options) noexcept(false) + : m_sessionInfo(MakeSessionInfo(CLOCK_MONOTONIC_RAW)) + , m_cache(cache) + , m_mode(options.m_mode) + , m_wakeupUseWatermark(options.m_wakeupUseWatermark) + , m_wakeupValue(options.m_wakeupValue) + , m_sampleType(options.m_sampleType) + , m_bufferCount(CalculateBufferCount(options)) + , m_pageSize(sysconf(_SC_PAGESIZE)) + , m_buffers(MakeBufferInfos(m_bufferCount, m_pageSize, options)) // may throw bad_alloc. + , m_tracepointInfoByCommonType() // may throw bad_alloc (but probably doesn't). + , m_tracepointInfoBySampleId() // may throw bad_alloc (but probably doesn't). + , m_bufferLeaderFiles(nullptr) + , m_sampleEventCount(0) + , m_lostEventCount(0) + , m_corruptEventCount(0) + , m_corruptBufferCount(0) + , m_eventDataBuffer() + , m_enumeratorBookmarks() + , m_pollfd(nullptr) + , m_enumEventInfo() +{ + assert(options.m_mode <= TracepointSessionMode::RealTime); + assert(m_bufferCount > 0 && m_bufferCount < 0x10000000); + assert(m_pageSize >= sizeof(perf_event_mmap_page) && m_pageSize < 0x10000000); + assert((m_pageSize & (m_pageSize - 1)) == 0); // power of 2 +} + +TracepointCache& +TracepointSession::Cache() const noexcept +{ + return m_cache; +} + +TracepointSessionMode +TracepointSession::Mode() const noexcept +{ + return m_mode; +} + +PerfEventSessionInfo const& +TracepointSession::SessionInfo() const noexcept +{ + return m_sessionInfo; +} + +bool +TracepointSession::IsRealtime() const noexcept +{ + return m_mode != TracepointSessionMode::Circular; +} + +uint32_t +TracepointSession::BufferSize(unsigned bufferIndex) const noexcept +{ + assert(bufferIndex < BufferCount()); + return m_buffers[bufferIndex].Size; +} + +uint32_t +TracepointSession::BufferCount() const noexcept +{ + return m_bufferCount; +} + +uint64_t +TracepointSession::SampleEventCount() const noexcept +{ + return m_sampleEventCount; +} + +uint64_t +TracepointSession::LostEventCount() const noexcept +{ + return m_lostEventCount; +} + +uint64_t +TracepointSession::CorruptEventCount() const noexcept +{ + return m_corruptEventCount; +} + +uint64_t +TracepointSession::CorruptBufferCount() const noexcept +{ + return m_corruptBufferCount; +} + +void +TracepointSession::Clear() noexcept +{ + for (uint32_t bufferIndex = 0; bufferIndex != m_bufferCount; bufferIndex += 1) + { + m_buffers[bufferIndex].Mmap.reset(); + m_buffers[bufferIndex].Data = nullptr; + } + + m_tracepointInfoByCommonType.clear(); + m_tracepointInfoBySampleId.clear(); + m_bufferLeaderFiles = nullptr; + + m_sampleEventCount = 0; + m_lostEventCount = 0; + m_corruptEventCount = 0; + m_corruptBufferCount = 0; +} + +_Success_(return == 0) int +TracepointSession::DisableTracepoint(unsigned id) noexcept +{ + auto const metadata = m_cache.FindById(id); + auto const error = metadata == nullptr + ? ENOENT + : DisableTracepointImpl(*metadata); + + return error; +} + +_Success_(return == 0) int +TracepointSession::DisableTracepoint(TracepointName name) noexcept +{ + int error; + + PerfEventMetadata const* metadata; + error = m_cache.FindOrAddFromSystem(name, &metadata); + if (error == 0) + { + error = DisableTracepointImpl(*metadata); + } + + return error; +} + +_Success_(return == 0) int +TracepointSession::EnableTracepoint(unsigned id) noexcept +{ + auto const metadata = m_cache.FindById(id); + auto const error = metadata == nullptr + ? ENOENT + : EnableTracepointImpl(*metadata); + + return error; +} + +_Success_(return == 0) int +TracepointSession::EnableTracepoint(TracepointName name) noexcept +{ + int error; + + PerfEventMetadata const* metadata; + error = m_cache.FindOrAddFromSystem(name, &metadata); + if (error == 0) + { + error = EnableTracepointImpl(*metadata); + } + + return error; +} + +TracepointInfoRange +TracepointSession::TracepointInfos() const noexcept +{ + return TracepointInfoRange(m_tracepointInfoByCommonType); +} + +TracepointInfoIterator +TracepointSession::TracepointInfosBegin() const noexcept +{ + return TracepointInfoIterator(m_tracepointInfoByCommonType.begin()); +} + +TracepointInfoIterator +TracepointSession::TracepointInfosEnd() const noexcept +{ + return TracepointInfoIterator(m_tracepointInfoByCommonType.end()); +} + +TracepointInfoIterator +TracepointSession::FindTracepointInfo(unsigned id) const noexcept +{ + return TracepointInfoIterator(m_tracepointInfoByCommonType.find(id)); +} + +TracepointInfoIterator +TracepointSession::FindTracepointInfo(TracepointName name) const noexcept +{ + auto metadata = m_cache.FindByName(name); + return TracepointInfoIterator(metadata + ? m_tracepointInfoByCommonType.find(metadata->Id()) + : m_tracepointInfoByCommonType.end()); +} + +_Success_(return == 0) int +TracepointSession::WaitForWakeup( + timespec const* timeout, + sigset_t const* sigmask, + _Out_opt_ int* pActiveCount) noexcept +{ + int error; + int activeCount; + + if (!IsRealtime() || m_bufferLeaderFiles == nullptr) + { + activeCount = 0; + error = EPERM; + } + else try + { + if (m_pollfd == nullptr) + { + m_pollfd = std::make_unique(m_bufferCount); + } + + unsigned pollfdCount = 0; + for (unsigned i = 0; i != m_bufferCount; i += 1) + { + if (m_buffers[i].Size != 0) + { + m_pollfd[pollfdCount] = { m_bufferLeaderFiles[i].get(), POLLIN, 0 }; + pollfdCount += 1; + } + } + + activeCount = ppoll(m_pollfd.get(), pollfdCount, timeout, sigmask); + if (activeCount < 0) + { + activeCount = 0; + error = errno; + } + else + { + error = 0; + } + } + catch (...) + { + activeCount = 0; + error = ENOMEM; + } + + if (pActiveCount) + { + *pActiveCount = activeCount; + } + return error; +} + +_Success_(return == 0) int +TracepointSession::GetBufferFiles( + _Out_writes_(BufferCount()) int* pBufferFiles) const noexcept +{ + int error; + + if (m_bufferLeaderFiles == nullptr) + { + memset(pBufferFiles, 0, m_bufferCount * sizeof(pBufferFiles[0])); + error = EPERM; + } + else + { + for (unsigned i = 0; i != m_bufferCount; i += 1) + { + pBufferFiles[i] = m_bufferLeaderFiles[i].get(); + } + + error = 0; + } + + return error; +} + +_Success_(return == 0) int +TracepointSession::SavePerfDataFile( + _In_z_ char const* perfDataFileName, + TracepointSavePerfDataFileOptions const& options) noexcept +{ + int error = 0; + PerfDataFileWriter writer; + TracepointTimestampRange writtenRange{}; // Start with an invalid range. + bool timesValid; + + if (options.m_timestampWrittenRange) + { + *options.m_timestampWrittenRange = writtenRange; + } + + error = writer.Create(perfDataFileName, options.m_openMode); + if (error != 0) + { + goto Done; + } + + // Mark the end of the "synthetic events" section (currently empty). + + error = writer.WriteFinishedInit(); + if (error != 0) + { + goto Done; + } + + // Write event data: + + error = FlushToWriter(writer, &writtenRange, options.m_filterRange); + if (error != 0) + { + goto Done; + } + + timesValid = (m_sampleType & PERF_SAMPLE_TIME) && writtenRange.First <= writtenRange.Last; + + // Write system information headers: + + error = SetWriterHeaders(writer, timesValid ? &writtenRange : nullptr); + if (error != 0) + { + goto Done; + } + + // Flush to disk: + + error = writer.FinalizeAndClose(); + if (error != 0) + { + goto Done; + } + + // Update output parameters: + + if (timesValid && options.m_timestampWrittenRange) + { + *options.m_timestampWrittenRange = writtenRange; + } + +Done: + + return error; +} + +_Success_(return == 0) int +TracepointSession::FlushToWriter( + tracepoint_decode::PerfDataFileWriter & writer, + _Inout_ TracepointTimestampRange* writtenRange, + TracepointTimestampRange filterRange) noexcept +{ + int error = 0; + IovecList vecList; + + if (m_bufferLeaderFiles != nullptr) + { + auto recordFn = [this, &vecList, writtenRange, filterRange]( + BufferInfo const& buffer, + uint16_t recordSize, + uint32_t recordBufferPos) noexcept + { + // Look up the correct value for m_enumEventInfo.event_desc. + + m_enumEventInfo.event_desc = nullptr; + if (PERF_RECORD_SAMPLE == BufferDataPosToHeader(buffer.Data, recordBufferPos)->type) + { + // TODO: We don't need a full parse here. Could potentially + // save a few cycles by inlining ParseSample and removing the + // parts we don't need. + + // If this succeeds it will set m_enumEventInfo. + if (ParseSample(buffer, recordSize, recordBufferPos)) + { + if (filterRange.First > m_enumEventInfo.time || + filterRange.Last < m_enumEventInfo.time) + { + // TODO: Optimization - in some cases, this means we're going + // to skip the rest of the buffer, so perhaps detect those + // cases and stop the enumeration? + return false; // Skip this event. + } + + if (m_enumEventInfo.time < writtenRange->First) + { + writtenRange->First = m_enumEventInfo.time; + } + + if (m_enumEventInfo.time > writtenRange->Last) + { + writtenRange->Last = m_enumEventInfo.time; + } + } + } + + // Add event data to vecList. + + auto const unmaskedPosEnd = recordBufferPos + recordSize; + if (unmaskedPosEnd <= buffer.Size) + { + // Event does not wrap. + vecList.Add(buffer.Data + recordBufferPos, recordSize); + } + else + { + // Event wraps. + vecList.Add(buffer.Data + recordBufferPos, buffer.Size - recordBufferPos); + vecList.Add(buffer.Data, unmaskedPosEnd - buffer.Size); + } + + return true; + }; + + // Pause one buffer at a time. + for (uint32_t bufferIndex = 0; bufferIndex != m_bufferCount; bufferIndex += 1) + { + EnumeratorBegin(bufferIndex); + + while (EnumeratorMoveNext(bufferIndex, recordFn)) + { + if (m_enumEventInfo.event_desc) + { + error = writer.AddTracepointEventDesc(*m_enumEventInfo.event_desc); + if (error != EEXIST && error != 0) + { + EnumeratorEnd(bufferIndex); + goto Done; + } + } + + if (vecList.RoomLeft() < 2) // Next recordFn may need up to 2 calls to Add(). + { + error = vecList.Flush(writer); + if (error != 0) + { + EnumeratorEnd(bufferIndex); + goto Done; + } + } + } + + error = vecList.Flush(writer); + + EnumeratorEnd(bufferIndex); + + if (error != 0) + { + goto Done; + } + } + } + +Done: + + return error; +} + +_Success_(return == 0) int +TracepointSession::SetWriterHeaders( + tracepoint_decode::PerfDataFileWriter& writer, + _In_opt_ TracepointTimestampRange const* writtenRange) noexcept +{ + int error; + + utsname uts; + if (0 == uname(&uts)) + { + // HOSTNAME, OSRELEASE, ARCH + error = writer.SetUtsNameHeaders(uts); + if (error != 0) + { + goto Done; + } + } + + { + auto const conf = sysconf(_SC_NPROCESSORS_CONF); + auto const onln = sysconf(_SC_NPROCESSORS_ONLN); + if (conf > 0 && onln > 0) + { + // NRCPUS + error = writer.SetNrCpusHeader(static_cast(conf), static_cast(onln)); + if (error != 0) + { + goto Done; + } + } + } + + // CLOCKID, CLOCK_DATA + error = writer.SetSessionInfoHeaders(m_sessionInfo); + if (error != 0) + { + goto Done; + } + + if (writtenRange != nullptr) + { + // SAMPLE_TIME + error = writer.SetSampleTimeHeader(writtenRange->First, writtenRange->Last); + if (error != 0) + { + goto Done; + } + } + +Done: + + return error; +} + +_Success_(return == 0) int +TracepointSession::DisableTracepointImpl(PerfEventMetadata const& metadata) noexcept +{ + int error; + + auto const existingIt = m_tracepointInfoByCommonType.find(metadata.Id()); + if (existingIt == m_tracepointInfoByCommonType.end()) + { + error = ENOENT; + } + else + { + error = SetTracepointEnableState(existingIt->second, false); + } + + return error; +} + +_Success_(return == 0) int +TracepointSession::EnableTracepointImpl(PerfEventMetadata const& metadata) noexcept +{ + int error; + + auto const existingIt = m_tracepointInfoByCommonType.find(metadata.Id()); + if (existingIt == m_tracepointInfoByCommonType.end()) + { + error = AddTracepoint(metadata, TracepointEnableState::Enabled); + } + else + { + error = SetTracepointEnableState(existingIt->second, true); + } + + return error; +} + +_Success_(return == 0) int +TracepointSession::IoctlForEachFile( + _In_reads_(filesCount) unique_fd const* files, + unsigned filesCount, + unsigned long request, + _In_reads_opt_(filesCount) unique_fd const* values) noexcept +{ + int error = 0; + + for (unsigned i = 0; i != filesCount; i += 1) + { + if (!files[i]) + { + continue; + } + + errno = 0; + auto const value = values ? values[i].get() : 0; + if (-1 == ioctl(files[i].get(), request, value)) + { + error = errno; + if (error == 0) + { + error = ENODEV; + } + } + } + + return error; +} + +bool +TracepointSession::ParseSample( + BufferInfo const& buffer, + uint16_t recordSize, + uint32_t recordBufferPos) noexcept +{ + assert(buffer.Mmap); + assert(buffer.Mmap.get() == buffer.Data - m_pageSize); + assert(buffer.Mmap.get_size() == buffer.Size + m_pageSize); + assert(0 == (recordSize & 7)); + assert(0 == (recordBufferPos & 7)); + assert(recordSize <= buffer.Size); + assert(recordBufferPos < buffer.Size); + + uint8_t const* p; + + if (recordBufferPos + recordSize <= buffer.Size) + { + // Event does not wrap. + p = buffer.Data + recordBufferPos; + } + else + { + // Event wraps. We need to double-buffer it. + + if (m_eventDataBuffer.size() < recordSize) + { + try + { + m_eventDataBuffer.resize(recordSize); + } + catch (...) + { + m_lostEventCount += 1; + return false; // out of memory + } + } + + auto const afterWrap = recordBufferPos + recordSize - buffer.Size; + auto const beforeWrap = buffer.Size - recordBufferPos; + auto const eventDataBuffer = m_eventDataBuffer.data(); + memcpy(eventDataBuffer, buffer.Data + recordBufferPos, beforeWrap); + memcpy(eventDataBuffer + beforeWrap, buffer.Data, afterWrap); + p = eventDataBuffer; + } + + auto const pEnd = p + recordSize; + auto const infoSampleTypes = m_sampleType; + uint64_t infoId = 0; + PerfEventDesc const* infoEventDesc; + char const* infoRawData = nullptr; + uint32_t infoRawDataSize = 0; + + perf_event_header const* const infoHeader = reinterpret_cast(p); + p += sizeof(perf_event_header); + + auto const SampleTypeSupported = 0u + | PERF_SAMPLE_IDENTIFIER + | PERF_SAMPLE_IP + | PERF_SAMPLE_TID + | PERF_SAMPLE_TIME + | PERF_SAMPLE_ADDR + | PERF_SAMPLE_ID + | PERF_SAMPLE_STREAM_ID + | PERF_SAMPLE_CPU + | PERF_SAMPLE_PERIOD + | PERF_SAMPLE_CALLCHAIN + | PERF_SAMPLE_RAW; + static_assert( + SampleTypeSupported == TracepointSessionOptions::SampleTypeSupported, + "SampleTypeSupported out of sync"); + + auto const SampleTypeDefault = 0u + | PERF_SAMPLE_IDENTIFIER + | PERF_SAMPLE_TID + | PERF_SAMPLE_TIME + | PERF_SAMPLE_CPU + | PERF_SAMPLE_RAW; + static_assert( + SampleTypeDefault == TracepointSessionOptions::SampleTypeDefault, + "SampleTypeDefault out of sync"); + + // Fast path for default sample type. + if (infoSampleTypes == SampleTypeDefault) + { + if (recordSize < + sizeof(perf_event_header) + + sizeof(uint64_t) + // PERF_SAMPLE_IDENTIFIER + sizeof(uint64_t) + // PERF_SAMPLE_TID + sizeof(uint64_t) + // PERF_SAMPLE_TIME + sizeof(uint64_t) + // PERF_SAMPLE_CPU + sizeof(uint64_t)) // PERF_SAMPLE_RAW + { + goto Error; + } + + // PERF_SAMPLE_IDENTIFIER + infoId = *reinterpret_cast(p); + p += sizeof(uint64_t); + + // PERF_SAMPLE_TID + auto const pTid = reinterpret_cast(p); + m_enumEventInfo.pid = pTid[0]; + m_enumEventInfo.tid = pTid[1]; + p += sizeof(uint64_t); + + // PERF_SAMPLE_TIME + m_enumEventInfo.time = *reinterpret_cast(p); + p += sizeof(uint64_t); + + // PERF_SAMPLE_CPU + auto const pCpu = reinterpret_cast(p); + m_enumEventInfo.cpu = pCpu[0]; + m_enumEventInfo.cpu_reserved = pCpu[1]; + p += sizeof(uint64_t); + + // PERF_SAMPLE_RAW + goto PerfSampleRaw; + } + + if (infoSampleTypes & PERF_SAMPLE_IDENTIFIER) + { + if (p == pEnd) goto Error; + infoId = *reinterpret_cast(p); + p += sizeof(uint64_t); + } + + if (infoSampleTypes & PERF_SAMPLE_IP) + { + if (p == pEnd) goto Error; + m_enumEventInfo.ip = *reinterpret_cast(p); + p += sizeof(uint64_t); + } + + if (infoSampleTypes & PERF_SAMPLE_TID) + { + if (p == pEnd) goto Error; + auto const pTid = reinterpret_cast(p); + m_enumEventInfo.pid = pTid[0]; + m_enumEventInfo.tid = pTid[1]; + p += sizeof(uint64_t); + } + + if (infoSampleTypes & PERF_SAMPLE_TIME) + { + if (p == pEnd) goto Error; + m_enumEventInfo.time = *reinterpret_cast(p); + p += sizeof(uint64_t); + } + + if (infoSampleTypes & PERF_SAMPLE_ADDR) + { + if (p == pEnd) goto Error; + m_enumEventInfo.addr = *reinterpret_cast(p); + p += sizeof(uint64_t); + } + + if (infoSampleTypes & PERF_SAMPLE_ID) + { + if (p == pEnd) goto Error; + infoId = *reinterpret_cast(p); + p += sizeof(uint64_t); + } + + if (infoSampleTypes & PERF_SAMPLE_STREAM_ID) + { + if (p == pEnd) goto Error; + m_enumEventInfo.stream_id = *reinterpret_cast(p); + p += sizeof(uint64_t); + } + + if (infoSampleTypes & PERF_SAMPLE_CPU) + { + if (p == pEnd) goto Error; + auto const pCpu = reinterpret_cast(p); + m_enumEventInfo.cpu = pCpu[0]; + m_enumEventInfo.cpu_reserved = pCpu[1]; + p += sizeof(uint64_t); + } + + if (infoSampleTypes & PERF_SAMPLE_PERIOD) + { + if (p == pEnd) goto Error; + m_enumEventInfo.period = *reinterpret_cast(p); + p += sizeof(uint64_t); + } + + if (infoSampleTypes & PERF_SAMPLE_CALLCHAIN) + { + if (p == pEnd) goto Error; + auto const infoCallchain = reinterpret_cast(p); + m_enumEventInfo.callchain = infoCallchain; + auto const count = *infoCallchain; + p += sizeof(uint64_t); + + if ((pEnd - p) / sizeof(uint64_t) < count) + { + goto Error; + } + p += count * sizeof(uint64_t); + } + + if (infoSampleTypes & PERF_SAMPLE_RAW) + { + if (p == pEnd) goto Error; + + PerfSampleRaw: + + assert(p < pEnd); + + auto const* pRaw = reinterpret_cast(p); + infoRawDataSize = pRaw[0]; + infoRawData = reinterpret_cast(pRaw + 1); + if ((pEnd - p) - sizeof(uint32_t) < infoRawDataSize) + { + goto Error; + } + + assert(p + sizeof(uint32_t) + infoRawDataSize <= pEnd); + + // Try to look up eventDesc by common type field: + + decltype(m_tracepointInfoByCommonType.end()) infoIt; + auto const commonTypeOffset = static_cast(m_cache.CommonTypeOffset()); + auto const commonTypeSize = m_cache.CommonTypeSize(); + if (infoRawDataSize <= commonTypeOffset || + infoRawDataSize - commonTypeOffset <= commonTypeSize) + { + infoIt = m_tracepointInfoByCommonType.end(); + } + else if (commonTypeSize == sizeof(uint16_t)) + { + uint16_t commonType; + memcpy(&commonType, infoRawData + commonTypeOffset, sizeof(commonType)); + infoIt = m_tracepointInfoByCommonType.find(commonType); + } + else if (commonTypeSize == sizeof(uint32_t)) + { + uint32_t commonType; + memcpy(&commonType, infoRawData + commonTypeOffset, sizeof(commonType)); + infoIt = m_tracepointInfoByCommonType.find(commonType); + } + else + { + assert(commonTypeSize == 1); + uint8_t commonType; + memcpy(&commonType, infoRawData + commonTypeOffset, sizeof(commonType)); + infoIt = m_tracepointInfoByCommonType.find(commonType); + } + + if (infoIt != m_tracepointInfoByCommonType.end()) + { + infoEventDesc = &infoIt->second.m_eventDesc; + goto Done; + } + } + else + { + assert(p <= pEnd); + } + + if (infoSampleTypes & (PERF_SAMPLE_IDENTIFIER | PERF_SAMPLE_ID)) + { + // Try to look up eventDesc by sample id: + + auto infoIt = m_tracepointInfoBySampleId.find(infoId); + if (infoIt != m_tracepointInfoBySampleId.end()) + { + infoEventDesc = &infoIt->second->m_eventDesc; + goto Done; + } + } + + // Unable to locate eventDesc. + goto Error; + +Done: + + m_enumEventInfo.event_desc = infoEventDesc; + m_enumEventInfo.session_info = &m_sessionInfo; + m_enumEventInfo.header = infoHeader; + m_enumEventInfo.id = infoId; + m_enumEventInfo.raw_data = infoRawData; + m_enumEventInfo.raw_data_size = infoRawDataSize; + + m_sampleEventCount += 1; + return true; + +Error: + + assert(p <= pEnd); + + m_enumEventInfo.event_desc = {}; + m_enumEventInfo.session_info = {}; + m_enumEventInfo.header = {}; + m_enumEventInfo.id = {}; + m_enumEventInfo.raw_data = {}; + m_enumEventInfo.raw_data_size = {}; + + m_corruptEventCount += 1; + return false; +} + +void +TracepointSession::EnumeratorEnd(uint32_t bufferIndex) const noexcept +{ + auto const& buffer = m_buffers[bufferIndex]; + assert(buffer.Size != 0); + + if (!IsRealtime()) + { + // Should not change while collection paused. + assert(buffer.DataHead64 == __atomic_load_n( + &static_cast(buffer.Mmap.get())->data_head, + __ATOMIC_RELAXED)); + + int error = ioctl(m_bufferLeaderFiles[bufferIndex].get(), PERF_EVENT_IOC_PAUSE_OUTPUT, 0); + if (error != 0) + { + DEBUG_PRINTF("CPU%u unpause error %u\n", bufferIndex, error); + } + } + else if (buffer.DataPos != buffer.DataTail) + { + // Create a new 64-bit tail value. + uint64_t newTail64; + static_assert(sizeof(buffer.DataPos) == 8 || sizeof(buffer.DataPos) == 4); + if constexpr (sizeof(buffer.DataPos) == 8) + { + newTail64 = buffer.DataPos; + } + else + { + // Convert m_bufferDataPos to a 64-bit value relative to m_bufferDataHead64. + // Order of operations needs to be careful about 64-bit wrapping, e.g. + // - DataHead64 = 0x600000000 + // - DataHead32 = 0x000000000 + // - DataPos32 = 0x0FFFFFFF8 + // Correct newTail64 is 0x5FFFFFFF8, not 0x6FFFFFFF8 + newTail64 = buffer.DataHead64 - (static_cast(buffer.DataHead64) - buffer.DataPos); + } + + assert(buffer.DataHead64 - newTail64 <= buffer.Size); + + auto const bufferHeader = static_cast(buffer.Mmap.get()); + + // ATOMIC_RELEASE: perf_events.h recommends smp_mb() here. + // For future consideration: Ordered enumerator could probably merge barriers. + // For future consideration: This probably just needs a compiler barrier. + __atomic_store_n(&bufferHeader->data_tail, newTail64, __ATOMIC_RELEASE); + } +} + +void +TracepointSession::EnumeratorBegin(uint32_t bufferIndex) noexcept +{ + auto const realtime = IsRealtime(); + if (!realtime) + { + int error = ioctl(m_bufferLeaderFiles[bufferIndex].get(), PERF_EVENT_IOC_PAUSE_OUTPUT, 1); + if (error != 0) + { + DEBUG_PRINTF("CPU%u pause error %u\n", bufferIndex, error); + } + } + + auto& buffer = m_buffers[bufferIndex]; + auto const bufferHeader = static_cast(buffer.Mmap.get()); + + // ATOMIC_ACQUIRE: perf_events.h recommends smp_rmb() here. + // For future consideration: Ordered enumerator could probably merge barriers. + buffer.DataHead64 = __atomic_load_n(&bufferHeader->data_head, __ATOMIC_ACQUIRE); + + if (0 != (buffer.DataHead64 & 7) || + m_pageSize != bufferHeader->data_offset || + buffer.Size != bufferHeader->data_size) + { + // Unexpected - corrupt trace buffer. + DEBUG_PRINTF("CPU%u bad perf_event_mmap_page: head=%llx offset=%lx size=%lx\n", + bufferIndex, + (unsigned long long)buffer.DataHead64, + (unsigned long)bufferHeader->data_offset, + (unsigned long)bufferHeader->data_size); + buffer.DataTail = static_cast(buffer.DataHead64) - buffer.Size; + buffer.DataPos = static_cast(buffer.DataHead64); + m_corruptBufferCount += 1; + } + else if (!realtime) + { + // Circular: write_backward == 1 + buffer.DataTail = static_cast(buffer.DataHead64) - buffer.Size; + buffer.DataPos = buffer.DataTail; + } + else + { + // Realtime: write_backward == 0 + auto const bufferDataTail64 = bufferHeader->data_tail; + buffer.DataTail = static_cast(bufferDataTail64); + if (buffer.DataHead64 - bufferDataTail64 > buffer.Size) + { + // Unexpected - assume bad tail pointer. + DEBUG_PRINTF("CPU%u bad data_tail: head=%llx tail=%llx\n", + bufferIndex, + (unsigned long long)buffer.DataHead64, + (unsigned long long)bufferDataTail64); + buffer.DataTail = static_cast(buffer.DataHead64) - buffer.Size; // Ensure tail gets updated. + buffer.DataPos = static_cast(buffer.DataHead64); + m_corruptBufferCount += 1; + } + else + { + buffer.DataPos = buffer.DataTail; + } + } +} + +template +bool +TracepointSession::EnumeratorMoveNext( + uint32_t bufferIndex, + RecordFn&& recordFn) noexcept(noexcept(recordFn(std::declval(), 0u, 0u))) +{ + auto& buffer = m_buffers[bufferIndex]; + assert(buffer.Mmap); + assert(buffer.Mmap.get() == buffer.Data - m_pageSize); + assert(buffer.Mmap.get_size() == buffer.Size + m_pageSize); + + for (;;) + { + auto const remaining = static_cast(buffer.DataHead64) - buffer.DataPos; + if (remaining == 0) + { + break; + } + + auto const eventHeaderBufferPos = buffer.DataPos & (buffer.Size - 1); + auto const eventHeader = *BufferDataPosToHeader(buffer.Data, eventHeaderBufferPos); + + if (eventHeader.size == 0 || + eventHeader.size > remaining) + { + // - Circular: this is probably not a real problem - it's probably + // unused buffer space or a partially-overwritten event. + // - Realtime: The buffer is corrupt. + m_corruptBufferCount += IsRealtime(); + + // In either case, buffer is done. Mark the buffer's events as consumed. + buffer.DataPos = static_cast(buffer.DataHead64); + break; + } + + if (0 != (eventHeader.size & 7)) + { + // Unexpected - corrupt event header. + DEBUG_PRINTF("CPU%u unaligned eventHeader.Size at pos %lx: %u\n", + bufferIndex, (unsigned long)buffer.DataPos, eventHeader.size); + + // The event is corrupt, can't parse beyond it. Mark the buffer's events as consumed. + m_corruptBufferCount += 1; + buffer.DataPos = static_cast(buffer.DataHead64); + break; + } + + buffer.DataPos += eventHeader.size; + + if (eventHeader.type == PERF_RECORD_LOST) + { + auto const newEventsLost64 = *reinterpret_cast( + buffer.Data + ((eventHeaderBufferPos + sizeof(perf_event_header) + sizeof(uint64_t)) & (buffer.Size - 1))); + m_lostEventCount += newEventsLost64; + } + + if (recordFn(buffer, eventHeader.size, eventHeaderBufferPos)) + { + return true; + } + } + + return false; +} + +_Success_(return == 0) int +TracepointSession::SetTracepointEnableState( + TracepointInfoImpl & tpi, + bool enabled) noexcept +{ + int error; + + static auto const UnknownState = TracepointEnableState::Unknown; + auto const desiredState = enabled + ? TracepointEnableState::Enabled + : TracepointEnableState::Disabled; + + if (desiredState == tpi.m_enableState) + { + error = 0; + goto Done; + } + + tpi.m_enableState = UnknownState; + + error = IoctlForEachFile( + tpi.m_bufferFiles.get(), + tpi.m_bufferFilesCount, + enabled ? PERF_EVENT_IOC_ENABLE : PERF_EVENT_IOC_DISABLE, + nullptr); + if (error == 0) + { + tpi.m_enableState = desiredState; + } + +Done: + + return error; +} + +_Success_(return == 0) int +TracepointSession::AddTracepoint( + PerfEventMetadata const& metadata, + TracepointEnableState enableState) noexcept(false) +{ + int error; + uint32_t cIdsAdded = 0; + uint64_t* pIds = nullptr; + + try + { + auto const systemName = metadata.SystemName(); + auto const eventName = metadata.Name(); + if (systemName.size() > 65535 || + eventName.size() > 65535) + { + error = E2BIG; + goto Error; + } + + uint32_t nonzeroBufferCount = 0; + for (uint32_t i = 0; i != m_bufferCount; i += 1) + { + if (m_buffers[i].Size != 0) + { + nonzeroBufferCount += 1; + } + } + + // We don't use the fields that were added after v3. Allocate space for + // the full structure (we expose the structure to users) but don't ask + // the kernel to look at the new fields. + unsigned constexpr PerfEventAttrSizeUsed = PERF_ATTR_SIZE_VER3; + + auto const cbEventDescStorage = + sizeof(perf_event_attr) + + nonzeroBufferCount * sizeof(uint64_t) + + systemName.size() + 1 + eventName.size() + 1; + auto eventDescStorage = std::make_unique(cbEventDescStorage); + + auto const pAttr = reinterpret_cast(eventDescStorage.get()); + pAttr->type = PERF_TYPE_TRACEPOINT; + pAttr->size = PerfEventAttrSizeUsed; + pAttr->config = metadata.Id(); + pAttr->sample_period = 1; + pAttr->sample_type = m_sampleType; + pAttr->read_format = PERF_FORMAT_ID; // Must align with the definition of struct ReadFormat. + pAttr->watermark = m_wakeupUseWatermark; + pAttr->use_clockid = 1; + pAttr->write_backward = !IsRealtime(); + pAttr->wakeup_events = m_wakeupValue; + pAttr->clockid = m_sessionInfo.Clockid(); + static_assert(offsetof(perf_event_attr, clockid) < PerfEventAttrSizeUsed); + + // pIds will be initialized after file handle creation. + // cIdsAdded tracks initialization. + pIds = reinterpret_cast(pAttr + 1); + + auto const pName = reinterpret_cast(pIds + nonzeroBufferCount); + { + size_t i = 0; + memcpy(&pName[i], systemName.data(), systemName.size()); + i += systemName.size(); + pName[i] = ':'; + i += 1; + memcpy(&pName[i], eventName.data(), eventName.size()); + i += eventName.size(); + pName[i] = '\0'; + } + + PerfEventDesc const eventDesc = { + pAttr, + pName, + &metadata, + pIds, + nonzeroBufferCount + }; + + auto er = m_tracepointInfoByCommonType.try_emplace(metadata.Id(), + eventDesc, + std::move(eventDescStorage), + std::make_unique(m_bufferCount), + m_bufferCount); + assert(er.second); + auto& tpi = er.first->second; + tpi.m_enableState = enableState; + + // Starting from here, if there is an error then we must erase(metadata.Id). + + for (uint32_t bufferIndex = 0; bufferIndex != m_bufferCount; bufferIndex += 1) + { + if (m_buffers[bufferIndex].Size == 0) + { + continue; + } + + errno = 0; + tpi.m_bufferFiles[bufferIndex].reset(perf_event_open(pAttr, -1, bufferIndex, -1, PERF_FLAG_FD_CLOEXEC)); + if (!tpi.m_bufferFiles[bufferIndex]) + { + error = errno; + if (error == 0) + { + error = ENODEV; + } + + goto Error; + } + } + + if (m_bufferLeaderFiles) + { + // Leader already exists. Add this event to the leader's mmaps. + error = IoctlForEachFile(tpi.m_bufferFiles.get(), m_bufferCount, PERF_EVENT_IOC_SET_OUTPUT, m_bufferLeaderFiles); + if (error) + { + goto Error; + } + } + else + { + // This is the first event. Make it the "leader" (the owner of the session buffers). + auto const prot = IsRealtime() + ? PROT_READ | PROT_WRITE + : PROT_READ; + for (uint32_t bufferIndex = 0; bufferIndex != m_bufferCount; bufferIndex += 1) + { + if (m_buffers[bufferIndex].Size == 0) + { + continue; + } + + auto const mmapSize = m_pageSize + m_buffers[bufferIndex].Size; + + errno = 0; + auto cpuMap = mmap(nullptr, mmapSize, prot, MAP_SHARED, tpi.m_bufferFiles[bufferIndex].get(), 0); + if (MAP_FAILED == cpuMap) + { + error = errno; + + // Clean up any mmaps that we opened. + for (uint32_t bufferIndex2 = 0; bufferIndex2 != bufferIndex; bufferIndex2 += 1) + { + m_buffers[bufferIndex2].Mmap.reset(); + m_buffers[bufferIndex2].Data = nullptr; + } + + if (error == 0) + { + error = ENODEV; + } + + goto Error; + } + + m_buffers[bufferIndex].Mmap.reset(cpuMap, mmapSize); + m_buffers[bufferIndex].Data = static_cast(cpuMap) + m_pageSize; + } + } + + // Find the sample_ids for the new tracepoints. + for (uint32_t i = 0; i != m_bufferCount; i += 1) + { + if (!tpi.m_bufferFiles[i]) + { + continue; + } + + ReadFormat data; + error = tpi.Read(i, &data); + if (error != 0) + { + goto Error; + } + + pIds[cIdsAdded] = data.id; + cIdsAdded += 1; + + auto const added = m_tracepointInfoBySampleId.emplace(data.id, &tpi).second; + assert(added); + (void)added; + } + + assert(cIdsAdded == nonzeroBufferCount); + + // Success. Commit it. (No exceptions beyond this point.) + + if (!m_bufferLeaderFiles) + { + m_bufferLeaderFiles = tpi.m_bufferFiles.get(); // Commit this event as the leader. + } + + goto Done; + } + catch (...) + { + error = ENOMEM; + goto Error; + } + +Error: + + for (uint32_t i = 0; i != cIdsAdded; i += 1) + { + if (m_buffers[i].Size == 0) + { + continue; + } + + m_tracepointInfoBySampleId.erase(pIds[i]); + } + + // May or may not have been added yet. If not, erase does nothing. + m_tracepointInfoByCommonType.erase(metadata.Id()); + +Done: + + return error; +} + +uint32_t +TracepointSession::CalculateBufferCount(TracepointSessionOptions const& options) noexcept +{ + auto const cpuCount = static_cast(sysconf(_SC_NPROCESSORS_ONLN)); + assert(cpuCount > 0); + assert(cpuCount < 0x10000000); + + uint32_t bufferCount; + if (options.m_cpuBufferSizes == nullptr && options.m_cpuBufferSizesCount == UINT32_MAX) + { + // Either they used the perCpuBufferSize constructor or they passed + // garbage parameters to the cpuBufferSizes constructor. + // Use one buffer per CPU. + bufferCount = cpuCount; + } + else + { + // They used the cpuBufferSizes constructor. + // Check TracepointSessionOptions constructor preconditions. + assert(options.m_cpuBufferSizes != nullptr); + assert(options.m_cpuBufferSizesCount > 0); + assert(options.m_cpuBufferSizesCount < 0x10000000); + + // Each buffer may have a different size, and some sizes may be zero. + // Don't waste space tracking zero-sized buffers. + bufferCount = std::min(cpuCount, options.m_cpuBufferSizesCount); + while (bufferCount > 1 && options.m_cpuBufferSizes[bufferCount - 1] == 0) + { + bufferCount -= 1; + } + } + + return bufferCount; +} + +std::unique_ptr +TracepointSession::MakeBufferInfos( + uint32_t bufferCount, + uint32_t pageSize, + TracepointSessionOptions const& options) noexcept(false) +{ + assert(bufferCount > 0); + assert(bufferCount < 0x10000000); + assert(pageSize != 0); + assert((pageSize & (pageSize - 1)) == 0); + + auto buffers = std::make_unique(bufferCount); + + if (options.m_cpuBufferSizes == nullptr && options.m_cpuBufferSizesCount == UINT32_MAX) + { + // Either they used the perCpuBufferSize constructor or they passed + // garbage parameters to the cpuBufferSizes constructor. + // All buffers will have the same non-zero size. + auto const bufferSize = RoundUpBufferSize(pageSize, options.m_perCpuBufferSize); + for (auto i = 0u; i != bufferCount; i += 1) + { + buffers[i].Size = bufferSize; + } + } + else + { + assert(bufferCount <= options.m_cpuBufferSizesCount); // Ensured by CalculateBufferCount. + + // They used the cpuBufferSizes constructor. + // Each buffer may have a different size, and some sizes may be zero. + for (auto i = 0u; i != bufferCount; i += 1) + { + auto const bufferSize = options.m_cpuBufferSizes[i] != 0 + ? RoundUpBufferSize(pageSize, options.m_cpuBufferSizes[i]) + : 0u; + buffers[i].Size = bufferSize; + } + } + + return buffers; +} diff --git a/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/src/TracepointSpec.cpp b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/src/TracepointSpec.cpp new file mode 100644 index 0000000000000..c46cdaa0bafb2 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/src/TracepointSpec.cpp @@ -0,0 +1,306 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include +#include +#include + +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); +} diff --git a/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/src/UniqueHandles.cpp b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/src/UniqueHandles.cpp new file mode 100644 index 0000000000000..d66b0a45dc8bc --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/src/UniqueHandles.cpp @@ -0,0 +1,140 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include + +#include +#include + +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; +} diff --git a/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/src/tracepoint-controlConfig.cmake.in b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/src/tracepoint-controlConfig.cmake.in new file mode 100644 index 0000000000000..0bbb5b0efb34f --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/src/tracepoint-controlConfig.cmake.in @@ -0,0 +1,2 @@ +@PACKAGE_INIT@ +include("${CMAKE_CURRENT_LIST_DIR}/tracepoint-controlTargets.cmake") diff --git a/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/tools/CMakeLists.txt b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/tools/CMakeLists.txt new file mode 100644 index 0000000000000..0e983fa789e9f --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/tools/CMakeLists.txt @@ -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) diff --git a/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/tools/tracepoint-collect.cpp b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/tools/tracepoint-collect.cpp new file mode 100644 index 0000000000000..97314fc56a1de --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-control-cpp/tools/tracepoint-collect.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 + 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 Read additional TracepointSpecs from . Each line in + the file is treated as a TracepointSpec. Empty lines and + lines starting with '#' are ignored. + +-o, --output Set the output filename. The default is "./perf.data". + +-w, --wakeup 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 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& 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& 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 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(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 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(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(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(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 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; +} diff --git a/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/CMakeLists.txt b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/CMakeLists.txt new file mode 100644 index 0000000000000..350e7535f3c13 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/CMakeLists.txt @@ -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) diff --git a/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/README.md b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/README.md new file mode 100644 index 0000000000000..c9d378753c32f --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/README.md @@ -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. diff --git a/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/include/tracepoint/PerfByteReader.h b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/include/tracepoint/PerfByteReader.h new file mode 100644 index 0000000000000..7aeb628c4ca46 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/include/tracepoint/PerfByteReader.h @@ -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 +#include // memcpy + +#ifdef _WIN32 +#include +#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 + 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 + ValType + Read(_In_ ValType const* pSrc) const noexcept + { + return ReadAs(pSrc); + } + }; +} +// namespace tracepoint_decode + +#endif // _included_PerfByteReader_h diff --git a/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/include/tracepoint/PerfDataFile.h b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/include/tracepoint/PerfDataFile.h new file mode 100644 index 0000000000000..f2805d0d78f4c --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/include/tracepoint/PerfDataFile.h @@ -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 +#include // FILE +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#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 attrStorage; + std::unique_ptr idsStorage; + }; + + uint64_t m_filePos; + uint64_t m_fileLen; + uint64_t m_dataBeginFilePos; + uint64_t m_dataEndFilePos; + FILE* m_file; + std::vector m_eventData; + std::vector m_headers[PERF_HEADER_LAST_FEATURE]; // Stored file-endian. + std::vector m_eventDescList; // Stored host-endian. Name points into m_headers. + std::map 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 m_ftraces; // Points into m_headers. + std::map 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 pAttr, + uint32_t cbAttrCopied, + _In_z_ char const* pName, + _In_reads_bytes_(cbIdsFileEndian) void const* pbIdsFileEndian, + uintptr_t cbIdsFileEndian) noexcept(false); + + template + _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 diff --git a/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/include/tracepoint/PerfDataFileDefs.h b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/include/tracepoint/PerfDataFileDefs.h new file mode 100644 index 0000000000000..2b8fe884c7133 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/include/tracepoint/PerfDataFileDefs.h @@ -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 + +#ifdef _WIN32 +#include +#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 diff --git a/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/include/tracepoint/PerfDataFileWriter.h b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/include/tracepoint/PerfDataFileWriter.h new file mode 100644 index 0000000000000..b1dfff974f97a --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/include/tracepoint/PerfDataFileWriter.h @@ -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 +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#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 m_eventDescs; + std::map m_tracepointInfoByCommonType; + std::vector m_headers[PERF_HEADER_LAST_FEATURE]; + + uint32_t m_tracingDataPageSize; + uint8_t m_tracingDataLongSize; + std::vector m_tracingDataHeaderPage; + std::vector m_tracingDataHeaderEvent; + std::vector> m_tracingDataFtraces; + std::vector m_tracingDataKallsyms; + std::vector m_tracingDataPrintk; + std::vector 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 diff --git a/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/include/tracepoint/PerfEventAbi.h b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/include/tracepoint/PerfEventAbi.h new file mode 100644 index 0000000000000..160696293a401 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/include/tracepoint/PerfEventAbi.h @@ -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 + +#ifdef _WIN32 +#include +#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 diff --git a/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/include/tracepoint/PerfEventInfo.h b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/include/tracepoint/PerfEventInfo.h new file mode 100644 index 0000000000000..ec193a0d2af5a --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/include/tracepoint/PerfEventInfo.h @@ -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 + +#ifdef _WIN32 +#include +#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 diff --git a/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/include/tracepoint/PerfEventMetadata.h b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/include/tracepoint/PerfEventMetadata.h new file mode 100644 index 0000000000000..a74b5e9919fdc --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/include/tracepoint/PerfEventMetadata.h @@ -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 +#include +#include + +#ifdef _WIN32 +#include +#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 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 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 diff --git a/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/include/tracepoint/PerfEventSessionInfo.h b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/include/tracepoint/PerfEventSessionInfo.h new file mode 100644 index 0000000000000..bf0ce0b455c76 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/include/tracepoint/PerfEventSessionInfo.h @@ -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 + +#ifdef _WIN32 +#include +#endif +#ifndef _Out_ +#define _Out_ +#endif + +namespace tracepoint_decode +{ + // Semantics equivalent to struct timespec from . + 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 diff --git a/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/samples/CMakeLists.txt b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/samples/CMakeLists.txt new file mode 100644 index 0000000000000..5a9d02a267e62 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/samples/CMakeLists.txt @@ -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) diff --git a/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/samples/perf-file-rewrite.cpp b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/samples/perf-file-rewrite.cpp new file mode 100644 index 0000000000000..c1717492a252c --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/samples/perf-file-rewrite.cpp @@ -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 +#include +#include +#include +#include + +#include +#include +#include + +#ifdef _WIN32 +#define strerror_r(errnum, buf, buflen) (strerror_s(buf, buflen, errnum), buf) +#define UNLINK(filename) _unlink(filename) +#else +#include +#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& sampleIdsUsed, + std::vector& 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(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 sampleIdsBuffer; + std::unordered_set 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(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(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; +} diff --git a/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/src/CMakeLists.txt b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/src/CMakeLists.txt new file mode 100644 index 0000000000000..feecd7f935b95 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/src/CMakeLists.txt @@ -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 + "$" + "$") +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") diff --git a/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/src/PerfByteReader.cpp b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/src/PerfByteReader.cpp new file mode 100644 index 0000000000000..72dfe8d9d3270 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/src/PerfByteReader.cpp @@ -0,0 +1,121 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include +#include +#include + +#ifdef _WIN32 +#include +#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 +#include +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(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(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(pSrc); + break; + case 2: + result = ReadAsU16(pSrc); + break; + case 4: + result = ReadAsU32(pSrc); + break; + case 8: + result = ReadAsU64(pSrc); + break; + } + return result; +} diff --git a/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/src/PerfDataFile.cpp b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/src/PerfDataFile.cpp new file mode 100644 index 0000000000000..90100f4978a58 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/src/PerfDataFile.cpp @@ -0,0 +1,1803 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace std::string_view_literals; +using namespace tracepoint_decode; + +#ifdef _WIN32 +#include // _setmode +#include // _O_BINARY +#define FSEEK64(file, offset, origin) _fseeki64(file, offset, origin) +#define FTELL64(file) _ftelli64(file) +#define FOPEN(path, mode) _fsopen(path, mode, _SH_DENYWR) +#define bswap_64(n) _byteswap_uint64(n) +#else // _WIN32 +#define FSEEK64(file, offset, origin) fseeko64(file, offset, origin) +#define FTELL64(file) ftello64(file) +#define FOPEN(path, mode) fopen(path, mode) +#include +#endif // _WIN32 + +#ifndef _Inout_ +#define _Inout_ +#endif +#ifndef _In_reads_bytes_ +#define _In_reads_bytes_(cbAttr) +#endif + +#define IF_EQUAL_GOTO_ERROR(index, count) \ + if (index == count) \ + { \ + error = EINVAL; \ + goto Error; \ + } \ + +// From: perf.data-file-format.txt +struct PerfDataFile::perf_file_section { + uint64_t offset; // offset from start of file + uint64_t size; // size of the section + + // Reverse the endian order of all fields in this struct. + void ByteSwap() noexcept + { + offset = bswap_64(offset); + size = bswap_64(size); + } +}; + +struct PerfDataFile::perf_pipe_header { + static constexpr uint64_t Magic2 = 0x32454C4946524550; // "PERFILE2" + + uint64_t magic; // If correctly byte-swapped, this will be equal to Magic2. + uint64_t size; // size of the header + + // Return true if we should initialize the ByteReader as big-endian. + bool CannotBeLittleEndian() const noexcept + { + // Little-endian file starts with "PERFILE2". + // If it doesn't start with 'P' it can't be little-endian. + return 'P' != *reinterpret_cast(this); + } + + // Reverse the endian order of all fields in this struct. + void ByteSwap() noexcept + { + magic = bswap_64(magic); + size = bswap_64(size); + } +}; + +// From: perf.data-file-format.txt +struct PerfDataFile::perf_file_header { + perf_pipe_header pipe_header; + uint64_t attr_size; // size of (perf_event_attrs + perf_file_section) in attrs. + perf_file_section attrs; + perf_file_section data; + perf_file_section event_types; // Not used. + + // 256-bit bitmap based on HEADER_BITS + uint64_t flags[4]; + + // Reverse the endian order of all fields in this struct. + void ByteSwap() noexcept + { + pipe_header.ByteSwap(); + attr_size = bswap_64(attr_size); + attrs.ByteSwap(); + data.ByteSwap(); + event_types.ByteSwap(); + for (auto& flag : flags) + { + flag = bswap_64(flag); + } + } +}; + +template +static constexpr unsigned +ArrayCount(T(&)[N]) noexcept +{ + return N; +} + +PerfDataFile::~PerfDataFile() noexcept +{ + if (m_file) + { + fclose(m_file); + } +} + +PerfDataFile::PerfDataFile() noexcept + : m_filePos(0) + , m_fileLen(0) + , m_dataBeginFilePos(0) + , m_dataEndFilePos(0) + , m_file(0) + , m_eventData() + , m_headers() + , m_eventDescList() + , m_eventDescById() + , m_sessionInfo() + , m_byteReader() + , m_sampleIdOffset(-1) + , m_nonSampleIdOffset(-1) + , m_commonTypeOffset(-1) + , m_commonTypeSize(0) + , m_parsedHeaderEventDesc(0) + , m_parsedTracingData(0) + , m_tracingDataLongSize(0) + , m_tracingDataPageSize(0) +{ + return; +} + +bool +PerfDataFile::FileBigEndian() const noexcept +{ + return m_byteReader.BigEndian(); +} + +PerfByteReader +PerfDataFile::ByteReader() const noexcept +{ + return m_byteReader; +} + +uint64_t +PerfDataFile::FilePos() const noexcept +{ + return m_filePos; +} + +uint64_t +PerfDataFile::DataBeginFilePos() const noexcept +{ + return m_dataBeginFilePos; +} + +uint64_t +PerfDataFile::DataEndFilePos() const noexcept +{ + return m_dataEndFilePos; +} + +uintptr_t +PerfDataFile::EventDescCount() const noexcept +{ + return m_eventDescList.size(); +} + +PerfEventDesc const& +PerfDataFile::EventDesc(uintptr_t eventDescIndex) const noexcept +{ + assert(eventDescIndex < m_eventDescList.size()); + return m_eventDescList[eventDescIndex]; +} + +_Ret_opt_ PerfEventDesc const* +PerfDataFile::FindEventDescById(uint64_t sampleId) const noexcept +{ + auto const it = m_eventDescById.find(sampleId); + return it != m_eventDescById.end() + ? &m_eventDescList[it->second] + : nullptr; +} + +std::string_view +PerfDataFile::Header(PerfHeaderIndex headerIndex) const noexcept +{ + return headerIndex < ArrayCount(m_headers) + ? std::string_view(m_headers[headerIndex].data(), m_headers[headerIndex].size()) + : std::string_view(); +} + +uint8_t +PerfDataFile::TracingDataLongSize() const noexcept +{ + return m_tracingDataLongSize; +} + +uint32_t +PerfDataFile::TracingDataPageSize() const noexcept +{ + return m_tracingDataPageSize; +} + +std::string_view +PerfDataFile::TracingDataHeaderPage() const noexcept +{ + return m_headerPage; +} + +std::string_view +PerfDataFile::TracingDataHeaderEvent() const noexcept +{ + return m_headerEvent; +} + +std::string_view const* +PerfDataFile::TracingDataFtraces() const noexcept +{ + return m_ftraces.data(); +} + +uint32_t +PerfDataFile::TracingDataFtraceCount() const noexcept +{ + return static_cast(m_ftraces.size()); +} + +std::string_view +PerfDataFile::TracingDataKallsyms() const noexcept +{ + return m_kallsyms; +} + +std::string_view +PerfDataFile::TracingDataPrintk() const noexcept +{ + return m_printk; +} + +std::string_view +PerfDataFile::TracingDataSavedCmdLine() const noexcept +{ + return m_cmdline; +} + +void +PerfDataFile::Close() noexcept +{ + m_filePos = 0; + m_fileLen = 0; + m_dataBeginFilePos = 0; + m_dataEndFilePos = 0; + + if (m_file != nullptr && m_file != stdin) + { + fclose(m_file); + } + + m_file = nullptr; + + for (auto& header : m_headers) + { + header.clear(); + } + + m_eventDescList.clear(); + m_eventDescById.clear(); + m_byteReader = PerfByteReader(); + m_sampleIdOffset = -1; + m_nonSampleIdOffset = -1; + m_commonTypeOffset = -1; + m_commonTypeSize = 0; + m_parsedHeaderEventDesc = false; + + // HEADER_TRACING_DATA + m_parsedTracingData = false; + m_tracingDataLongSize = 0; + m_tracingDataPageSize = 0; + m_headerPage = {}; + m_headerEvent = {}; + m_ftraces.clear(); + m_metadataById.clear(); + m_kallsyms = {}; + m_printk = {}; + m_cmdline = {}; +} + +_Success_(return == 0) int +PerfDataFile::Open(_In_z_ char const* filePath) noexcept +{ + int error; + perf_file_header header; + + Close(); + m_file = FOPEN(filePath, "rb"); + if (!m_file) + { + error = errno; + } + else if (0 != (error = FileRead(&header.pipe_header, sizeof(header.pipe_header)))) + { + // error already set. + } + else + { + m_byteReader = PerfByteReader(header.pipe_header.CannotBeLittleEndian()); + auto const headerMagic = m_byteReader.ReadAsU64(&header.pipe_header.magic); + auto const headerSize = m_byteReader.ReadAsU64(&header.pipe_header.size); + if (headerMagic != header.pipe_header.Magic2) + { + error = EINVAL; // Bad magic + } + else if (!EnsureEventDataSize(0x10000)) + { + error = ENOMEM; + } + else if (headerSize == sizeof(header.pipe_header)) + { + // Pipe mode, no metadata section, no seeking needed. + assert(m_filePos == sizeof(header.pipe_header)); + m_dataBeginFilePos = sizeof(header.pipe_header); + m_dataEndFilePos = UINT64_MAX; + error = 0; + } + else if (headerSize < sizeof(header)) + { + error = EINVAL; + } + else if ( + 0 != (error = UpdateFileLen()) || + 0 != (error = FileSeekAndRead( + sizeof(header.pipe_header), + &header.pipe_header + 1, + sizeof(header) - sizeof(header.pipe_header)))) + { + // error already set. + } + else + { + if (m_byteReader.ByteSwapNeeded()) + { + header.ByteSwap(); + } + + if (!SectionValid(header.attrs) || + !SectionValid(header.data) || + !SectionValid(header.event_types)) + { + error = EINVAL; + } + else if ( + 0 != (error = LoadAttrs(header.attrs, header.attr_size)) || + 0 != (error = LoadHeaders(header.data, header.flags[0])) || + 0 != (error = FileSeek(header.data.offset))) + { + // error already set. + } + else + { + assert(m_filePos == header.data.offset); + m_dataBeginFilePos = header.data.offset; + m_dataEndFilePos = header.data.offset + header.data.size; + ParseHeaderClockid(); + ParseHeaderClockData(); + ParseHeaderEventDesc(); + ParseTracingData(); + error = 0; + } + } + } + + return error; +} + +_Success_(return == 0) int +PerfDataFile::OpenStdin() noexcept +{ + int error; + perf_file_header header; + + Close(); + +#ifdef _WIN32 + + if (_setmode(_fileno(stdin), _O_BINARY) < 0) + { + return errno; + } + +#endif + + m_file = stdin; + if (0 != (error = FileRead(&header.pipe_header, sizeof(header.pipe_header)))) + { + // error already set. + } + else + { + m_byteReader = PerfByteReader(header.pipe_header.CannotBeLittleEndian()); + auto const headerMagic = m_byteReader.ReadAsU64(&header.pipe_header.magic); + auto const headerSize = m_byteReader.ReadAsU64(&header.pipe_header.size); + if (headerMagic != header.pipe_header.Magic2) + { + error = EINVAL; // Bad magic + } + else if (!EnsureEventDataSize(0x10000)) + { + error = ENOMEM; + } + else if (headerSize == sizeof(header.pipe_header)) + { + // Pipe mode, no metadata section, no seeking needed. + assert(m_filePos == sizeof(header.pipe_header)); + m_dataBeginFilePos = sizeof(header.pipe_header); + m_dataEndFilePos = UINT64_MAX; + error = 0; + } + else + { + // Don't try to seek on stdin. + error = ENOTSUP; + } + } + + return error; +} + +template +_Success_(return == 0) int +PerfDataFile::ReadPostEventData(uint16_t eventSizeFromHeader) noexcept +{ + if (eventSizeFromHeader < sizeof(perf_event_header) + sizeof(SizeType)) + { + return EINVAL; + } + + auto const specialDataSize = m_byteReader.ReadAs( + m_eventData.data() + sizeof(perf_event_header)); + if (specialDataSize > 0x80000000 || 0 != (specialDataSize & 7u)) + { + return EINVAL; + } + else if (auto const specialDataSize32 = static_cast(specialDataSize); + !EnsureEventDataSize(eventSizeFromHeader + specialDataSize32)) + { + return ENOMEM; + } + else if (specialDataSize32 > m_dataEndFilePos - m_filePos) + { + return EINVAL; + } + else + { + return FileRead(m_eventData.data() + eventSizeFromHeader, specialDataSize32); + } +} + +_Success_(return == 0) int +PerfDataFile::ReadEvent(_Outptr_result_maybenull_ perf_event_header const** ppEventHeader) noexcept +{ + int error; + + try + { + auto const eventStartFilePos = m_filePos; + + if (eventStartFilePos >= m_dataEndFilePos) + { + error = m_filePos == m_dataEndFilePos && m_filePos != UINT64_MAX + ? 0 // normal-mode has reached EOF. + : EPIPE; // Calling ReadEvent after error/EOF. + goto ErrorOrEof; + } + + assert(m_eventData.size() >= 0x10000); // Should be resized during Open(). + if (sizeof(perf_event_header) > m_dataEndFilePos - m_filePos) + { + error = EINVAL; + goto ErrorOrEof; + } + + if (0 != (error = FileRead(m_eventData.data(), sizeof(perf_event_header)))) + { + if (error == EPIPE && + m_filePos == eventStartFilePos && + m_filePos != UINT64_MAX && + m_dataEndFilePos == UINT64_MAX) + { + error = 0; // pipe-mode has reached EOF. + } + goto ErrorOrEof; + } + + if (m_byteReader.ByteSwapNeeded()) + { + reinterpret_cast(m_eventData.data())->ByteSwap(); + } + + auto const eventHeaderSize = reinterpret_cast(m_eventData.data())->size; + auto const eventHeaderType = reinterpret_cast(m_eventData.data())->type; + + if (eventHeaderSize < sizeof(perf_event_header)) + { + error = EINVAL; + goto ErrorOrEof; + } + + auto const cbEventData = static_cast(eventHeaderSize - sizeof(perf_event_header)); + if (cbEventData > m_dataEndFilePos - m_filePos) + { + error = EINVAL; + goto ErrorOrEof; + } + + if (0 != (error = FileRead(m_eventData.data() + sizeof(perf_event_header), cbEventData))) + { + goto ErrorOrEof; + } + + // Successfully read the basic event data. + // Check for any special cases based on the type. + switch (eventHeaderType) + { + case PERF_RECORD_HEADER_ATTR: + { + if (cbEventData >= PERF_ATTR_SIZE_VER0) + { + auto const pbEventData = m_eventData.data() + sizeof(perf_event_header); + auto const attrSize = m_byteReader.Read(&reinterpret_cast(pbEventData)->size); + if (attrSize > cbEventData) + { + error = EINVAL; + goto ErrorOrEof; + } + + auto const cbAttrInEvent = static_cast(attrSize); + auto const cbAttrToCopy = std::min(cbAttrInEvent, sizeof(perf_event_attr)); + + auto pAttr = std::make_unique(); + memcpy(pAttr.get(), pbEventData, cbAttrToCopy); + error = AddAttr( + std::move(pAttr), cbAttrToCopy, "", pbEventData + cbAttrInEvent, cbEventData - cbAttrInEvent); + if (error) + { + goto ErrorOrEof; + } + } + break; + } + case PERF_RECORD_HEADER_TRACING_DATA: + { + // Note: ReadPostEventData may cause m_eventData to reallocate. + if (0 != (error = ReadPostEventData(eventHeaderSize))) + { + goto ErrorOrEof; + } + else if (!m_parsedTracingData) + { + auto const pbEventData = m_eventData.data() + sizeof(perf_event_header); + auto const len = m_byteReader.ReadAsU32(pbEventData); + + // ReadPostEventData ensures this. + assert(sizeof(perf_event_header) + sizeof(uint32_t) + len <= m_eventData.size()); + + auto& header = m_headers[PERF_HEADER_TRACING_DATA]; + header.resize(len); + memcpy(header.data(), pbEventData + sizeof(uint32_t), len); + ParseTracingData(); + } + break; + } + case PERF_RECORD_HEADER_BUILD_ID: + { + auto const pbEventData = m_eventData.data() + sizeof(perf_event_header); + auto& header = m_headers[PERF_HEADER_BUILD_ID]; + header.resize(cbEventData); + memcpy(header.data(), pbEventData, cbEventData); + break; + } + case PERF_RECORD_AUXTRACE: + { + // Note: ReadPostEventData may cause m_eventData to reallocate. + if (0 != (error = ReadPostEventData(eventHeaderSize))) + { + goto ErrorOrEof; + } + break; + } + case PERF_RECORD_HEADER_FEATURE: + { + if (cbEventData >= sizeof(uint64_t)) + { + auto const pbEventData = m_eventData.data() + sizeof(perf_event_header); + auto const bit = m_byteReader.ReadAsU64(pbEventData); + if (bit < ArrayCount(m_headers)) + { + auto& header = m_headers[static_cast(bit)]; + header.resize(cbEventData - sizeof(uint64_t)); + memcpy( + header.data(), + pbEventData + sizeof(uint64_t), + cbEventData - sizeof(uint64_t)); + + switch (bit) + { + case PERF_HEADER_CLOCKID: + ParseHeaderClockid(); + break; + case PERF_HEADER_CLOCK_DATA: + ParseHeaderClockData(); + break; + } + } + } + break; + } + case PERF_RECORD_FINISHED_INIT: + { + ParseHeaderEventDesc(); + break; + } + default: + break; + } + + if (m_filePos > m_dataEndFilePos) + { + error = EINVAL; + goto ErrorOrEof; + } + + *ppEventHeader = reinterpret_cast(m_eventData.data()); + return 0; + } + catch (std::bad_alloc const&) + { + error = ENOMEM; + } + +ErrorOrEof: + + m_filePos = UINT64_MAX; // Subsequent ReadEvent should get EPIPE. + *ppEventHeader = nullptr; + return error; +} + +uint32_t +PerfDataFile::EventDataSize(perf_event_header const* pEventHeader) noexcept +{ + uint32_t size; + + // A few event types have data beyond what the header defines. + switch (pEventHeader->type) + { + default: + assert(pEventHeader->size >= sizeof(perf_event_header)); // ReadEvent would have failed otherwise. + size = pEventHeader->size; + break; + + case PERF_RECORD_HEADER_TRACING_DATA: + assert(pEventHeader->size >= sizeof(perf_event_header) + sizeof(uint64_t)); + size = static_cast(pEventHeader->size + m_byteReader.ReadAs(pEventHeader + 1)); + break; + + case PERF_RECORD_AUXTRACE: + assert(pEventHeader->size >= sizeof(perf_event_header) + sizeof(uint32_t)); + size = static_cast(pEventHeader->size + m_byteReader.ReadAs(pEventHeader + 1)); + break; + } + + return size; +} + +_Success_(return == 0) int +PerfDataFile::GetSampleEventInfo( + _In_ perf_event_header const* pEventHeader, + _Out_ PerfSampleEventInfo* pInfo) const noexcept +{ + auto const SupportedSampleTypes = 0 + | PERF_SAMPLE_IDENTIFIER + | PERF_SAMPLE_IP + | PERF_SAMPLE_TID + | PERF_SAMPLE_TIME + | PERF_SAMPLE_ADDR + | PERF_SAMPLE_ID + | PERF_SAMPLE_STREAM_ID + | PERF_SAMPLE_CPU + | PERF_SAMPLE_PERIOD + | PERF_SAMPLE_READ + | PERF_SAMPLE_CALLCHAIN + | PERF_SAMPLE_RAW; + + int error; + uint64_t id; + + error = GetSampleEventId(pEventHeader, &id); + if (!error) + { + auto eventDescIt = m_eventDescById.find(id); + if (eventDescIt == m_eventDescById.end()) + { + error = ENOENT; + goto Error; + } + + auto const& eventDesc = m_eventDescList[eventDescIt->second]; + auto const infoSampleTypes = eventDesc.attr->sample_type & SupportedSampleTypes; + char const* infoRawData = nullptr; + uint32_t infoRawDataSize = 0; + + auto const pArray = reinterpret_cast(pEventHeader); + auto const cArray = pEventHeader->size / sizeof(uint64_t); + assert(cArray >= 2); // Otherwise GetSampleEventId would have failed. + size_t iArray = 1; + + if (infoSampleTypes & PERF_SAMPLE_IDENTIFIER) + { + iArray += 1; + } + + if (infoSampleTypes & PERF_SAMPLE_IP) + { + IF_EQUAL_GOTO_ERROR(iArray, cArray); + pInfo->ip = m_byteReader.Read(&pArray[iArray]); + iArray += 1; + } + + if (infoSampleTypes & PERF_SAMPLE_TID) + { + IF_EQUAL_GOTO_ERROR(iArray, cArray); + auto const* p32 = reinterpret_cast(&pArray[iArray]); + pInfo->pid = m_byteReader.Read(&p32[0]); + pInfo->tid = m_byteReader.Read(&p32[1]); + iArray += 1; + } + + if (infoSampleTypes & PERF_SAMPLE_TIME) + { + IF_EQUAL_GOTO_ERROR(iArray, cArray); + pInfo->time = m_byteReader.Read(&pArray[iArray]); + iArray += 1; + } + + if (infoSampleTypes & PERF_SAMPLE_ADDR) + { + IF_EQUAL_GOTO_ERROR(iArray, cArray); + pInfo->addr = m_byteReader.Read(&pArray[iArray]); + iArray += 1; + } + + if (infoSampleTypes & PERF_SAMPLE_ID) + { + IF_EQUAL_GOTO_ERROR(iArray, cArray); + iArray += 1; + } + + if (infoSampleTypes & PERF_SAMPLE_STREAM_ID) + { + IF_EQUAL_GOTO_ERROR(iArray, cArray); + pInfo->stream_id = m_byteReader.Read(&pArray[iArray]); + iArray += 1; + } + + if (infoSampleTypes & PERF_SAMPLE_CPU) + { + IF_EQUAL_GOTO_ERROR(iArray, cArray); + auto const* p32 = reinterpret_cast(&pArray[iArray]); + pInfo->cpu = m_byteReader.Read(&p32[0]); + pInfo->cpu_reserved = m_byteReader.Read(&p32[1]); + iArray += 1; + } + + if (infoSampleTypes & PERF_SAMPLE_PERIOD) + { + IF_EQUAL_GOTO_ERROR(iArray, cArray); + pInfo->period = m_byteReader.Read(&pArray[iArray]); + iArray += 1; + } + + if (infoSampleTypes & PERF_SAMPLE_READ) + { + IF_EQUAL_GOTO_ERROR(iArray, cArray); + pInfo->read_values = &pArray[iArray]; + + auto const SupportedReadFormats = 0 + | PERF_FORMAT_TOTAL_TIME_ENABLED + | PERF_FORMAT_TOTAL_TIME_RUNNING + | PERF_FORMAT_ID + | PERF_FORMAT_GROUP + | PERF_FORMAT_LOST; + auto const attrReadFormat = eventDesc.attr->read_format; + if (attrReadFormat & ~SupportedReadFormats) + { + error = ENOTSUP; + goto Error; + } + else if (attrReadFormat & PERF_FORMAT_GROUP) + { + IF_EQUAL_GOTO_ERROR(iArray, cArray); + auto const cValues = m_byteReader.Read(&pArray[iArray]); + + auto const cStaticItems = 1u // cValues + + (0 != (attrReadFormat & PERF_FORMAT_TOTAL_TIME_ENABLED)) + + (0 != (attrReadFormat & PERF_FORMAT_TOTAL_TIME_RUNNING)); + auto const cPerValue = 1u // value + + (0 != (attrReadFormat & PERF_FORMAT_ID)) + + (0 != (attrReadFormat & PERF_FORMAT_LOST)); + auto const cItems = cStaticItems + static_cast(cValues) * cPerValue; + if (cValues >= 0x10000 / sizeof(uint64_t) || + cArray - iArray < cItems) + { + error = EINVAL; + goto Error; + } + + iArray += cItems; + } + else + { + auto const cItems = 1u // value + + (0 != (attrReadFormat & PERF_FORMAT_TOTAL_TIME_ENABLED)) + + (0 != (attrReadFormat & PERF_FORMAT_TOTAL_TIME_RUNNING)) + + (0 != (attrReadFormat & PERF_FORMAT_ID)) + + (0 != (attrReadFormat & PERF_FORMAT_LOST)); + if (cArray - iArray < cItems) + { + error = EINVAL; + goto Error; + } + + iArray += cItems; + } + } + + if (infoSampleTypes & PERF_SAMPLE_CALLCHAIN) + { + IF_EQUAL_GOTO_ERROR(iArray, cArray); + auto const infoCallchain = &pArray[iArray]; + pInfo->callchain = infoCallchain; + auto const count = m_byteReader.Read(infoCallchain); + iArray += 1; + + if (cArray - iArray < count) + { + error = EINVAL; + goto Error; + } + iArray += static_cast(count); + } + + if (infoSampleTypes & PERF_SAMPLE_RAW) + { + IF_EQUAL_GOTO_ERROR(iArray, cArray); + auto const* p32 = reinterpret_cast(&pArray[iArray]); + infoRawDataSize = m_byteReader.Read(&p32[0]); + infoRawData = reinterpret_cast(p32 + 1); + if ((cArray - iArray) * sizeof(uint64_t) - sizeof(uint32_t) < infoRawDataSize) + { + error = EINVAL; + goto Error; + } + + iArray += (sizeof(uint32_t) + infoRawDataSize + sizeof(uint64_t) - 1) / sizeof(uint64_t); + } + + assert(iArray <= cArray); + pInfo->event_desc = &eventDesc; + pInfo->session_info = &m_sessionInfo; + pInfo->header = pEventHeader; + pInfo->id = id; + pInfo->raw_data = infoRawData; + pInfo->raw_data_size = infoRawDataSize; + return 0; + } + +Error: + + pInfo->event_desc = {}; + pInfo->session_info = {}; + pInfo->header = {}; + pInfo->id = {}; + pInfo->raw_data = {}; + pInfo->raw_data_size = {}; + return error; +} + +_Success_(return == 0) int +PerfDataFile::GetNonSampleEventInfo( + _In_ perf_event_header const* pEventHeader, + _Out_ PerfNonSampleEventInfo* pInfo) const noexcept +{ + auto const SupportedSampleTypes = 0 + | PERF_SAMPLE_TID + | PERF_SAMPLE_TIME + | PERF_SAMPLE_ID + | PERF_SAMPLE_STREAM_ID + | PERF_SAMPLE_CPU + | PERF_SAMPLE_IDENTIFIER; + + int error; + uint64_t id; + + error = GetNonSampleEventId(pEventHeader, &id); + if (!error) + { + auto eventDescIt = m_eventDescById.find(id); + if (eventDescIt == m_eventDescById.end()) + { + error = ENOENT; + goto Error; + } + + auto const& eventDesc = m_eventDescList[eventDescIt->second]; + auto const infoSampleTypes = eventDesc.attr->sample_type & SupportedSampleTypes; + + auto const pArray = reinterpret_cast(pEventHeader); + auto const cArray = pEventHeader->size / sizeof(uint64_t); + assert(cArray >= 2); // Otherwise GetNonSampleEventId would have failed. + auto iArray = cArray; + + if (infoSampleTypes & PERF_SAMPLE_IDENTIFIER) + { + iArray -= 1; + } + + if (infoSampleTypes & PERF_SAMPLE_CPU) + { + iArray -= 1; + IF_EQUAL_GOTO_ERROR(iArray, 0); + auto const* p32 = reinterpret_cast(&pArray[iArray]); + pInfo->cpu = m_byteReader.Read(&p32[0]); + pInfo->cpu_reserved = m_byteReader.Read(&p32[1]); + } + + if (infoSampleTypes & PERF_SAMPLE_STREAM_ID) + { + iArray -= 1; + IF_EQUAL_GOTO_ERROR(iArray, 0); + pInfo->stream_id = m_byteReader.Read(&pArray[iArray]); + } + + if (infoSampleTypes & PERF_SAMPLE_ID) + { + iArray -= 1; + IF_EQUAL_GOTO_ERROR(iArray, 0); + } + + if (infoSampleTypes & PERF_SAMPLE_TIME) + { + iArray -= 1; + IF_EQUAL_GOTO_ERROR(iArray, 0); + pInfo->time = m_byteReader.Read(&pArray[iArray]); + } + + if (infoSampleTypes & PERF_SAMPLE_TID) + { + iArray -= 1; + IF_EQUAL_GOTO_ERROR(iArray, 0); + auto const* p32 = reinterpret_cast(&pArray[iArray]); + pInfo->pid = m_byteReader.Read(&p32[0]); + pInfo->tid = m_byteReader.Read(&p32[1]); + } + + assert(iArray > 0); + assert(iArray < 0x10000 / sizeof(uint64_t)); + pInfo->event_desc = &eventDesc; + pInfo->session_info = &m_sessionInfo; + pInfo->header = pEventHeader; + pInfo->id = id; + return 0; + } + +Error: + + pInfo->event_desc = {}; + pInfo->session_info = {}; + pInfo->header = {}; + pInfo->id = {}; + return error; +} + +_Success_(return == 0) int +PerfDataFile::LoadAttrs(perf_file_section const& attrs, uint64_t cbAttrAndIdSection64) noexcept +{ + int error; + + if (attrs.size > 0x80000000 || + cbAttrAndIdSection64 < PERF_ATTR_SIZE_VER0 + sizeof(perf_file_section) || + cbAttrAndIdSection64 > 0x10000) + { + error = EINVAL; + } + else try + { + error = 0; + + auto const cbAttrAndIdSection = static_cast(cbAttrAndIdSection64); + uint32_t const cbAttrInFile = cbAttrAndIdSection - sizeof(perf_file_section); + auto const cbAttrToCopy = std::min(cbAttrInFile, sizeof(perf_event_attr)); + + m_eventDescList.reserve(m_eventDescList.size() + static_cast(attrs.size) / cbAttrAndIdSection); + + auto const attrFilePosEnd = attrs.offset + attrs.size; + for (auto attrFilePos = attrs.offset; attrFilePos < attrFilePosEnd;) + { + auto pAttr = std::make_unique(); + error = FileSeekAndRead(attrFilePos, pAttr.get(), cbAttrToCopy); + attrFilePos += cbAttrInFile; + if (error) + { + break; + } + + perf_file_section section; + error = FileSeekAndRead(attrFilePos, §ion, sizeof(section)); + attrFilePos += sizeof(section); + if (error) + { + break; + } + + if (!SectionValid(section) || + 0 != (section.size & 7) || + section.size > 0x80000000) + { + error = EINVAL; + break; + } + + auto const sectionSize = static_cast(section.size); + + if (!EnsureEventDataSize(sectionSize)) + { + error = ENOMEM; + break; + } + + error = FileSeekAndRead(section.offset, m_eventData.data(), sectionSize); + if (error) + { + goto Done; + } + + error = AddAttr(std::move(pAttr), cbAttrToCopy, "", m_eventData.data(), sectionSize); + if (error) + { + goto Done; + } + } + } + catch (std::bad_alloc const&) + { + error = ENOMEM; + } + +Done: + + return error; +} + +_Success_(return == 0) int +PerfDataFile::LoadHeaders(perf_file_section const& data, uint64_t flags) noexcept +{ + int error; + + try + { + error = 0; + + auto filePos = data.offset + data.size; + uint64_t mask = 1; + for (auto& header : m_headers) + { + if (flags & mask) + { + perf_file_section section; + error = FileSeekAndRead(filePos, §ion, sizeof(section)); + filePos += sizeof(section); + if (error) + { + break; + } + + if (!SectionValid(section) || + section.size > 0x80000000) + { + error = EINVAL; + break; + } + + header.resize(static_cast(section.size)); + error = FileSeekAndRead(section.offset, header.data(), header.size()); + if (error) + { + break; + } + } + + mask <<= 1; + } + } + catch (std::bad_alloc const&) + { + error = ENOMEM; + } + + return error; +} + +// Returns pEnd - p. +static constexpr size_t +Remaining(char const* p, char const* pEnd) noexcept +{ + assert(p <= pEnd); + return static_cast(pEnd - p); +} + +// Expects NUL-terminated string. +// Sets sv = string value, not including NUL. +// On success, returns pointer to after the NUL. +// On failure, returns nullptr. +static char const* +ReadSz(char const* p, char const* pEnd, _Out_ std::string_view* sv) noexcept +{ + auto const max = Remaining(p, pEnd); + auto const len = strnlen(p, max); + if (len == max) + { + *sv = {}; + return nullptr; + } + else + { + *sv = { p, len }; + return p + len + 1; + } +} + +// Expects SizeType size + char value[size]. +// Sets sv = value. +// On success, returns pointer to after the value. +// On failure, returns nullptr. +template +static char const* +ReadSection( + PerfByteReader byteReader, + char const* p, + char const* pEnd, + _Out_ std::string_view* sv) noexcept +{ + if (Remaining(p, pEnd) < sizeof(SizeType)) + { + *sv = {}; + return nullptr; + } + + SizeType fullSize = byteReader.ReadAs(p); + p += sizeof(SizeType); + + if (Remaining(p, pEnd) < fullSize) + { + *sv = {}; + return nullptr; + } + + auto const size = static_cast(fullSize); + *sv = { p, size }; + return p + size; +} + +// Expects expectedName + NUL + SizeType size + char value[size]. +// On success, sets sv = value and returns pointer to after value. +// If expectedName not present, sets sv = {} and returns p. +// On error, returns nullptr. +template +static char const* +ReadNamedSection( + PerfByteReader byteReader, + char const* p, + char const* pEnd, + std::string_view expectedName, + _Out_ std::string_view* sv) noexcept +{ + auto const cchExpectedName = expectedName.size() + 1; + if (Remaining(p, pEnd) < cchExpectedName || + 0 != memcmp(p, expectedName.data(), cchExpectedName)) + { + *sv = {}; + return p; + } + + return ReadSection(byteReader, p + cchExpectedName, pEnd, sv); +} + +void +PerfDataFile::ParseTracingData() noexcept +{ + try + { + auto const& tracingData = m_headers[PERF_HEADER_TRACING_DATA]; + auto constexpr TracingSignature = "\x17\x08\x44tracing"sv; + if (tracingData.size() >= 10 && + 0 == memcmp(tracingData.data(), TracingSignature.data(), TracingSignature.size())) + { + m_parsedTracingData = true; + + std::string_view version; + + auto p = tracingData.data(); + auto const pEnd = p + tracingData.size(); + p += TracingSignature.size(); + + // Version + + p = ReadSz(p, pEnd, &version); + if (!p) + { + return; // Unexpected. + } + + auto const tracingDataVersion = strtod(version.data(), nullptr); + + // Big Endian, LongSize, PageSize + + if (Remaining(p, pEnd) < 1 + 1 + sizeof(uint32_t)) + { + return; // Unexpected. + } + + PerfByteReader byteReader(*p != 0); + p += 1; + + m_tracingDataLongSize = *p; + p += 1; + + m_tracingDataPageSize = byteReader.ReadAsU32(p); + p += sizeof(uint32_t); + + // header_page + + p = ReadNamedSection(byteReader, p, pEnd, "header_page"sv, &m_headerPage); + if (!p) + { + return; // Unexpected. + } + + // header_event (not really used anymore) + + p = ReadNamedSection(byteReader, p, pEnd, "header_event"sv, &m_headerEvent); + if (!p) + { + return; // Unexpected. + } + + // ftraces + + if (Remaining(p, pEnd) < sizeof(uint32_t)) + { + return; // Unexpected. + } + + auto const ftraceCount = byteReader.ReadAsU32(p); + p += sizeof(uint32_t); + m_ftraces.reserve(ftraceCount); + for (uint32_t ftraceIndex = 0; ftraceIndex != ftraceCount; ftraceIndex += 1) + { + std::string_view ftrace; + p = ReadSection(byteReader, p, pEnd, &ftrace); + if (!p) + { + return; // Unexpected. + } + + m_ftraces.push_back(ftrace); + } + + // systems (and events) + + if (Remaining(p, pEnd) < sizeof(uint32_t)) + { + return; // Unexpected. + } + + auto const systemCount = byteReader.ReadAsU32(p); + p += sizeof(uint32_t); + for (uint32_t systemIndex = 0; systemIndex != systemCount; systemIndex += 1) + { + std::string_view systemName; + p = ReadSz(p, pEnd, &systemName); + if (!p) + { + return; // Unexpected. + } + + if (Remaining(p, pEnd) < sizeof(uint32_t)) + { + return; // Unexpected. + } + + auto const eventCount = byteReader.ReadAsU32(p); + p += sizeof(uint32_t); + for (uint32_t eventIndex = 0; eventIndex != eventCount; eventIndex += 1) + { + std::string_view formatFileContents; + p = ReadSection(byteReader, p, pEnd, &formatFileContents); + if (!p) + { + return; // Unexpected. + } + + PerfEventMetadata eventMetadata; + auto longSize64 = m_tracingDataLongSize == 0 + ? sizeof(uintptr_t) == 8 + : m_tracingDataLongSize == 8; + if (eventMetadata.Parse(longSize64, systemName, formatFileContents)) + { + int8_t commonTypeOffset = -1; + uint8_t commonTypeSize = 0; + for (uint32_t i = 0; i != eventMetadata.CommonFieldCount(); i += 1) + { + auto const& field = eventMetadata.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(field.Offset()); + commonTypeSize = static_cast(field.Size()); + } + break; + } + } + + if (commonTypeOffset == -1) + { + // Unexpected: did not find a usable "common_type" field. + continue; + } + else if (m_commonTypeOffset == -1) + { + // First event to be parsed. Use its "common_type" field. + m_commonTypeOffset = commonTypeOffset; + m_commonTypeSize = commonTypeSize; + } + else if ( + m_commonTypeOffset != commonTypeOffset || + m_commonTypeSize != commonTypeSize) + { + // Unexpected: found a different "common_type" field. + continue; + } + + m_metadataById.try_emplace(eventMetadata.Id(), std::move(eventMetadata)); + } + } + } + + // Update EventDesc with the new metadata. + for (auto& desc : m_eventDescList) + { + if (desc.metadata == nullptr && desc.attr->type == PERF_TYPE_TRACEPOINT) + { + auto it = m_metadataById.find(static_cast(desc.attr->config)); + if (it != m_metadataById.end()) + { + desc.metadata = &it->second; + } + } + } + + // kallsyms + + p = ReadSection(byteReader, p, pEnd, &m_kallsyms); + if (!p) + { + return; // Unexpected. + } + + // printk + + p = ReadSection(byteReader, p, pEnd, &m_printk); + if (!p) + { + return; // Unexpected. + } + + // saved_cmdline + + if (tracingDataVersion >= 0.6) + { + p = ReadSection(byteReader, p, pEnd, &m_cmdline); + if (!p) + { + return; // Unexpected. + } + } + } + } + catch (std::bad_alloc const&) + { + return; + } +} + +void +PerfDataFile::ParseHeaderClockid() noexcept +{ + struct Clockid + { + uint64_t clockid; + }; + + auto const& data = m_headers[PERF_HEADER_CLOCKID]; + if (data.size() >= sizeof(Clockid)) + { + auto const pClockid = reinterpret_cast(data.data()); + m_sessionInfo.SetClockid(static_cast(m_byteReader.Read(&pClockid->clockid))); + } +} + +void +PerfDataFile::ParseHeaderClockData() noexcept +{ + struct ClockData + { + uint32_t version; + uint32_t clockid; + uint64_t wall_clock_ns; + uint64_t clockid_time_ns; + }; + + auto const& data = m_headers[PERF_HEADER_CLOCK_DATA]; + if (data.size() >= sizeof(ClockData)) + { + auto const pClockData = reinterpret_cast(data.data()); + if (1 <= m_byteReader.Read(&pClockData->version)) + { + m_sessionInfo.SetClockData( + m_byteReader.Read(&pClockData->clockid), + m_byteReader.Read(&pClockData->wall_clock_ns), + m_byteReader.Read(&pClockData->clockid_time_ns)); + } + } +} + +void +PerfDataFile::ParseHeaderEventDesc() noexcept +{ + if (m_parsedHeaderEventDesc) + { + return; + } + + try + { + auto const& eventDesc = m_headers[PERF_HEADER_EVENT_DESC]; + if (eventDesc.size() >= sizeof(uint32_t) + sizeof(uint32_t)) + { + m_parsedHeaderEventDesc = true; + + auto p = eventDesc.data(); + auto const pEnd = p + eventDesc.size(); + auto const cEvents = m_byteReader.ReadAsU32(p); + p += sizeof(uint32_t); + auto const cbAttrInHeader = m_byteReader.ReadAsU32(p); + p += sizeof(uint32_t); + if (cbAttrInHeader < PERF_ATTR_SIZE_VER0 || cbAttrInHeader > 0x10000) + { + return; // Unexpected. + } + + for (uint32_t iEvent = 0; iEvent != cEvents; iEvent += 1) + { + if (Remaining(p, pEnd) < cbAttrInHeader + sizeof(uint32_t) + sizeof(uint32_t)) + { + return; // Unexpected. + } + + auto const pAttrInHeader = reinterpret_cast(p); + p += cbAttrInHeader; + auto const cIds = m_byteReader.ReadAsU32(p); + p += sizeof(uint32_t); + auto const cbString = m_byteReader.ReadAsU32(p); + p += sizeof(uint32_t); + + if (m_byteReader.Read(&pAttrInHeader->size) != cbAttrInHeader || + cIds > 0x10000 || + cbString > 0x10000 || + Remaining(p, pEnd) < cbString + cIds * sizeof(uint64_t) || + strnlen(p, cbString) >= cbString) + { + return; // Unexpected. + } + + auto const pString = p; + p += cbString; + + auto const cbAttrToCopy = std::min(cbAttrInHeader, sizeof(perf_event_attr)); + + auto pAttr = std::make_unique(); + memcpy(pAttr.get(), pAttrInHeader, cbAttrToCopy); + + (void)AddAttr(std::move(pAttr), cbAttrToCopy, pString, p, cIds * sizeof(uint64_t)); + + p += cIds * sizeof(uint64_t); + assert(p <= pEnd); + } + } + } + catch (std::bad_alloc const&) + { + return; + } +} + +_Success_(return == 0) int +PerfDataFile::GetSampleEventId(_In_ perf_event_header const* pEventHeader, _Out_ uint64_t* pId) const noexcept +{ + int error; + uint64_t id; + + if (m_sampleIdOffset < 0) + { + id = 0; + error = ENODATA; + } + else if (pEventHeader->size < sizeof(perf_event_header) + sizeof(uint64_t) + m_sampleIdOffset * sizeof(uint64_t)) + { + id = 0; + error = EINVAL; + } + else + { + auto const pArray = reinterpret_cast(pEventHeader + 1); + id = m_byteReader.Read(&pArray[m_sampleIdOffset]); + error = 0; + } + + *pId = id; + return error; +} + +_Success_(return == 0) int +PerfDataFile::GetNonSampleEventId(_In_ perf_event_header const* pEventHeader, _Out_ uint64_t *pId) const noexcept +{ + int error; + uint64_t id; + + if (m_nonSampleIdOffset < 0) + { + id = 0; + error = ENODATA; + } + else if (pEventHeader->size < sizeof(perf_event_header) + sizeof(uint64_t) + m_nonSampleIdOffset * sizeof(uint64_t)) + { + id = 0; + error = EINVAL; + } + else + { + auto const pArrayLast = reinterpret_cast(pEventHeader) - 1 + + (pEventHeader->size / sizeof(uint64_t)); + id = m_byteReader.Read(&pArrayLast[-m_nonSampleIdOffset]); + error = 0; + } + + *pId = id; + return error; +} + +_Success_(return == 0) int +PerfDataFile::AddAttr( + std::unique_ptr pAttr, + uint32_t cbAttrCopied, + _In_z_ char const* pName, + _In_reads_bytes_(cbIdsFileEndian) void const* pbIdsFileEndian, + uintptr_t cbIdsFileEndian) noexcept(false) +{ + assert(cbAttrCopied <= sizeof(perf_event_attr)); + + if (m_byteReader.ByteSwapNeeded()) + { + pAttr->ByteSwap(); + } + + auto const sampleType = pAttr->sample_type; + + int8_t sampleIdOffset; + int8_t nonSampleIdOffset; + if (0 != (sampleType & PERF_SAMPLE_IDENTIFIER)) + { + // ID is at a fixed offset. + sampleIdOffset = 0; + nonSampleIdOffset = 0; + } + else if (0 == (sampleType & PERF_SAMPLE_ID)) + { + // ID is not available. + sampleIdOffset = -2; + nonSampleIdOffset = -2; + } + else + { + // ID offset not fixed. + sampleIdOffset = 0 + + (0 != (sampleType & PERF_SAMPLE_IP)) + + (0 != (sampleType & PERF_SAMPLE_TID)) + + (0 != (sampleType & PERF_SAMPLE_TIME)) + + (0 != (sampleType & PERF_SAMPLE_ADDR)); + nonSampleIdOffset = 0 + + (0 != (sampleType & PERF_SAMPLE_CPU)) + + (0 != (sampleType & PERF_SAMPLE_STREAM_ID)); + } + + if (!pAttr->sample_id_all) + { + // ID is not available for non-sample events. + nonSampleIdOffset = -2; + } + + if (sampleIdOffset != m_sampleIdOffset) + { + if (m_sampleIdOffset != -1) + { + // Unexpected: Inconsistent sampleIdOffset across the attrs in the trace. + return EINVAL; + } + + m_sampleIdOffset = sampleIdOffset; + } + + if (nonSampleIdOffset != m_nonSampleIdOffset) + { + if (m_nonSampleIdOffset != -1) + { + // Unexpected: Inconsistent nonSampleIdOffset across the attrs in the trace. + return EINVAL; + } + + m_nonSampleIdOffset = nonSampleIdOffset; + } + + auto const cIds = static_cast(cbIdsFileEndian / sizeof(uint64_t)); + auto const pIdsFileEndian = static_cast(pbIdsFileEndian); + auto pIds = std::make_unique(cIds); + for (uint32_t i = 0; i != cIds; i += 1) + { + pIds[i] = m_byteReader.Read(&pIdsFileEndian[i]); + } + + pAttr->size = static_cast(cbAttrCopied); + auto const eventDescIndex = m_eventDescList.size(); + auto const pEventListIds = pIds.get(); // To access ids after we move-from pIds. + + PerfEventMetadata const* pMetadata = nullptr; + if (pAttr->type == PERF_TYPE_TRACEPOINT) + { + auto itMetadata = m_metadataById.find(static_cast(pAttr->config)); + if (itMetadata != m_metadataById.end()) + { + pMetadata = &itMetadata->second; + } + } + + m_eventDescList.push_back({ + { pAttr.get(), pName, pMetadata, pIds.get(), cIds}, + std::move(pAttr), + std::move(pIds) + }); + + for (uint32_t i = 0; i != cIds; i += 1) + { + m_eventDescById[pEventListIds[i]] = eventDescIndex; + } + + return 0; +} + +bool +PerfDataFile::EnsureEventDataSize(uint32_t minSize) noexcept +{ + if (m_eventData.size() < minSize) + { + try + { + m_eventData.reserve(minSize); + m_eventData.resize(m_eventData.capacity()); + } + catch (std::bad_alloc const&) + { + return false; + } + } + + return true; +} + +_Success_(return == 0) int +PerfDataFile::UpdateFileLen() noexcept +{ + int error; + + if (FSEEK64(m_file, 0, SEEK_END)) + { + error = errno; + } + else if (auto const fileLen = FTELL64(m_file); + fileLen < 0) + { + error = errno; + } + else + { + m_filePos = fileLen; + m_fileLen = fileLen; + error = 0; + } + + return error; +} + +bool +PerfDataFile::SectionValid(perf_file_section const& section) const noexcept +{ + auto const endOffset = section.offset + section.size; + return endOffset >= section.offset && endOffset <= m_fileLen; +} + +_Success_(return == 0) int +PerfDataFile::FileRead(_Out_writes_bytes_all_(cb) void* p, uintptr_t cb) noexcept +{ + auto pLeft = static_cast(p); + auto cLeft = cb; + + for (;;) + { + auto const cRead = fread(pLeft, 1, cLeft, m_file); + m_filePos += cRead; + + if (cRead == cLeft) + { + return 0; + } + else if (ferror(m_file)) + { + return EIO; + } + else if (cRead == 0) + { + return EPIPE; + } + + pLeft += cRead; + cLeft -= cRead; + } +} + +_Success_(return == 0) int +PerfDataFile::FileSeek(uint64_t filePos) noexcept +{ + int error; + + if (filePos == m_filePos) + { + error = 0; + } + else if (FSEEK64(m_file, filePos, SEEK_SET)) + { + error = errno; + } + else + { + m_filePos = filePos; + error = 0; + } + + return error; +} + +_Success_(return == 0) int +PerfDataFile::FileSeekAndRead( + uint64_t filePos, + _Out_writes_bytes_all_(cb) void* p, + uintptr_t cb) noexcept +{ + int error = FileSeek(filePos); + return error ? error : FileRead(p, cb); +} diff --git a/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/src/PerfDataFileWriter.cpp b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/src/PerfDataFileWriter.cpp new file mode 100644 index 0000000000000..9473d76a3cb29 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/src/PerfDataFileWriter.cpp @@ -0,0 +1,1013 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include +#include +#include +#include + +#include +#include +#include +#include +#include // _O_BINARY + +#ifdef _WIN32 +#include +static bool constexpr HostIsBigEndian = false; +#define CLOSE(file) _close(file) +#define LSEEK64(file, offset, origin) _lseeki64(file, offset, origin) +#define WRITE(file, data, size) _write(file, data, size) +#define WRITE_SIZE(size) static_cast(std::min(0x80000000, size)) +#define GETPAGESIZE() 4096 // PAGE_SIZE +#define O_CLOEXEC 0 +#else // _WIN32 +#include +#include +#include +#include +static bool constexpr HostIsBigEndian = __BYTE_ORDER == __BIG_ENDIAN; +#define CLOSE(file) close(file) +#define LSEEK64(file, offset, origin) lseek64(file, offset, origin) +#define WRITE(file, data, size) write(file, data, size) +#define WRITE_SIZE(size) (size) +#define GETPAGESIZE() sysconf(_SC_PAGESIZE) +#endif // _WIN32 + +#ifndef _Inout_ +#define _Inout_ +#endif + +using namespace tracepoint_decode; + +static constexpr auto InvalidFilePos = static_cast(-1); +static constexpr char Zero64[8] = {}; + +_Success_(return == 0) static int +open_s(_Out_ int* pFile, _In_z_ char const* path, int oflags, int pmode) +{ +#ifdef _WIN32 + return _sopen_s(pFile, path, oflags | _O_BINARY, _SH_DENYRW, pmode & (_S_IREAD | _S_IWRITE)); +#else + auto const file = open(path, oflags, pmode); + *pFile = file; + return file < 0 ? errno : 0; +#endif +} + +template +static void +AppendValue(_Inout_ std::vector* pHeader, T const& value) +{ + auto const pbValue = reinterpret_cast(&value); + pHeader->insert(pHeader->end(), pbValue, pbValue + sizeof(T)); +} + +static void +AppendStringZ(_Inout_ std::vector* pHeader, std::string_view value) +{ + pHeader->insert(pHeader->end(), value.data(), value.data() + value.size()); + pHeader->push_back(0); +} + +static void +AppendSection32( + _Inout_ std::vector* pHeader, + _In_reads_bytes_(cb) char const* pb, + uint32_t cb) +{ + AppendValue(pHeader, cb); + pHeader->insert(pHeader->end(), pb, pb + cb); +} + +static void +AppendSection64( + _Inout_ std::vector* pHeader, + _In_reads_bytes_(cb) char const* pb, + size_t cb) +{ + AppendValue(pHeader, cb); + pHeader->insert(pHeader->end(), pb, pb + cb); +} + +static void +AppendNamedSection64( + _Inout_ std::vector* pHeader, + std::string_view name, + std::string_view defaultValue, + std::vector const& setValue) +{ + auto value = setValue.data() == nullptr + ? defaultValue + : std::string_view(setValue.data(), setValue.size()); + AppendStringZ(pHeader, name); + AppendSection64(pHeader, value.data(), value.size()); +} + +// From: perf.data-file-format.txt +struct PerfDataFileWriter::perf_file_section { + uint64_t offset; // offset from start of file + uint64_t size; // size of the section +}; + +struct PerfDataFileWriter::perf_file_header +{ + static constexpr uint64_t Magic2 = 0x32454C4946524550; // "PERFILE2" + + uint64_t magic; // If correctly byte-swapped, this will be equal to Magic2. + uint64_t size; // size of the header + uint64_t attr_size; // size of an attribute in attrs + perf_file_section attrs; + perf_file_section data; + perf_file_section event_types; // Not used - superceded by PERF_HEADER_TRACING_DATA. + + // 256-bit bitmap based on HEADER_BITS + uint64_t flags[4]; +}; + +struct PerfDataFileWriter::EventDesc +{ + static uint32_t const NameMaxSize = 65535u; + static uint32_t const SampleIdsMaxSize = 0xFFFFFFFF; + + std::vector name; // does not include nul-termination, max size is NameMaxSize. + std::vector sampleIds; // max size is SampleIdsMaxSize. + perf_event_attr attr; + + EventDesc(PerfEventDesc const& desc, uint32_t nameLen) + : name(desc.name, desc.name + nameLen) + , sampleIds(desc.ids, desc.ids + desc.ids_count) + { + if (sizeof(attr) <= desc.attr->size) + { + memcpy(&attr, desc.attr, sizeof(attr)); + attr.size = static_cast(sizeof(attr)); + } + else + { + memset(&attr, 0, sizeof(attr)); + memcpy(&attr, desc.attr, desc.attr->size); + } + } +}; + +struct PerfDataFileWriter::TracepointInfo +{ + PerfEventMetadata const& metadata; + size_t eventDescIndex; + + TracepointInfo(PerfEventMetadata const& _metadata, size_t _eventDescIndex) + : metadata(_metadata) + , eventDescIndex(_eventDescIndex) + { + return; + } +}; + +PerfDataFileWriter::~PerfDataFileWriter() +{ + CloseNoFinalize(); +} + +PerfDataFileWriter::PerfDataFileWriter() noexcept(false) + : m_filePos(InvalidFilePos) + , m_file(-1) + , m_eventDescs() + , m_tracepointInfoByCommonType() + , m_headers() + , m_tracingDataPageSize(0) + , m_tracingDataLongSize(sizeof(size_t)) +{ + return; +} + +void +PerfDataFileWriter::CloseNoFinalize() noexcept +{ + assert(ValidFilePos()); + if (m_file >= 0) + { + CLOSE(m_file); + } + + m_file = -1; + m_filePos = InvalidFilePos; +} + +_Success_(return == 0) int +PerfDataFileWriter::FinalizeAndClose() noexcept +{ + int error = 0; + + try + { + if (m_headers[PERF_HEADER_TRACING_DATA].empty()) + { + SynthesizeTracingData(); + } + + if (m_headers[PERF_HEADER_EVENT_DESC].empty()) + { + SynthesizeEventDesc(); + } + + perf_file_header fileHeader = {}; + fileHeader.magic = perf_file_header::Magic2; + fileHeader.size = sizeof(perf_file_header); + fileHeader.attr_size = sizeof(perf_event_attr) + sizeof(perf_file_section); + + // Current implementation starts the data section immediately after the file header. + // It ends wherever we are now. + fileHeader.data.offset = sizeof(perf_file_header); + fileHeader.data.size = m_filePos - sizeof(perf_file_header); + + // The sections for the perf headers must go immediately after the data section. + // Current implementation puts the data for the perf headers right after that. + error = WriteHeaders(&fileHeader.flags[0]); + if (error != 0) + { + goto Done; + } + + // The attr section contains a sequence of attr+idSection blocks. + // Current implementation puts the id data right after that. + error = WriteAttrs(&fileHeader.attrs); + if (error != 0) + { + goto Done; + } + + // Finalize the file header. + + assert(ValidFilePos()); + + auto const seekResult = LSEEK64(m_file, 0, SEEK_SET); + m_filePos = 0; + if (seekResult != 0) + { + error = seekResult < 0 ? errno : EDOM; + goto Done; + } + + error = WriteData(&fileHeader, sizeof(fileHeader)); + } + catch (...) + { + error = ENOMEM; + } + +Done: + + CloseNoFinalize(); + return error; +} + +_Success_(return == 0) int +PerfDataFileWriter::Create(_In_z_ char const* filePath, int mode) noexcept +{ + int error; + + CloseNoFinalize(); + m_eventDescs.clear(); + m_tracepointInfoByCommonType.clear(); + for (auto& header : m_headers) + { + header.clear(); + } + + error = open_s(&m_file, filePath, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, mode); + if (m_file < 0) + { + error = errno; + } + else + { + m_filePos = 0; + static constexpr perf_file_header zeroHeader = {}; + error = WriteData(&zeroHeader, sizeof(zeroHeader)); + if (error != 0) + { + CloseNoFinalize(); + } + } + + return error; +} + +uint64_t +PerfDataFileWriter::FilePos() const noexcept +{ + assert(ValidFilePos()); + return m_filePos; +} + +_Success_(return == 0) int +PerfDataFileWriter::WriteEventData( + _In_reads_bytes_(dataSize) void const* data, + size_t dataSize) noexcept +{ + int error = WriteData(data, dataSize); + return error; +} + +#ifndef _WIN32 + +_Success_(return >= 0) ptrdiff_t +PerfDataFileWriter::WriteEventDataIovecs( + _In_reads_(iovecsCount) struct iovec const* iovecs, + int iovecsCount) noexcept +{ + auto const writeResult = writev(m_file, iovecs, iovecsCount); + if (writeResult >= 0) + { + m_filePos += writeResult; + } + + return writeResult; +} + +#endif // !_WIN32 + +_Success_(return == 0) int +PerfDataFileWriter::WriteFinishedInit() noexcept +{ + static perf_event_header const finishedInit = { + PERF_RECORD_FINISHED_INIT, 0, sizeof(perf_event_header) }; + return WriteEventData(&finishedInit, sizeof(finishedInit)); +} + +_Success_(return == 0) int +PerfDataFileWriter::WriteFinishedRound() noexcept +{ + static perf_event_header const finishedRound = { + PERF_RECORD_FINISHED_ROUND, 0, sizeof(perf_event_header) }; + return WriteEventData(&finishedRound, sizeof(finishedRound)); +} + +size_t +PerfDataFileWriter::GetHeaderSize(PerfHeaderIndex index) const noexcept +{ + return index >= PERF_HEADER_LAST_FEATURE + ? 0u + : m_headers[index].size(); +} + +void const* +PerfDataFileWriter::GetHeaderData(PerfHeaderIndex index) const noexcept +{ + return index >= PERF_HEADER_LAST_FEATURE + ? 0 + : m_headers[index].data(); +} + +_Success_(return == 0) int +PerfDataFileWriter::SetHeader( + PerfHeaderIndex index, + _In_reads_bytes_(dataSize) void const* data, + size_t dataSize) noexcept +{ + int error; + + try + { + if (index >= PERF_HEADER_LAST_FEATURE) + { + error = EINVAL; + } + else + { + auto const pData = static_cast(data); + m_headers[index].assign(&pData[0], &pData[dataSize]); + error = 0; + } + } + catch (...) + { + error = ENOMEM; + } + + return error; +} + +_Success_(return == 0) int +PerfDataFileWriter::SetStringHeader( + PerfHeaderIndex index, + _In_z_ char const* str) noexcept +{ + int error; + + try + { + if (index >= PERF_HEADER_LAST_FEATURE) + { + error = EINVAL; + } + else + { + auto const strLen = strlen(str); + if (strLen >= 0xFFFFFFFF) + { + error = E2BIG; + } + else + { + auto const cbStr = strLen + 1; + auto const headerLen = (sizeof(uint32_t) + cbStr + 7) & ~size_t(7); + + /* + struct perf_header_string { + uint32_t len; + char string[len]; // zero terminated + }; + */ + auto& header = m_headers[index]; + header.clear(); + header.reserve(headerLen); + AppendValue(&header, static_cast(cbStr)); // uint32_t len; + header.insert(header.end(), &str[0], &str[cbStr]); // char string[len]; + header.resize(headerLen); // NUL-terminate, pad to a multiple of 8 bytes. + error = 0; + } + } + } + catch (...) + { + error = ENOMEM; + } + + return error; +} + +_Success_(return == 0) int +PerfDataFileWriter::SetNrCpusHeader(uint32_t available, uint32_t online) noexcept +{ + struct { + uint32_t available; + uint32_t online; + } const nrcpus = { + available, + online, + }; + return SetHeader(PERF_HEADER_NRCPUS, &nrcpus, sizeof(nrcpus)); +} + +_Success_(return == 0) int +PerfDataFileWriter::SetSampleTimeHeader(uint64_t first, uint64_t last) noexcept +{ + struct { + uint64_t first; + uint64_t last; + } const times = { + first, + last, + }; + return SetHeader(PERF_HEADER_SAMPLE_TIME, ×, sizeof(times)); +} + +_Success_(return == 0) int +PerfDataFileWriter::SetClockidHeader(uint32_t clockid) noexcept +{ + uint64_t const clockid64 = clockid; + return SetHeader(PERF_HEADER_CLOCKID, &clockid64, sizeof(clockid64)); +} + +_Success_(return == 0) int +PerfDataFileWriter::SetClockDataHeader(uint32_t clockid, uint64_t wallClockNS, uint64_t clockidTimeNS) noexcept +{ + struct { + uint32_t version; + uint32_t clockid; + uint64_t wallClockNS; + uint64_t clockidTimeNS; + } const clockData = { + 1, + clockid, + wallClockNS, + clockidTimeNS, + }; + return SetHeader(PERF_HEADER_CLOCK_DATA, &clockData, sizeof(clockData)); +} + +_Success_(return == 0) int +PerfDataFileWriter::SetSessionInfoHeaders(PerfEventSessionInfo const& sessionInfo) noexcept +{ + int error = 0; + + auto const clockid = sessionInfo.Clockid(); + if (clockid == 0xFFFFFFFF) + { + m_headers[PERF_HEADER_CLOCKID].clear(); + } + else + { + error = SetClockidHeader(clockid); + if (error != 0) + { + goto Done; + } + } + + if (!sessionInfo.ClockOffsetKnown()) + { + m_headers[PERF_HEADER_CLOCK_DATA].clear(); + } + else + { + uint64_t wallClockNS, clockidTimeNS; + sessionInfo.GetClockData(&wallClockNS, &clockidTimeNS); + error = SetClockDataHeader(clockid, wallClockNS, clockidTimeNS); + if (error != 0) + { + goto Done; + } + } + +Done: + + return error; +} + +#ifndef _WIN32 + +_Success_(return == 0) int +PerfDataFileWriter::SetUtsNameHeaders(utsname const& uts) noexcept +{ + int error; + + error = SetStringHeader(PERF_HEADER_HOSTNAME, uts.nodename); + if (error != 0) + { + goto Done; + } + + error = SetStringHeader(PERF_HEADER_OSRELEASE, uts.release); + if (error != 0) + { + goto Done; + } + + error = SetStringHeader(PERF_HEADER_ARCH, uts.machine); + if (error != 0) + { + goto Done; + } + +Done: + + return error; +} + +#endif // !_WIN32 + +_Success_(return == 0) int +PerfDataFileWriter::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 +{ + int error; + + if (kallsyms.size() > 0x80000000 || + printk.size() > 0x80000000) + { + error = E2BIG; + } + else try + { + if (longSize != 0) + { + m_tracingDataLongSize = longSize; + } + + if (pageSize != 0) + { + m_tracingDataPageSize = pageSize; + } + + if (headerPage.data()) + { + m_tracingDataHeaderPage.assign(headerPage.data(), headerPage.data() + headerPage.size()); + } + + if (headerEvent.data()) + { + m_tracingDataHeaderEvent.assign(headerEvent.data(), headerEvent.data() + headerEvent.size()); + } + + if (ftraces) + { + m_tracingDataFtraces.clear(); + m_tracingDataFtraces.reserve(ftraceCount); + for (uint32_t i = 0; i != ftraceCount; i += 1) + { + auto ftrace = ftraces[i]; + m_tracingDataFtraces.emplace_back(ftrace.data(), ftrace.data() + ftrace.size()); + } + } + + if (kallsyms.data()) + { + m_tracingDataKallsyms.assign(kallsyms.data(), kallsyms.data() + kallsyms.size()); + } + + if (printk.data()) + { + m_tracingDataPrintk.assign(printk.data(), printk.data() + printk.size()); + } + + if (savedCmdLine.data()) + { + m_tracingDataSavedCmdline.assign(savedCmdLine.data(), savedCmdLine.data() + savedCmdLine.size()); + } + + error = 0; + } + catch (...) + { + error = ENOMEM; + } + + return error; +} + +_Success_(return == 0) int +PerfDataFileWriter::AddEventDesc(PerfEventDesc const& desc) noexcept +{ + int error; + + assert(desc.attr != nullptr); // Precondition + assert(desc.name != nullptr); // Precondition + if (m_eventDescs.size() == 0xFFFFFFFF) + { + error = E2BIG; + } + else try + { + auto const nameLen = strlen(desc.name); + if (nameLen > EventDesc::NameMaxSize) + { + error = E2BIG; + } + else + { + m_eventDescs.emplace_back(desc, static_cast(nameLen)); + error = 0; + } + } + catch (...) + { + error = ENOMEM; + } + + return error; +} + +bool +PerfDataFileWriter::HasTracepointEventDesc(uint32_t common_type) const noexcept +{ + return 0 != m_tracepointInfoByCommonType.count(common_type); +} + +_Success_(return == 0) int +PerfDataFileWriter::AddTracepointEventDesc(PerfEventDesc const& desc) noexcept +{ + int error; + + assert(desc.attr != nullptr); // Precondition + assert(desc.name != nullptr); // Precondition + assert(desc.metadata != nullptr); // Precondition + try + { + auto [it, inserted] = m_tracepointInfoByCommonType.try_emplace( + desc.metadata->Id(), + *desc.metadata, + m_eventDescs.size()); + if (!inserted) + { + error = EEXIST; + } + else + { + // Added new metadata so add new EventDesc too. + error = AddEventDesc(desc); + if (error != 0) + { + m_tracepointInfoByCommonType.erase(it); + } + } + } + catch (...) + { + error = ENOMEM; + } + + return error; +} + +bool +PerfDataFileWriter::ValidFilePos() const noexcept +{ + if (m_file < 0) + { + // File is closed, m_filePos should be -1. + return m_filePos == InvalidFilePos; + } + + auto const seekResult = LSEEK64(m_file, 0, SEEK_CUR); + if (seekResult < 0) + { + // File is in error condition, m_filePos is unknown. + return true; + } + + return m_filePos == static_cast(seekResult); +} + +_Success_(return == 0) int +PerfDataFileWriter::WriteData( + _In_reads_bytes_(dataSize) void const* data, + size_t dataSize) noexcept +{ + int error = 0; + + for (size_t i = 0; i < dataSize;) + { + auto const writeSize = WRITE_SIZE(dataSize - i); + auto const writeResult = WRITE( + m_file, + static_cast(data) + i, + writeSize); + if (writeResult < 0) + { + error = errno; + break; + } + + m_filePos += writeResult; + i += writeResult; + } + + return error; +} + +void +PerfDataFileWriter::SynthesizeTracingData() +{ + using namespace std::string_view_literals; + + static constexpr auto DefaultHeaderPage = + "\tfield: u64 timestamp;\toffset:0;\tsize:8;\tsigned:0;\n" + "\tfield: local_t commit;\toffset:8;\tsize:8;\tsigned:1;\n" + "\tfield: int overwrite;\toffset:8;\tsize:1;\tsigned:1;\n" + "\tfield: char data;\toffset:16;\tsize:4080;\tsigned:0;\n" + ""sv; + static constexpr auto DefaultHeaderEvent = + "# compressed entry header\n" + "\ttype_len : 5 bits\n" + "\ttime_delta : 27 bits\n" + "\tarray : 32 bits\n" + "\n" + "\tpadding : type == 29\n" + "\ttime_extend : type == 30\n" + "\ttime_stamp : type == 31\n" + "\tdata max type_len == 28\n" + ""sv; + + auto& header = m_headers[PERF_HEADER_TRACING_DATA]; + header.clear(); + + if (m_tracingDataPageSize == 0) + { + m_tracingDataPageSize = GETPAGESIZE(); + } + + AppendStringZ(&header, "\x17\x08\x44tracing0.6"sv); + AppendValue(&header, HostIsBigEndian); + AppendValue(&header, m_tracingDataLongSize); + AppendValue(&header, m_tracingDataPageSize); + AppendNamedSection64(&header, "header_page"sv, DefaultHeaderPage, m_tracingDataHeaderPage); + AppendNamedSection64(&header, "header_event"sv, DefaultHeaderEvent, m_tracingDataHeaderEvent); + + // ftraces + assert(m_tracingDataFtraces.size() <= 0xFFFFFFFF); + AppendValue(&header, static_cast(m_tracingDataFtraces.size())); + for (auto const& ftrace : m_tracingDataFtraces) + { + AppendSection64(&header, ftrace.data(), ftrace.size()); + } + + // systems (and events) + + // Group events by system. + using InfoIt = decltype(m_tracepointInfoByCommonType.cbegin()); + std::map> commonTypesBySystem; + for (auto it = m_tracepointInfoByCommonType.cbegin(); it != m_tracepointInfoByCommonType.cend(); ++it) + { + auto const& meta = it->second.metadata; + commonTypesBySystem[meta.SystemName()].push_back(it); + } + + // SystemCount + AppendValue( + &header, + static_cast(commonTypesBySystem.size())); + + // Systems + for (auto& ctbs : commonTypesBySystem) + { + // SystemName + AppendStringZ(&header, ctbs.first); + + // EventCount + AppendValue(&header, static_cast(ctbs.second.size())); + + // Events + for (auto it : ctbs.second) + { + auto const& meta = it->second.metadata; + auto const format = meta.FormatFileContents(); + AppendSection64(&header, format.data(), format.size()); + } + } + + // Other stuff. + AppendSection32( + &header, + m_tracingDataKallsyms.data(), + static_cast(m_tracingDataKallsyms.size())); + AppendSection32( + &header, + m_tracingDataPrintk.data(), + static_cast(m_tracingDataPrintk.size())); + AppendSection64( + &header, + m_tracingDataSavedCmdline.data(), + m_tracingDataSavedCmdline.size()); +} + +void +PerfDataFileWriter::SynthesizeEventDesc() +{ + auto& header = m_headers[PERF_HEADER_EVENT_DESC]; + header.clear(); + + /* + From perf.data-file-format.txt: + struct { + uint32_t nr; // number of events + uint32_t attr_size; //size of each perf_event_attr + struct { + struct perf_event_attr attr; // size of attr_size + uint32_t nr_ids; + struct perf_header_string event_string; + uint64_t ids[nr_ids]; + } events[nr]; // Variable length records + }; + */ + + assert(m_eventDescs.size() <= 0xFFFFFFFF); + auto const nr = static_cast(m_eventDescs.size()); + + AppendValue(&header, nr); // nr + AppendValue(&header, sizeof(perf_event_attr)); // attr_size + for (auto& desc : m_eventDescs) // events + { + auto const name = desc.name.data(); + + static_assert(EventDesc::NameMaxSize < static_cast(EventDesc::NameMaxSize + 8u), + "Bad NameMaxSize (nameSize + namePad can overflow)"); + assert(desc.name.size() <= EventDesc::NameMaxSize); + auto const nameSize = static_cast(desc.name.size()); + auto const namePad = 8u - (nameSize & 7); // 1 to 8 bytes of '\0'. + + assert(desc.sampleIds.size() <= EventDesc::SampleIdsMaxSize); + auto const nr_ids = static_cast(desc.sampleIds.size()); + + AppendValue(&header, desc.attr); // attr + AppendValue(&header, nr_ids); // nr_ids + AppendValue(&header, nameSize + namePad); // event_string.len + header.insert(header.end(), &name[0], &name[nameSize]); // event_string.string + header.insert(header.end(), &Zero64[0], &Zero64[namePad]);// NUL + pad to x8 + header.insert( // ids + header.end(), + reinterpret_cast(desc.sampleIds.data()), + reinterpret_cast(desc.sampleIds.data() + desc.sampleIds.size())); + } +} + +_Success_(return == 0) int +PerfDataFileWriter::WriteHeaders(_Out_ uint64_t* pFlags0) noexcept +{ + int error = 0; + uint64_t flags0 = 0; + + // Update the flags and compute where the first perf header will go. + auto firstPerfHeaderOffset = m_filePos; + for (uint32_t i = 0; i < PERF_HEADER_LAST_FEATURE; i += 1) + { + auto const& header = m_headers[i]; + if (!header.empty()) + { + flags0 |= static_cast(1) << i; + firstPerfHeaderOffset += sizeof(perf_file_section); + } + } + + // Store perf_file_section for each perf header. + auto perfHeaderOffset = firstPerfHeaderOffset; + for (uint32_t i = 0; i < PERF_HEADER_LAST_FEATURE; i += 1) + { + auto& header = m_headers[i]; + if (!header.empty()) + { + auto headerSize = header.size(); + + perf_file_section headerSection = {}; + headerSection.offset = perfHeaderOffset; + headerSection.size = headerSize; + perfHeaderOffset += headerSize; + + error = WriteData(&headerSection, sizeof(headerSection)); + if (error != 0) + { + goto Done; + } + } + } + + // Store data for each perf header that is present. + for (uint32_t i = 0; i < PERF_HEADER_LAST_FEATURE; i += 1) + { + auto const& header = m_headers[i]; + if (!header.empty()) + { + error = WriteData(header.data(), header.size()); + if (error != 0) + { + goto Done; + } + } + } + + assert(m_filePos == perfHeaderOffset); + +Done: + + *pFlags0 = flags0; + return error; +} + +_Success_(return == 0) int +PerfDataFileWriter::WriteAttrs(_Out_ perf_file_section* pAttrsSection) noexcept +{ + int error = 0; + + uint64_t const PerfAttrEntrySize = sizeof(perf_event_attr) + sizeof(perf_file_section); + auto const perfAttrSectionSize = PerfAttrEntrySize * m_eventDescs.size(); + pAttrsSection->offset = m_filePos; + pAttrsSection->size = perfAttrSectionSize; + + auto const firstAttrIdsOffset = m_filePos + perfAttrSectionSize; + auto attrIdsOffset = firstAttrIdsOffset; + + for (auto const& desc : m_eventDescs) + { + error = WriteData(&desc.attr, sizeof(perf_event_attr)); + if (error != 0) + { + goto Done; + } + + perf_file_section idsSection = {}; + idsSection.offset = attrIdsOffset; + idsSection.size = desc.sampleIds.size() * sizeof(desc.sampleIds[0]); + attrIdsOffset += idsSection.size; + + error = WriteData(&idsSection, sizeof(idsSection)); + if (error != 0) + { + goto Done; + } + } + + for (auto const& desc : m_eventDescs) + { + error = WriteData( + desc.sampleIds.data(), + desc.sampleIds.size() * sizeof(desc.sampleIds[0])); + if (error != 0) + { + goto Done; + } + } + +Done: + + return error; +} diff --git a/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/src/PerfEventAbi.cpp b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/src/PerfEventAbi.cpp new file mode 100644 index 0000000000000..ddfd934cf4d75 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/src/PerfEventAbi.cpp @@ -0,0 +1,184 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include +#include +#include +#include + +#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 +#endif // _WIN32 + +using namespace tracepoint_decode; + +template +static constexpr unsigned +ArrayCount(T(&)[N]) noexcept +{ + return N; +} + +template +static void +Bswap(T& value) noexcept +{ + if constexpr (sizeof(value) == sizeof(uint16_t)) + { + value = static_cast(bswap_16(value)); + } + else if constexpr (sizeof(value) == sizeof(uint32_t)) + { + value = static_cast(bswap_32(value)); + } + else if constexpr (sizeof(value) == sizeof(uint64_t)) + { + value = static_cast(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(this) + offsetof_bitfield; + pb != reinterpret_cast(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); +} diff --git a/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/src/PerfEventInfo.cpp b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/src/PerfEventInfo.cpp new file mode 100644 index 0000000000000..6e27dad36cac1 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/src/PerfEventInfo.cpp @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include +#include +#include +#include + +using namespace tracepoint_decode; + +uint64_t +PerfSampleEventInfo::SampleType() const noexcept +{ + assert(event_desc->attr); // Requires: GetSampleEventInfo() succeeded. + return event_desc->attr->sample_type; +} + +perf_event_attr const& +PerfSampleEventInfo::Attr() const noexcept +{ + assert(event_desc->attr); // Requires: GetSampleEventInfo() succeeded. + return *event_desc->attr; +} + +_Ret_z_ char const* +PerfSampleEventInfo::Name() const noexcept +{ + assert(event_desc->attr); // Requires: GetSampleEventInfo() succeeded. + return event_desc->name; +} + +_Ret_opt_ PerfEventMetadata const* +PerfSampleEventInfo::Metadata() const noexcept +{ + assert(event_desc->attr); // Requires: GetSampleEventInfo() succeeded. + return event_desc->metadata; +} + +uint64_t +PerfNonSampleEventInfo::SampleType() const noexcept +{ + assert(event_desc->attr); // Requires: GetNonSampleEventInfo() succeeded. + return event_desc->attr->sample_type; +} + +perf_event_attr const& +PerfNonSampleEventInfo::Attr() const noexcept +{ + assert(event_desc->attr); // Requires: GetNonSampleEventInfo() succeeded. + return *event_desc->attr; +} + +_Ret_z_ char const* +PerfNonSampleEventInfo::Name() const noexcept +{ + assert(event_desc->attr); // Requires: GetNonSampleEventInfo() succeeded. + return event_desc->name; +} diff --git a/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/src/PerfEventMetadata.cpp b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/src/PerfEventMetadata.cpp new file mode 100644 index 0000000000000..d0f907608551e --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/src/PerfEventMetadata.cpp @@ -0,0 +1,892 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include +#include +#include +#include +#include +#include + +using namespace std::string_view_literals; +using namespace tracepoint_decode; + +#ifndef _In_ +#define _In_ +#endif +#ifndef _In_z_ +#define _In_z_ +#endif +#ifndef _Inout_ +#define _Inout_ +#endif +#ifndef _Out_ +#define _Out_ +#endif + +//#define DEBUG_PRINTF(...) ((void)0) +#ifndef DEBUG_PRINTF +#define DEBUG_PRINTF(...) fprintf(stderr, __VA_ARGS__) +#include +#endif // DEBUG_PRINTF + +static bool +IsSpaceOrTab(char ch) noexcept +{ + return ch == ' ' || ch == '\t'; +} + +static bool +IsEolChar(char ch) noexcept +{ + return ch == '\r' || ch == '\n'; +} + +static constexpr bool +IsIdentStart(char ch) noexcept +{ + uint8_t const chLower = ch | 0x20; + return ('a' <= chLower && chLower <= 'z') || + ch == '_'; +} + +static constexpr bool +IsIdentContinue(char ch) noexcept +{ + uint8_t const chLower = ch | 0x20; + return ('a' <= chLower && chLower <= 'z') || + ('0' <= ch && ch <= '9') || + ch == '_'; +} + +static bool +ParseOptionalU16(_In_z_ char const* value, _Out_ uint16_t* pResult) noexcept +{ + errno = 0; + auto const lresult = strtoul(value, nullptr, 0); + if (0 != errno || lresult > 0xFFFF) + { + *pResult = 0; + return false; + } + else + { + *pResult = static_cast(lresult); + return true; + } +} + +// Given p pointing after the opening quote, returns position after the closing quote. +static char const* +ConsumeString(char const* p, char const* pEnd, char quote) noexcept +{ + while (p != pEnd) + { + char consumed = *p; + p += 1; + + if (consumed == quote) + { + break; + } + else if (consumed == '\\') + { + if (p == pEnd) + { + DEBUG_PRINTF("EOF within '\\' escape\n"); + break; // Unexpected. + } + + // Ignore whatever comes after the backslash, which + // is significant if it is quote or '\\'. + p += 1; + } + } + + return p; +} + +// Given p pointing after the opening brance, returns position after the closing brace. +static char const* +ConsumeBraced(char const* p, char const* pEnd, char open, char close) noexcept +{ + unsigned nesting = 1; + while (p != pEnd) + { + char consumed = *p; + p += 1; + if (consumed == close) + { + nesting -= 1; + if (nesting == 0) + { + break; + } + } + else if (consumed == open) + { + nesting += 1; + } + } + + return p; +} + +enum TokenType : uint8_t +{ + TokenNone, + TokenIdent, // e.g. MyFile + TokenBrackets, // e.g. [...] + TokenParentheses, // e.g. (...) + TokenString, // e.g. "asdf" + TokenPunctuation, // e.g. * +}; + +class Tokenizer +{ + char const* const pEnd; + char const* pCurrent; + +public: + + std::string_view value; + TokenType type; + + explicit + Tokenizer(std::string_view str) noexcept + : pEnd(str.data() + str.size()) + , pCurrent(str.data()) + , value() + , type() {} + + void + MoveNext() noexcept + { + TokenType newType; + auto p = pCurrent; + + while (p != pEnd && *p <= ' ') + { + p += 1; + } + + auto const pTokenStart = p; + + if (p == pEnd) + { + newType = TokenNone; + } + else if (IsIdentStart(*p)) + { + // Return identifier. + p += 1; + while (p != pEnd && IsIdentContinue(*p)) + { + p += 1; + } + + newType = TokenIdent; + } + else + { + switch (*p) + { + case '\'': + case '\"': + // Return up to the closing quote. + p = ConsumeString(p + 1, pEnd, *p); + newType = TokenString; + break; + case '(': + // Return up to closing paren (allow nesting). + p = ConsumeBraced(p + 1, pEnd, '(', ')'); + newType = TokenParentheses; + break; + case '[': + // Return up to closing brace (allow nesting). + p = ConsumeBraced(p + 1, pEnd, '[', ']'); + newType = TokenBrackets; + break; + default: // Return single character token. + p += 1; + newType = TokenPunctuation; + break; + } + } + + pCurrent = p; + value = { pTokenStart, static_cast(p - pTokenStart) }; + type = newType; + } +}; + +PerfFieldMetadata +PerfFieldMetadata::Parse( + bool longSize64, + std::string_view formatLine) noexcept +{ + PerfFieldMetadata metadata; + + std::string_view field; + uint16_t offset = 0; + bool foundOffset = false; + uint16_t size = 0; + bool foundSize = false; + int8_t isSigned = -1; + + auto p = formatLine.data(); + auto const pEnd = p + formatLine.size(); + + // FIND: field, offset, size + + // Search for " NAME: VALUE;" + while (p != pEnd) + { + // Skip spaces and semicolons. + while (IsSpaceOrTab(*p) || *p == ';') + { + p += 1; + if (p == pEnd) goto Done; + } + + // "NAME:" + auto const pPropName = p; + while (*p != ':') + { + p += 1; + if (p == pEnd) + { + DEBUG_PRINTF("EOL before ':' in format\n"); + goto Done; // Unexpected. + } + } + + auto const propName = std::string_view(pPropName, p - pPropName); + p += 1; // Skip ':' + + // Skip spaces. + while (p != pEnd && IsSpaceOrTab(*p)) + { + DEBUG_PRINTF("Space before propval in format\n"); + p += 1; // Unexpected. + } + + // "VALUE;" + auto const pPropValue = p; + while (p != pEnd && *p != ';') + { + p += 1; + } + + if (propName == "field"sv || propName == "field special"sv) + { + field = std::string_view(pPropValue, p - pPropValue); + } + else if (propName == "offset"sv && p != pEnd) + { + foundOffset = ParseOptionalU16(pPropValue, &offset); + } + else if (propName == "size"sv && p != pEnd) + { + foundSize = ParseOptionalU16(pPropValue, &size); + } + else if (propName == "signed"sv && p != pEnd) + { + uint16_t signedVal; + isSigned = !ParseOptionalU16(pPropValue, &signedVal) + ? -1 // Not a valid U16, isSigned = NULL. + : signedVal != 0; + } + } + +Done: + + if (!field.empty() && foundOffset && foundSize) + { + metadata = PerfFieldMetadata(longSize64, field, offset, size, isSigned); + } + + return metadata; +} + +PerfFieldMetadata::PerfFieldMetadata( + bool longSize64, + std::string_view field, + uint16_t offset, + uint16_t size, + int8_t isSigned) noexcept + : m_name() + , m_field(field) + , m_offset(offset) + , m_size(size) + , m_fixedArrayCount() + , m_elementSize() + , m_format() + , m_array() +{ + bool foundLongLong = false; + bool foundLong = false; + bool foundShort = false; + bool foundUnsigned = false; + bool foundSigned = false; + bool foundStruct = false; + bool foundDataLoc = false; + bool foundRelLoc = false; + bool foundArray = false; + bool foundPointer = false; + std::string_view baseType; + + // DEDUCE: m_name, m_fixedArrayCount + + Tokenizer tokenizer(m_field); + for (;;) + { + tokenizer.MoveNext(); + switch (tokenizer.type) + { + case TokenNone: + goto TokensDone; + + case TokenIdent: + if (tokenizer.value == "long"sv) + { + if (foundLong) + { + foundLongLong = true; + } + else + { + foundLong = true; + } + } + else if (tokenizer.value == "short"sv) + { + foundShort = true; + } + else if (tokenizer.value == "unsigned"sv) + { + foundUnsigned = true; + } + else if (tokenizer.value == "signed"sv) + { + foundSigned = true; + } + else if (tokenizer.value == "struct"sv) + { + foundStruct = true; + } + else if (tokenizer.value == "__data_loc"sv) + { + foundDataLoc = true; + } + else if (tokenizer.value == "__rel_loc"sv) + { + foundRelLoc = true; + } + else if ( + tokenizer.value != "__attribute__"sv && + tokenizer.value != "const"sv && + tokenizer.value != "volatile"sv) + { + baseType = m_name; + m_name = tokenizer.value; + } + break; + + case TokenBrackets: + foundArray = true; + (void)ParseOptionalU16(tokenizer.value.data() + 1, &m_fixedArrayCount); + tokenizer.MoveNext(); + if (tokenizer.type == TokenIdent) + { + baseType = m_name; + m_name = tokenizer.value; + } + goto TokensDone; + + case TokenParentheses: + case TokenString: + // Ignored. + break; + + case TokenPunctuation: + // Most punctuation ignored. + if (tokenizer.value == "*"sv) + { + foundPointer = true; + } + break; + + default: + assert(false); + goto TokensDone; + } + } + +TokensDone: + + if (m_name.empty()) + { + m_name = noname; + } + + // DEDUCE: m_elementSize, m_format + + bool fixupElementSize = false; + + if (foundPointer) + { + m_format = PerfFieldFormatHex; + m_elementSize = longSize64 ? PerfFieldElementSize64 : PerfFieldElementSize32; + } + else if (foundStruct) + { + m_format = PerfFieldFormatNone; + m_elementSize = PerfFieldElementSize8; + } + else if (baseType.empty() || baseType == "int"sv) + { + m_format = foundUnsigned + ? PerfFieldFormatUnsigned + : PerfFieldFormatSigned; + if (foundLongLong) + { + m_elementSize = PerfFieldElementSize64; + } + else if (foundLong) + { + m_elementSize = longSize64 ? PerfFieldElementSize64 : PerfFieldElementSize32; + if (foundUnsigned) + { + m_format = PerfFieldFormatHex; // Use hex for unsigned long. + } + } + else if (foundShort) + { + m_elementSize = PerfFieldElementSize16; + } + else + { + m_elementSize = PerfFieldElementSize32; // "unsigned" or "signed" means "int". + if (baseType.empty() && !foundUnsigned && !foundSigned) + { + // Unexpected. + DEBUG_PRINTF("No baseType found for \"%.*s\"\n", + (unsigned)m_field.size(), m_field.data()); + } + } + } + else if (baseType == "char"sv) + { + m_format = foundUnsigned + ? PerfFieldFormatUnsigned + : foundSigned + ? PerfFieldFormatSigned + : PerfFieldFormatString; + m_elementSize = PerfFieldElementSize8; + } + else if (baseType == "u8"sv || baseType == "__u8"sv || baseType == "uint8_t"sv) + { + m_format = PerfFieldFormatUnsigned; + m_elementSize = PerfFieldElementSize8; + } + else if (baseType == "s8"sv || baseType == "__s8"sv || baseType == "int8_t"sv) + { + m_format = PerfFieldFormatSigned; + m_elementSize = PerfFieldElementSize8; + } + else if (baseType == "u16"sv || baseType == "__u16"sv || baseType == "uint16_t"sv) + { + m_format = PerfFieldFormatUnsigned; + m_elementSize = PerfFieldElementSize16; + } + else if (baseType == "s16"sv || baseType == "__s16"sv || baseType == "int16_t"sv) + { + m_format = PerfFieldFormatSigned; + m_elementSize = PerfFieldElementSize16; + } + else if (baseType == "u32"sv || baseType == "__u32"sv || baseType == "uint32_t"sv) + { + m_format = PerfFieldFormatUnsigned; + m_elementSize = PerfFieldElementSize32; + } + else if (baseType == "s32"sv || baseType == "__s32"sv || baseType == "int32_t"sv) + { + m_format = PerfFieldFormatSigned; + m_elementSize = PerfFieldElementSize32; + } + else if (baseType == "u64"sv || baseType == "__u64"sv || baseType == "uint64_t"sv) + { + m_format = PerfFieldFormatUnsigned; + m_elementSize = PerfFieldElementSize64; + } + else if (baseType == "s64"sv || baseType == "__s64"sv || baseType == "int64_t"sv) + { + m_format = PerfFieldFormatSigned; + m_elementSize = PerfFieldElementSize64; + } + else + { + m_format = PerfFieldFormatHex; + fixupElementSize = true; + } + + // FIXUP: m_format + + if (m_format == PerfFieldFormatUnsigned || m_format == PerfFieldFormatSigned) + { + // If valid, isSigned overrides baseType. + switch (isSigned) + { + default: break; + case 0: m_format = PerfFieldFormatUnsigned; break; + case 1: m_format = PerfFieldFormatSigned; break; + } + } + + // DEDUCE: m_array + + if (foundRelLoc) + { + m_array = PerfFieldArrayRelDyn; + m_fixedArrayCount = 0; + } + else if (foundDataLoc) + { + m_array = PerfFieldArrayDynamic; + m_fixedArrayCount = 0; + } + else if (foundArray) + { + m_array = PerfFieldArrayFixed; + if (fixupElementSize && m_fixedArrayCount != 0 && m_size % m_fixedArrayCount == 0) + { + // Try to deduce element size from size and array count. + switch (m_size / m_fixedArrayCount) + { + default: + break; + case 1: + m_elementSize = PerfFieldElementSize8; + fixupElementSize = false; + break; + case 2: + m_elementSize = PerfFieldElementSize16; + fixupElementSize = false; + break; + case 4: + m_elementSize = PerfFieldElementSize32; + fixupElementSize = false; + break; + case 8: + m_elementSize = PerfFieldElementSize64; + fixupElementSize = false; + break; + } + } + } + else + { + m_array = PerfFieldArrayNone; + m_fixedArrayCount = 0; + + // If valid, size overrides element size deduced from type name. + switch (m_size) + { + default: + break; + case 1: + m_elementSize = PerfFieldElementSize8; + fixupElementSize = false; + break; + case 2: + m_elementSize = PerfFieldElementSize16; + fixupElementSize = false; + break; + case 4: + m_elementSize = PerfFieldElementSize32; + fixupElementSize = false; + break; + case 8: + m_elementSize = PerfFieldElementSize64; + fixupElementSize = false; + break; + } + } + + if (fixupElementSize) + { + m_elementSize = PerfFieldElementSize8; + } +} + +std::string_view +PerfFieldMetadata::GetFieldBytes( + _In_reads_bytes_(eventRawDataSize) void const* eventRawData, + uintptr_t eventRawDataSize, + bool fileBigEndian) const noexcept +{ + std::string_view result; + PerfByteReader const byteReader(fileBigEndian); + auto const eventRawDataChars = static_cast(eventRawData); + + if (static_cast(m_offset) + m_size <= eventRawDataSize) + { + if (m_size == 0) + { + // size 0 means "the rest of the event data" + result = { eventRawDataChars + m_offset, eventRawDataSize - m_offset }; + } + else switch (m_array) + { + default: + result = { eventRawDataChars + m_offset, m_size }; + break; + + case PerfFieldArrayDynamic: + case PerfFieldArrayRelDyn: + if (m_size == 4) + { + // 4-byte value is an offset/length pair leading to the real data. + auto const dyn = byteReader.ReadAsU32(eventRawDataChars + m_offset); + auto const dynSize = dyn >> 16; + auto dynOffset = dyn & 0xFFFF; + if (m_array == PerfFieldArrayRelDyn) + { + // offset is relative to end of field. + dynOffset += m_offset + m_size; + } + + if (dynOffset + dynSize <= eventRawDataSize) + { + result = { eventRawDataChars + dynOffset, dynSize }; + } + } + else if (m_size == 2) + { + // 2-byte value is an offset leading to the real data, size is strlen. + size_t dynOffset = byteReader.ReadAsU16(eventRawDataChars + m_offset); + if (m_array == PerfFieldArrayRelDyn) + { + // offset is relative to end of field. + dynOffset += m_offset + m_size; + } + + if (dynOffset < eventRawDataSize) + { + auto const dynSize = strnlen( + eventRawDataChars + dynOffset, + eventRawDataSize - dynOffset); + if (dynSize < eventRawDataSize - dynOffset) + { + result = { eventRawDataChars + dynOffset, dynSize }; + } + } + } + break; + } + } + + return result; +} + +PerfEventMetadata::~PerfEventMetadata() +{ + return; +} + +PerfEventMetadata::PerfEventMetadata() noexcept + : m_systemName() + , m_formatFileContents() + , m_name() + , m_printFmt() + , m_fields() + , m_id() + , m_commonFieldCount() + , m_commonFieldsSize() + , m_kind() +{ + return; +} + +void +PerfEventMetadata::Clear() noexcept +{ + m_systemName = {}; + m_name = {}; + m_printFmt = {}; + m_fields = {}; + m_id = {}; + m_commonFieldCount = {}; + m_commonFieldsSize = {}; + m_kind = {}; +} + +bool +PerfEventMetadata::Parse( + bool longSize64, + std::string_view systemName, + std::string_view formatFileContents) noexcept(false) +{ + Clear(); + + m_systemName = systemName; + m_formatFileContents = formatFileContents; + + bool foundId = false; + auto p = formatFileContents.data(); + auto const pEnd = p + formatFileContents.size(); + + // Search for lines like "NAME: VALUE..." + while (p != pEnd) + { + ContinueNextLine: + + // Skip any newlines. + while (IsEolChar(*p)) + { + p += 1; + if (p == pEnd) goto Done; + } + + // Skip spaces. + while (IsSpaceOrTab(*p)) + { + DEBUG_PRINTF("Space before propname in event\n"); + p += 1; // Unexpected. + if (p == pEnd) goto Done; + } + + // "NAME:" + auto const pPropName = p; + while (*p != ':') + { + if (IsEolChar(*p)) + { + DEBUG_PRINTF("EOL before ':' in format\n"); + goto ContinueNextLine; // Unexpected. + } + + p += 1; + + if (p == pEnd) + { + DEBUG_PRINTF("EOF before ':' in format\n"); + goto Done; // Unexpected. + } + } + + auto const propName = std::string_view(pPropName, p - pPropName); + p += 1; // Skip ':' + + // Skip spaces. + while (p != pEnd && IsSpaceOrTab(*p)) + { + p += 1; + } + + auto const pPropValue = p; + + // "VALUE..." + while (p != pEnd && !IsEolChar(*p)) + { + char consumed; + + consumed = *p; + p += 1; + + if (consumed == '"') + { + p = ConsumeString(p, pEnd, '"'); + } + } + + // Did we find something we can use? + if (propName == "name"sv) + { + m_name = std::string_view(pPropValue, p - pPropValue); + } + else if (propName == "ID"sv && p != pEnd) + { + errno = 0; + m_id = strtoul(pPropValue, nullptr, 0); + foundId = 0 == errno; + } + else if (propName == "print fmt"sv) + { + m_printFmt = std::string_view(pPropValue, p - pPropValue); + } + else if (propName == "format"sv) + { + bool common = true; + m_fields.clear(); + + // Search for lines like: " field:TYPE NAME; offset:N; size:N; signed:N;" + while (p != pEnd) + { + assert(IsEolChar(*p)); + + if (pEnd - p >= 2 && p[0] == '\r' && p[1] == '\n') + { + p += 2; // Skip CRLF. + } + else + { + p += 1; // Skip CR or LF. + } + + auto const pLine = p; + while (p != pEnd && !IsEolChar(*p)) + { + p += 1; + } + + if (pLine == p) + { + // Blank line. + if (common) + { + // First blank line means we're done with common fields. + common = false; + continue; + } + else + { + // Second blank line means we're done with format. + break; + } + } + + m_fields.push_back(PerfFieldMetadata::Parse(longSize64, std::string_view(pLine, p - pLine))); + if (m_fields.back().Field().empty()) + { + DEBUG_PRINTF("Field parse failure\n"); + m_fields.pop_back(); // Unexpected. + } + else + { + m_commonFieldCount += common; + } + } + } + } + +Done: + + if (m_commonFieldCount == 0) + { + m_commonFieldsSize = 0; + } + else + { + auto const& lastCommonField = m_fields[m_commonFieldCount - 1]; + m_commonFieldsSize = lastCommonField.Offset() + lastCommonField.Size(); + } + + m_kind = + m_fields.size() > m_commonFieldCount && + m_fields[m_commonFieldCount].Name() == "eventheader_flags"sv + ? PerfEventKind::EventHeader + : PerfEventKind::Normal; + return !m_name.empty() && foundId; +} diff --git a/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/src/PerfEventSessionInfo.cpp b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/src/PerfEventSessionInfo.cpp new file mode 100644 index 0000000000000..089e2b7d49777 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/src/PerfEventSessionInfo.cpp @@ -0,0 +1,124 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include +#include + +using namespace tracepoint_decode; + +static auto const Billion = 1000000000u; + +void +PerfEventSessionInfo::SetClockid( + uint32_t clockid) noexcept +{ + m_clockId = clockid; +} + +void +PerfEventSessionInfo::SetClockData( + uint32_t clockid, + uint64_t wallClockNS, + uint64_t clockidTimeNS) noexcept +{ + if (clockid == 0xFFFFFFFF) + { + // Offset is unspecified. + + m_clockOffsetSec = 0; + m_clockOffsetNsec = 0; + m_clockId = clockid; + m_clockOffsetKnown = false; + } + else if (clockidTimeNS <= wallClockNS) + { + // Offset is positive. + + // wallClockNS = clockidTimeNS + offsetNS + // offsetNS = wallClockNS - clockidTimeNS + auto const offsetNS = wallClockNS - clockidTimeNS; + + // offsetNS = sec * Billion + nsec + + // sec = offsetNS / Billion + m_clockOffsetSec = static_cast(offsetNS / Billion); + + // nsec = offsetNS % Billion + m_clockOffsetNsec = static_cast(offsetNS % Billion); + + m_clockId = clockid; + m_clockOffsetKnown = true; + } + else + { + // Offset is negative. + + // wallClockNS = clockidTimeNS + offsetNS + // offsetNS = wallClockNS - clockidTimeNS + // -negOffsetNS = wallClockNS - clockidTimeNS + // negOffsetNS = clockidTimeNS - wallClockNS + auto const negOffsetNS = clockidTimeNS - wallClockNS; + + // negOffsetNS = (negOffsetNS / Billion) * Billion + (negOffsetNS % Billion) + // negOffsetNS = (negOffsetNS / Billion) * Billion + (negOffsetNS % Billion) - Billion + Billion + // negOffsetNS = (negOffsetNS / Billion + 1) * Billion + (negOffsetNS % Billion) - Billion + + // negOffsetNS = negSec * Billion + negNsec + // negSec = negOffsetNS / Billion + 1 + // negNsec = (negOffsetNS % Billion) - Billion + + // sec = -(negOffsetNS / Billion + 1) + m_clockOffsetSec = -static_cast(negOffsetNS / Billion) - 1; + + // nsec = -((negOffsetNS % Billion) - Billion) + m_clockOffsetNsec = Billion - static_cast(negOffsetNS % Billion); + + // Fix up case where nsec is too large. + if (m_clockOffsetNsec == Billion) + { + m_clockOffsetSec += 1; + m_clockOffsetNsec -= Billion; + } + + m_clockId = clockid; + m_clockOffsetKnown = true; + } +} + +void +PerfEventSessionInfo::GetClockData( + _Out_ uint64_t* wallClockNS, + _Out_ uint64_t* clockidTimeNS) const noexcept +{ + if (m_clockOffsetSec >= 0) + { + *clockidTimeNS = 0; + *wallClockNS = static_cast(m_clockOffsetSec) * Billion + m_clockOffsetNsec; + } + else + { + *wallClockNS = 0; + *clockidTimeNS = static_cast(-m_clockOffsetSec) * Billion - m_clockOffsetNsec; + } +} + +PerfEventTimeSpec +PerfEventSessionInfo::ClockOffset() const noexcept +{ + return { m_clockOffsetSec, m_clockOffsetNsec }; +} + +PerfEventTimeSpec +PerfEventSessionInfo::TimeToRealTime(uint64_t time) const noexcept +{ + auto sec = static_cast(time / Billion); + auto nsec = static_cast(time % Billion); + sec += m_clockOffsetSec; + nsec += m_clockOffsetNsec; + if (nsec >= Billion) + { + sec += 1; + nsec -= Billion; + } + return { sec, nsec }; +} diff --git a/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/src/tracepoint-decodeConfig.cmake.in b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/src/tracepoint-decodeConfig.cmake.in new file mode 100644 index 0000000000000..1858ed0106e53 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint-decode-cpp/src/tracepoint-decodeConfig.cmake.in @@ -0,0 +1,2 @@ +@PACKAGE_INIT@ +include("${CMAKE_CURRENT_LIST_DIR}/tracepoint-decodeTargets.cmake") diff --git a/src/native/external/LinuxTracepoints/libtracepoint/CMakeLists.txt b/src/native/external/LinuxTracepoints/libtracepoint/CMakeLists.txt new file mode 100644 index 0000000000000..60490c018663b --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint/CMakeLists.txt @@ -0,0 +1,47 @@ +cmake_minimum_required(VERSION 3.10) +include(../version.cmake) +project(tracepoint + VERSION ${LINUXTRACEPOINTS_VERSION} + DESCRIPTION "Linux tracepoints interface 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") +set(BUILD_TESTS ON CACHE BOOL "Build test code") +set(BUILD_TOOLS ON CACHE BOOL "Build tool 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) + add_subdirectory(src) + + if(BUILD_TESTS) + add_subdirectory(utest) + endif() + + if(BUILD_SAMPLES) + add_subdirectory(samples) + endif() + + if(BUILD_TOOLS) + add_subdirectory(tools) + endif() + +endif() diff --git a/src/native/external/LinuxTracepoints/libtracepoint/LICENSE b/src/native/external/LinuxTracepoints/libtracepoint/LICENSE new file mode 100644 index 0000000000000..9e841e7a26e4e --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint/LICENSE @@ -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 diff --git a/src/native/external/LinuxTracepoints/libtracepoint/README.md b/src/native/external/LinuxTracepoints/libtracepoint/README.md new file mode 100644 index 0000000000000..88d0ae527a9cb --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint/README.md @@ -0,0 +1,38 @@ +# libtracepoint + +`tracepoint.h` defines a low-level C/C++ tracing interface. The +[default implementation](src/tracepoint.c) +writes data to the Linux +[user_events](https://docs.kernel.org/trace/user_events.html) facility. +The implementation of this interface can be replaced at link time to support +alternative scenarios, e.g. testing with a mock tracing implementation. + +- [samples/tracepoint-sample.c](samples/tracepoint-sample.c) - + demonstrates basic usage of the interface. +- [tracepoint.h](include/tracepoint/tracepoint.h) - + interface functions. +- [tracepoint-state.h](include/tracepoint/tracepoint-state.h) - + interface data types. +- [tracepoint.c](src/tracepoint.c) - + default implementation. +- [tracepoint-provider.h](include/tracepoint/tracepoint-provider.h) - + high-level C/C++ API for writing tracepoint events to any implementation + of the tracepoint interface. + +`tracepoint.h` is a low-level interface. Application developers are more likely +to use a higher-level library implemented on top of this interface, such as +`tracepoint-provider.h`. + +This library includes a `tracepoint-register` tool that can be used to +pre-register a `user_event` tracepoint. This allows you to start collecting a +trace before the corresponding scenario starts running. + +Alternative implementations of this interface are expected, e.g. a mock +tracing implementation could be used for testing. A developer would +select the alternative implementation by linking against a different +library. + +An alternative implementation that writes directly to a file (bypassing the +kernel's event routing and filtering) could be implemented as follows: + +- [../libeventheader-tracepoint/samples/tracepoint-file.cpp](../libeventheader-tracepoint/samples/tracepoint-file.cpp) diff --git a/src/native/external/LinuxTracepoints/libtracepoint/include/CMakeLists.txt b/src/native/external/LinuxTracepoints/libtracepoint/include/CMakeLists.txt new file mode 100644 index 0000000000000..bed27a0e85325 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint/include/CMakeLists.txt @@ -0,0 +1,47 @@ +cmake_minimum_required(VERSION 3.10) +include(../../version.cmake) +project(tracepoint-headers + VERSION ${LINUXTRACEPOINTS_VERSION} + DESCRIPTION "Linux tracepoints interface for C/C++ (headers)" + HOMEPAGE_URL "https://github.com/microsoft/LinuxTracepoints" + LANGUAGES C CXX) +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) + +list(APPEND TRACEPOINT_HEADERS + "${PROJECT_SOURCE_DIR}/tracepoint/tracepoint.h") + +if(NOT WIN32) + list(APPEND TRACEPOINT_HEADERS + "${PROJECT_SOURCE_DIR}/tracepoint/tracepoint-impl.h" + "${PROJECT_SOURCE_DIR}/tracepoint/tracepoint-provider.h" + "${PROJECT_SOURCE_DIR}/tracepoint/tracepoint-state.h") +endif() + +# tracepoint-headers = TRACEPOINT_HEADERS +add_library(tracepoint-headers INTERFACE) +target_include_directories(tracepoint-headers + INTERFACE + "$" + "$") +set_target_properties(tracepoint-headers PROPERTIES + PUBLIC_HEADER "${TRACEPOINT_HEADERS}") +install(TARGETS tracepoint-headers + EXPORT tracepoint-headersTargets + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/tracepoint) +install(EXPORT tracepoint-headersTargets + FILE "tracepoint-headersTargets.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/tracepoint-headers") +configure_package_config_file( + "${CMAKE_CURRENT_SOURCE_DIR}/tracepoint-headersConfig.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/tracepoint-headersConfig.cmake" + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/tracepoint-headers" + NO_SET_AND_CHECK_MACRO + NO_CHECK_REQUIRED_COMPONENTS_MACRO) +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/tracepoint-headersConfigVersion.cmake" + COMPATIBILITY SameMinorVersion) +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/tracepoint-headersConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/tracepoint-headersConfigVersion.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/tracepoint-headers") diff --git a/src/native/external/LinuxTracepoints/libtracepoint/include/tracepoint-headersConfig.cmake.in b/src/native/external/LinuxTracepoints/libtracepoint/include/tracepoint-headersConfig.cmake.in new file mode 100644 index 0000000000000..f89bddd454bf8 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint/include/tracepoint-headersConfig.cmake.in @@ -0,0 +1,2 @@ +@PACKAGE_INIT@ +include("${CMAKE_CURRENT_LIST_DIR}/tracepoint-headersTargets.cmake") diff --git a/src/native/external/LinuxTracepoints/libtracepoint/include/tracepoint/tracepoint-impl.h b/src/native/external/LinuxTracepoints/libtracepoint/include/tracepoint/tracepoint-impl.h new file mode 100644 index 0000000000000..f64fc8eeb4d73 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint/include/tracepoint/tracepoint-impl.h @@ -0,0 +1,250 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +/* +Helper functions for use by implementations of the tracepoint interface. +*/ + +#pragma once +#ifndef _included_tracepoint_impl_h +#define _included_tracepoint_impl_h 1 + +#include +#include +#include // qsort + +#ifndef assert +#include +#endif + +#ifdef WIN32 +#define __atomic_store_n(ptr, val, order) *(ptr) = (val) +#endif + +// Don't warn if user of this header doesn't use every function. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" + +// For use by tracepoint_fix_array. +static int +tracepoint_fix_array_compare(void const* p1, void const* p2) +{ + // Reverse sort so that NULL goes at end. + void const* v1 = *(void const* const*)p1; + void const* v2 = *(void const* const*)p2; + return v1 < v2 ? 1 : v1 == v2 ? 0 : -1; +} + +/* +Remove duplicates and NULLs from an array of pointers. +For use by tracepoint_open_provider_with_tracepoints. +Returns the new end of the array (the position after the last non-NULL value). +*/ +static void* +tracepoint_fix_array(void const** start_ptr, void const** stop_ptr) +{ + void const** good_ptr = start_ptr; + void const** next_ptr; + + assert(stop_ptr - start_ptr >= 0); + assert(stop_ptr - start_ptr <= 0x7fffffff); + + if (start_ptr != stop_ptr) + { + // Sort. + qsort(start_ptr, (size_t)(stop_ptr - start_ptr), sizeof(void*), tracepoint_fix_array_compare); + + // Remove adjacent repeated elements. + for (; good_ptr + 1 != stop_ptr; good_ptr += 1) + { + if (*good_ptr == *(good_ptr + 1)) + { + void const** next_ptr; + for (next_ptr = good_ptr + 2; next_ptr != stop_ptr; next_ptr += 1) + { + if (*good_ptr != *next_ptr) + { + good_ptr += 1; + *good_ptr = *next_ptr; + } + } + break; + } + } + + if (*good_ptr != NULL) + { + good_ptr += 1; + } + + // Fill any remaining entries with NULL + for (next_ptr = good_ptr; next_ptr != stop_ptr; next_ptr += 1) + { + if (*next_ptr == NULL) + { + break; + } + + *next_ptr = NULL; + } + } + + return good_ptr; +} + +/* +Default implementation of tracepoint_open_provider_with_tracepoints. +*/ +static int +tracepoint_open_provider_with_tracepoints_impl( + tracepoint_provider_state* provider_state, + tracepoint_definition const** tp_definition_start, + tracepoint_definition const** tp_definition_stop) +{ + int err = tracepoint_open_provider(provider_state); + if (err != 0) + { + return err; + } + + tracepoint_definition const** adjusted_stop = (tracepoint_definition const**) + tracepoint_fix_array((void const**)tp_definition_start, (void const**)tp_definition_stop); + int const count = (int)(adjusted_stop - tp_definition_start); + for (int i = 0; i < count; i += 1) + { + (void)tracepoint_connect( + tp_definition_start[i]->state, + provider_state, + tp_definition_start[i]->tp_name_args); + } + + return 0; +} + +/* +Performs common initialization for an implementation of +tracepoint_open_provider. + +This should be called while holding a lock guarding the providers so that the +updates appear atomic. + +- If there are any tracepoints connected to this provider, disconnects them and + resets them to TRACEPOINT_STATE_INIT. +- Sets provider's data_file field to the specified value. +- Sets provider's next and prev fields to &tracepoint_list_head. +*/ +static void +tracepoint_open_provider_impl( + tracepoint_provider_state* provider_state, + int data_file) +{ + // Reset all tracepoints in our list. + tracepoint_list_node* node = provider_state->tracepoint_list_head.next; + if (node != NULL) + { + assert(node->prev == &provider_state->tracepoint_list_head); + while (node != &provider_state->tracepoint_list_head) + { + tracepoint_state* tp_state = (tracepoint_state*)((char*)node - offsetof(tracepoint_state, tracepoint_list_link)); + node = node->next; + assert(node->prev == &tp_state->tracepoint_list_link); + + __atomic_store_n(&tp_state->status_word, 0, __ATOMIC_RELAXED); + __atomic_store_n(&tp_state->write_index, -1, __ATOMIC_RELAXED); + __atomic_store_n(&tp_state->provider_state, NULL, __ATOMIC_RELAXED); + tp_state->tracepoint_list_link.next = NULL; + tp_state->tracepoint_list_link.prev = NULL; + } + } + + __atomic_store_n(&provider_state->data_file, data_file, __ATOMIC_RELAXED); + __atomic_store_n(&provider_state->ref_count, 0, __ATOMIC_RELAXED); + provider_state->tracepoint_list_head.next = &provider_state->tracepoint_list_head; + provider_state->tracepoint_list_head.prev = &provider_state->tracepoint_list_head; +} + +/* +Performs common initialization for an implementation of +tracepoint_close_provider. + +This should be called while holding a lock guarding the providers so that the +updates appear atomic. + +- If there are any tracepoints connected to this provider, disconnects them and + resets them to TRACEPOINT_STATE_INIT. +- Sets provider's data_file field to -1. +- Sets provider's next and prev fields to &tracepoint_list_head. +*/ +static void +tracepoint_close_provider_impl( + tracepoint_provider_state* provider_state) +{ + tracepoint_open_provider_impl(provider_state, -1); +} + +/* +Performs common initialization for an implementation of tracepoint_connect. + +This should be called while holding a lock guarding the providers so that the +updates appear atomic. + +- Removes tp_state from its current list, if any. +- Initializes tp_state with the specified field values. +- If provider_state is not NULL, adds tp_state to provider_state's list. +*/ +static void +tracepoint_connect_impl( + tracepoint_state* tp_state, + tracepoint_provider_state* provider_state, + int write_index) +{ + if (tp_state->provider_state != NULL) + { + // Disconnect from existing provider. + assert(tp_state->tracepoint_list_link.next->prev == &tp_state->tracepoint_list_link); + tp_state->tracepoint_list_link.next->prev = tp_state->tracepoint_list_link.prev; + assert(tp_state->tracepoint_list_link.prev->next == &tp_state->tracepoint_list_link); + tp_state->tracepoint_list_link.prev->next = tp_state->tracepoint_list_link.next; + } + else + { + assert(tp_state->tracepoint_list_link.next == NULL); + assert(tp_state->tracepoint_list_link.prev == NULL); + } + + // Initialize event fields. + __atomic_store_n(&tp_state->write_index, write_index, __ATOMIC_RELAXED); + __atomic_store_n(&tp_state->provider_state, provider_state, __ATOMIC_RELAXED); + + if (provider_state == NULL) + { + // Leave disconnected + __atomic_store_n(&tp_state->status_word, 0, __ATOMIC_RELAXED); + tp_state->tracepoint_list_link.next = NULL; + tp_state->tracepoint_list_link.prev = NULL; + } + else + { + // Add to new provider's list. + if (provider_state->tracepoint_list_head.next == NULL) + { + assert(provider_state->tracepoint_list_head.prev == NULL); + provider_state->tracepoint_list_head.next = &provider_state->tracepoint_list_head; + provider_state->tracepoint_list_head.prev = &provider_state->tracepoint_list_head; + } + else + { + assert(provider_state->tracepoint_list_head.prev != NULL); + } + + tracepoint_list_node* next = provider_state->tracepoint_list_head.next; + provider_state->tracepoint_list_head.next = &tp_state->tracepoint_list_link; + next->prev = &tp_state->tracepoint_list_link; + tp_state->tracepoint_list_link.next = next; + tp_state->tracepoint_list_link.prev = &provider_state->tracepoint_list_head; + } +} + +#pragma GCC diagnostic pop // "-Wunused-function" + +#endif // _included_tracepoint_impl_h diff --git a/src/native/external/LinuxTracepoints/libtracepoint/include/tracepoint/tracepoint-provider.h b/src/native/external/LinuxTracepoints/libtracepoint/include/tracepoint/tracepoint-provider.h new file mode 100644 index 0000000000000..0547019c091a9 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint/include/tracepoint/tracepoint-provider.h @@ -0,0 +1,826 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +/* +Macros for generating Linux user_events tracepoints via libtracepoint. + +Prerequisites (if not met, register/write/unregister will be no-ops): + +- Kernel built with tracefs and UserEvents (CONFIG_USER_EVENTS=y). +- tracefs mounted (e.g. /sys/kernel/tracing or /sys/kernel/debug/tracing). +- Caller must have appropriate permissions: x on tracefs mount point, + rw on .../tracing/user_events_data. + +Quick start: + + #include + + // Define a group of tracepoints that register and unregister together. + TPP_DEFINE_PROVIDER(MyProvider); + + int main(int argc, char* argv[]) + { + // Register all tracepoints in group. + TPP_REGISTER_PROVIDER(MyProvider); + + // If the "MyTracepoint1" tracepoint is registered and enabled, + // evaluate the field value expressions and generate a tracepoint + // event. This tracepoint will be registered and unregistered as + // part of the MyProvider group. + TPP_WRITE(MyProvider, "MyTracepoint1", + TPP_STRING("arg0", argv[0]), // field name is "arg0". + TPP_INT32("argc", argc)); // field name is "argc". + + // Unregister all tracepoints in group. + TPP_UNREGISTER_PROVIDER(MyProvider); + } + + // Advanced: Define my_tracepoint_func(char const* str, int32_t num) + // and my_tracepoint_func_enabled(void) functions for the "MyTracepoint2" + // tracepoint. This tracepoint will be registered and unregistered as part + // of the MyProvider group. The generated my_tracepoint_func_enabled + // function behaves the same as TPP_WRITE, but TPP_FUNCTION can be useful + // if you need to be able to check whether the tracepoint is enabled + // separately from generating the event. + TPP_FUNCTION(MyProvider, "MyTracepoint2", my_tracepoint, + TPP_STRING("name", str), // field name is "name". + TPP_INT32("count", num)); // field name is "size". + +The following top-level macros are provided: + +- TPP_DECLARE_PROVIDER(ProviderSymbol) - forward declaration of ProviderSymbol +- TPP_DEFINE_PROVIDER(ProviderSymbol) - definition of ProviderSymbol +- TPP_REGISTER_PROVIDER(ProviderSymbol) - activates all tracepoints in group +- TPP_UNREGISTER_PROVIDER(ProviderSymbol) - deactivates all tracepoints in group +- TPP_WRITE(ProviderSymbol, "tracepoint_name", field macros...) +- TPP_FUNCTION(ProviderSymbol, "tracepoint_name", func_name, field macros...) + +The following field macros are provided: + +- TPP_UINT8("field_name", value) - creates a "u8" field. +- TPP_INT8("field_name", value) - creates an "s8" field. +- TPP_UINT16("field_name", value) - creates a "u16" field. +- TPP_INT16("field_name", value) - creates an "s8" field. +- TPP_UINT32("field_name", value) - creates a "u32" field. +- TPP_INT32("field_name", value) - creates an "s32" field. +- TPP_UINT64("field_name", value) - creates a "u64" field. +- TPP_INT64("field_name", value) - creates an "s64" field. +- TPP_UINTPTR("field_name", value) - creates a "u32" or "u64" field. +- TPP_INTPTR("field_name", value) - creates an "s32" or "s64" field. +- TPP_STRING("field_name", value_ptr) - creates a "__rel_loc char[]" field. +- TPP_CHAR_ARRAY("field_name", size, value_ptr) - creates a "char[SIZE]" field. +- TPP_UCHAR_ARRAY("field_name", size, value_ptr) - creates an "unsigned char[SIZE]" field. +- TPP_STRUCT_PTR("field_name", "struct_type", size, value_ptr) - creates a "struct struct_type" field. +- TPP_CUSTOM_BYVAL - custom by-value field. +- TPP_CUSTOM_BYREF - custom by-ref field. +- TPP_CUSTOM_REL_LOC - custom rel_loc by-ref field. +- TPP_CUSTOM_REL_LOC_STR - custom rel_loc nul-terminated field. + +Usage notes: + +Symbols starting with TPP_ are part of the public interface of this +header. Symbols starting with "_tpp" are private internal implementation +details that are not supported for use outside this header and may be changed +or removed in future versions of this header. + +Tracepoint names, field names, and struct names need to be specified as string +literals like "my_field_name". They cannot be variables. Using variables may +cause compile errors or may successfully compile but lead to +incorrectly-generated tracepoints. + +Tracepoint names, field names, and struct names are restricted, but the +restrictions are not validated by the macros in this header: + +- Names need to be ASCII identifiers. They must start with '_' or an ASCII + letter and contain only '_', ASCII letters, and ASCII digits. +- Tracepoint names may be followed by ":FLAG1,FLAG2...". +- Tracepoint names need to be unique: you must not define multiple tracepoints + with the same name but different field names or field types. +- You can have multiple tracepoints with the same name, the same field names, + and the same field types, but this is inefficient and error-prone. Prefer to + define a function that generates the tracepoint instead of using the same + TPP_WRITE or TPP_FUNCTION in multiple places in your code. + +The Size parameter to TPP_CHAR_ARRAY and TPP_UCHAR_ARRAY must be an integer +literal like 45 or 0x20 or a macro that resolves to an integer literal. It +cannot be an expression, e.g. it cannot be sizeof(TYPE). +*/ + +#pragma once +#ifndef _included_tracepoint_provider_h +#define _included_tracepoint_provider_h 1 + +#include "tracepoint.h" +#include + +#ifdef __EDG__ +#pragma region Public_interface +#endif + +/* +This structure is left undefined to ensure a compile error for any attempt to +copy or dereference the provider symbol. The provider symbol is a token, not a +variable or handle. +*/ +struct _tpp_provider_symbol; + +/* +Macro TPP_DECLARE_PROVIDER(ProviderSymbol): +Invoke this macro to forward-declare a provider symbol that will be defined +elsewhere. TPP_DECLARE_PROVIDER is typically used in a header. + +An invocation of + + TPP_DECLARE_PROVIDER(MyProvider); + +can be thought of as expanding to something like this: + + extern "C" _tpp_provider_symbol MyProvider; // Forward declaration + +A symbol declared by TPP_DECLARE_PROVIDER must later be defined in a +.c or .cpp file using the TPP_DEFINE_PROVIDER macro. +*/ +#define TPP_DECLARE_PROVIDER(ProviderSymbol) \ + _tpp_EXTERN_C tracepoint_definition const* _tpp_PASTE2(__start__tppEventPtrs_, ProviderSymbol)[] __attribute__((weak, visibility("hidden"))); \ + _tpp_EXTERN_C tracepoint_definition const* _tpp_PASTE2(__stop__tppEventPtrs_, ProviderSymbol)[] __attribute__((weak, visibility("hidden"))); \ + _tpp_EXTERN_C struct _tpp_provider_symbol ProviderSymbol __attribute__((visibility("hidden"))); /* Empty provider variable to help with code navigation. */ \ + _tpp_EXTERN_C tracepoint_provider_state _tpp_PASTE2(_tppProvState_, ProviderSymbol) __attribute__((visibility("hidden"))) /* Actual provider variable is hidden behind prefix. */ + +/* +Macro TPP_DEFINE_PROVIDER(ProviderSymbol): +Invoke this macro to define the symbol for a provider. A provider is a +set of tracepoints that are registered and unregistered as a group. + +An invocation of + + TPP_DEFINE_PROVIDER(MyProvider); + +can be thought of as expanding to something like this: + + _tpp_provider_symbol MyProvider = { ... }; + +Note that the provider symbol is created in the "unregistered" state. A call +to TPP_WRITE with an unregistered provider is a safe no-op. Call +TPP_REGISTER_PROVIDER to register the provider. +*/ +#define TPP_DEFINE_PROVIDER(ProviderSymbol) \ + TPP_DECLARE_PROVIDER(ProviderSymbol); \ + tracepoint_provider_state _tpp_PASTE2(_tppProvState_, ProviderSymbol) = TRACEPOINT_PROVIDER_STATE_INIT \ + +/* +Macro TPP_UNREGISTER_PROVIDER(ProviderSymbol): +Invoke this macro to unregister your provider, deactivating all of the +tracepoints that are associated with the provider. Normally you will register +at component initialization (program startup or shared object load) and +unregister at component shutdown (program exit or shared object unload). + +It is ok to call TPP_UNREGISTER_PROVIDER on a provider that has not been +registered (e.g. if the call to TPP_REGISTER_PROVIDER failed). Unregistering +an unregistered provider is a safe no-op. + +After unregistering a provider, it is ok to register it again. In other words, +the following sequence is ok: + + TPP_REGISTER_PROVIDER(MyProvider); + ... + TPP_UNREGISTER_PROVIDER(MyProvider); + ... + TPP_REGISTER_PROVIDER(MyProvider); + ... + TPP_UNREGISTER_PROVIDER(MyProvider); + +Re-registering a provider should only happen because a component has been +uninitialized and then reinitialized. You should not register and unregister a +provider each time you need to write a few events. + +Note that unregistration is important, especially in the case of a shared +object that might be dynamically unloaded before the process ends. Failure to +unregister before the shared object unloads may cause process memory +corruption when the kernel tries to update the enabled/disabled states of +tracepoint variables that no longer exist. +*/ +#define TPP_UNREGISTER_PROVIDER(ProviderSymbol) \ + (tracepoint_close_provider( \ + &_tpp_PASTE2(_tppProvState_, ProviderSymbol) )) + +/* +Macro TPP_REGISTER_PROVIDER(ProviderSymbol): +Invoke this macro to register your provider, activating all of the tracepoints +that are associated with the provider. Normally you will register at component +initialization (program startup or shared object load) and unregister at +component shutdown (program exit or shared object unload). + +Returns 0 for success, errno otherwise. Result is primarily for +debugging/diagnostics and is usually ignored for production code. If +registration fails, subsequent TPP_WRITE will be a safe no-op. + +Thread safety: It is NOT safe to call TPP_REGISTER_PROVIDER while a +TPP_REGISTER_PROVIDER or TPP_UNREGISTER_PROVIDER for the same provider symbol +might be in progress on another thread. + +Precondition: The provider must be in the "unregistered" state. It is a fatal +error to call TPP_REGISTER_PROVIDER on a provider that is already registered. +*/ +#define TPP_REGISTER_PROVIDER(ProviderSymbol) \ + (tracepoint_open_provider_with_tracepoints( \ + &_tpp_PASTE2(_tppProvState_, ProviderSymbol), \ + _tpp_PASTE2(__start__tppEventPtrs_, ProviderSymbol), \ + _tpp_PASTE2(__stop__tppEventPtrs_, ProviderSymbol) )) + +/* +Macro TPP_WRITE(ProviderSymbol, "tracepoint_name", fields...): +Invoke this macro to generate a tracepoint event. + +An invocation of + + TPP_WRITE(MyProvider, "MyTracepointName", + TPP_INT32("int_field_name", my_int_value()), + TPP_STRING("string_field_name", my_string_value())); + +can be thought of as expanding to something like this: + + _tpp_enabled_MyTracepointName != 0 + ? _tpp_emit_MyTracepointName(my_int_value(), my_string_value()) + : 0 + +The "tracepoint_name" parameter must be a char string literal (not a variable) +and must be a valid ASCII identifier: it must start with '_' or an ASCII +letter, and must contain only '_', ASCII letters, and ASCII digits. + +Supports up to 99 field parameters (subject to compiler and tracepoint system +limitations). Each field parameter must be a field macro such as TPP_UINT8, +TPP_STRING, etc. + +Returns 0 if the tracepoint is written, EBADF if the tracepoint is unregistered +or disabled, or other errno for an error. Result is primarily for +debugging/diagnostics and is usually ignored for production code. +*/ +#define TPP_WRITE(ProviderSymbol, TracepointNameString, ...) \ + _tppWriteImpl(ProviderSymbol, TracepointNameString, __VA_ARGS__) + +/* +Macro TPP_FUNCTION(ProviderSymbol, "tracepoint_name", func_name, fields...): +Invoke this macro to define func_name(fields...) and func_name_enabled(). + +The generated func_name(fields...) function is equivalent to +TPP_WRITE(ProviderSymbol, "tracepoint_name", fields...), and the +func_name_enabled() function efficiently returns nonzero if "tracepoint_name" +is registered and enabled. + +An invocation of + + TPP_FUNCTION(MyProvider, "MyTracepointName", my_tracepoint, + TPP_INT32("int_field_name", myIntVar), + TPP_STRING("string_field_name", myString)); + +can be thought of as expanding to something like this: + + static int _tpp_enabled_MyTracepointName; // Hidden magic variable. + + int my_tracepoint_enabled(void) + { + return _tpp_enabled_MyTracepointName; + } + + int my_tracepoint(int32_t myIntVar, char const* myString) + { + return _tpp_enabled_MyTracepointName != 0 + ? _tpp_emit_MyTracepointName(myIntVar, myString) + : 0 + } + +The "tracepoint_name" parameter must be a char string literal (not a variable) +and must be a valid ASCII identifier: it must start with '_' or an ASCII +letter and must contain only '_', ASCII letters, and ASCII digits. + +Supports up to 99 field parameters (subject to compiler and tracepoint system +limitations). Each field parameter must be a field macro such as TPP_UINT8, +TPP_STRING, etc. + +The generated func_name(field values...) function returns 0 if the tracepoint +is written, EBADF if the tracepoint is unregistered or disabled, or other errno +for an error. Result is primarily for debugging/diagnostics and is usually +ignored for production code. +*/ +#define TPP_FUNCTION(ProviderSymbol, TracepointNameString, FunctionName, ...) \ + _tppFunctionImpl(ProviderSymbol, TracepointNameString, FunctionName, __VA_ARGS__) + +#define _tpp_NARGS(...) _tpp_NARGS_imp(_tpp_IS_EMPTY(__VA_ARGS__), (__VA_ARGS__)) + +/* +Field macros for use as field parameters to TPP_WRITE or TPP_FUNCTION: + +Example: + + TPP_WRITE(MyProvider, "MyTracepointName", + TPP_INT32("int_field_name", my_int_value()), + TPP_STRING("string_field_name", my_string_value())); + +The "FieldNameString" parameter must be a char string literal (not a variable) +and must be a valid ASCII identifier: it must start with '_' or an ASCII +letter and must contain only '_', ASCII letters, and ASCII digits. + +When these macros are used in TPP_WRITE, the Value parameter may be a complex +expression such as a call to a function. The Value parameter's expression will +only be evaluated if the tracepoint is registered and enabled. + +When these macros are used in TPP_FUNCTION, the Value parameter must be a +simple identifier. The Value parameter will be used as the name of the +corresponding function parameter. +*/ +#define TPP_UINT8(FieldNameString, Value) TPP_CUSTOM_BYVAL("u8 " FieldNameString, uint8_t, Value) +#define TPP_INT8(FieldNameString, Value) TPP_CUSTOM_BYVAL("s8 " FieldNameString, int8_t, Value) +#define TPP_UINT16(FieldNameString, Value) TPP_CUSTOM_BYVAL("u16 " FieldNameString, uint16_t, Value) +#define TPP_INT16(FieldNameString, Value) TPP_CUSTOM_BYVAL("s16 " FieldNameString, int16_t, Value) +#define TPP_UINT32(FieldNameString, Value) TPP_CUSTOM_BYVAL("u32 " FieldNameString, uint32_t, Value) +#define TPP_INT32(FieldNameString, Value) TPP_CUSTOM_BYVAL("s32 " FieldNameString, int32_t, Value) +#define TPP_UINT64(FieldNameString, Value) TPP_CUSTOM_BYVAL("u64 " FieldNameString, uint64_t, Value) +#define TPP_INT64(FieldNameString, Value) TPP_CUSTOM_BYVAL("s64 " FieldNameString, int64_t, Value) + +#if UINTPTR_MAX == UINT64_MAX +#define TPP_UINTPTR(FieldNameString, Value) TPP_CUSTOM_BYVAL("u64 " FieldNameString, uint64_t, Value) +#define TPP_INTPTR(FieldNameString, Value) TPP_CUSTOM_BYVAL("s64 " FieldNameString, int64_t, Value) +#elif UINTPTR_MAX == UINT32_MAX +#define TPP_UINTPTR(FieldNameString, Value) TPP_CUSTOM_BYVAL("u32 " FieldNameString, uint32_t, Value) +#define TPP_INTPTR(FieldNameString, Value) TPP_CUSTOM_BYVAL("s32 " FieldNameString, int32_t, Value) +#endif // UINTPTR size + +// TPP_STRING("FieldNameString", ValuePtr): +// Adds a NUL-terminated char string field to the tracepoint. +// +// Field value will be "" if ValuePtr == NULL. +// Field value size will be (ValuePtr == NULL ? 1 : strlen(ValuePtr) + 1). +// Resulting field will be defined as "__rel_loc char[] FieldNameString". +#define TPP_STRING(FieldNameString, ValuePtr) \ + TPP_CUSTOM_REL_LOC_STR("char[] " FieldNameString, char, ValuePtr) + +// TPP_CHAR_ARRAY("FieldNameString", Size, ValuePtr): +// Adds a fixed-length char[Size] field to the tracepoint. +// The Size parameter must be an integer literal or a macro that +// evaluates to an integer literal. It cannot be an expression. For example, +// it could be 32 or 0x20 but it cannot be something like sizeof(TYPE). +// +// ValuePtr is treated as void const*. +// Resulting field will be defined as "char[Size] FieldNameString". +#define TPP_CHAR_ARRAY(FieldNameString, Size, ValuePtr) \ + TPP_CUSTOM_BYREF("char[" _tpp_STRINGIZE(Size) "] " FieldNameString, void, Size, ValuePtr) + +// TPP_UCHAR_ARRAY("FieldNameString", Size, ValuePtr): +// Adds a fixed-length unsigned char[Size] field to the tracepoint. +// The Size parameter must be an integer literal or a macro that +// evaluates to an integer literal. It cannot be an expression. For example, +// it could be 32 or 0x20 but it cannot be something like sizeof(TYPE). +// +// ValuePtr is treated as void const*. +// Resulting field will be defined as "unsigned char[Size] FieldNameString". +#define TPP_UCHAR_ARRAY(FieldNameString, Size, ValuePtr) \ + TPP_CUSTOM_BYREF("unsigned char[" _tpp_STRINGIZE(Size) "] " FieldNameString, void, Size, ValuePtr) + +// TPP_STRUCT_PTR("FieldNameString", "StructTypeString", Size, ValuePtr): +// Adds struct data to the tracepoint. +// The Size parameter must be an integer literal or a macro that +// evaluates to an integer literal. It cannot be an expression. For example, +// it could be 32 or 0x20 but it cannot be something like sizeof(TYPE). +// +// ValuePtr is treated as void const*. +// Resulting field will be defined as "struct StructTypeString FieldNameString Size". +#define TPP_STRUCT_PTR(FieldNameString, StructTypeString, Size, ValuePtr) \ + TPP_CUSTOM_BYREF("struct " StructTypeString " " FieldNameString " " _tpp_STRINGIZE(Size), void, Size, ValuePtr) + +// TPP_CUSTOM_BYVAL("FieldDeclString", Ctype, Value): +// Advanced: Adds a by-value field to the tracepoint. +// +// Example: TPP_CUSTOM_BYVAL("unsigned int my_field_name", uint32_t, my_val) +// +// Value must be implicitly-convertible to Ctype. +// Field size will be sizeof(Ctype). +#define TPP_CUSTOM_BYVAL(FieldDeclString, Ctype, Value) \ + (_tppArgByVal, FieldDeclString, Ctype, Value) + +// TPP_CUSTOM_BYREF("FieldDeclString", Ctype, ConstantValueSize, ValuePtr): +// Advanced: Adds a fixed-length by-ref field to the tracepoint. +// +// Example: TPP_CUSTOM_BYREF("char[16] my_field_name", UUID, sizeof(UUID), &my_uuid) +// +// ValuePtr must be implicitly-convertible to Ctype const*. +// ConstantValueSize must be the compile-time constant field size in bytes. +#define TPP_CUSTOM_BYREF(FieldDeclString, Ctype, ConstantValueSize, ValuePtr) \ + (_tppArgByRef, FieldDeclString, Ctype, ConstantValueSize, ValuePtr) + +// TPP_CUSTOM_REL_LOC("FieldDeclString", Ctype, ValueSize, ValuePtr): +// Advanced: Adds a variable-length by-ref field to the tracepoint. +// +// ValueSize must be the field size in bytes. +// ValuePtr must be implicitly-convertible to Ctype const*. +// Resulting field will be defined as "__rel_loc FieldDeclString". +#define TPP_CUSTOM_REL_LOC(FieldDeclString, Ctype, ValueSize, ValuePtr) \ + (_tppArgRelLoc, FieldDeclString, Ctype, ValueSize, ValuePtr) + +// TPP_CUSTOM_REL_LOC_STR("FieldDeclString", Ctype, ValuePtr): +// Advanced: Adds a nul-terminated by-ref field to the tracepoint. +// +// TPP_CUSTOM_REL_LOC_STR("FieldDeclString", Ctype, ValuePtr) +// is approximately equivalent to +// TPP_CUSTOM_REL_LOC("FieldDeclString", Ctype, ValuePtr, strlen((char*)ValuePtr)) +// except that it treats NULL as "" and it only evaluates ValuePtr once. +// +// ValuePtr must be implicitly-convertible to Ctype const*. +// Resulting field will be defined as "__rel_loc FieldDeclString". +#define TPP_CUSTOM_REL_LOC_STR(FieldDeclString, Ctype, ValuePtr) \ + (_tppArgRelLocStr, FieldDeclString, Ctype, 0, ValuePtr) + +#ifdef __EDG__ +#pragma endregion +#endif + +#ifdef __EDG__ +#pragma region Internal_utility macros (for internal use only) +#endif + +#ifndef _tpp_NOEXCEPT +#ifdef __cplusplus +#define _tpp_NOEXCEPT noexcept +#else // __cplusplus +#define _tpp_NOEXCEPT +#endif // __cplusplus +#endif // _tpp_NOEXCEPT + +#ifndef _tpp_INLINE_ATTRIBUTES +#define _tpp_INLINE_ATTRIBUTES +#endif // _tpp_INLINE_ATTRIBUTES + +#ifdef __cplusplus +#define _tpp_EXTERN_C extern "C" +#else // __cplusplus +#define _tpp_EXTERN_C extern // In C, linkage is already "C". +#endif // __cplusplus + +// Internal implementation detail: Not for use outside of tracepoint-provider.h. +#define _tpp_PASTE2(a, b) _tpp_PASTE2_imp(a, b) +#define _tpp_PASTE2_imp(a, b) a##b + +// Internal implementation detail: Not for use outside of tracepoint-provider.h. +#define _tpp_PARENTHESIZE(...) (__VA_ARGS__) + +// Internal implementation detail: Not for use outside of tracepoint-provider.h. +#define _tpp_STRINGIZE(x) _tpp_STRINGIZE_imp(x) +#define _tpp_STRINGIZE_imp(x) #x + +// Internal implementation detail: Not for use outside of tracepoint-provider.h. +#define _tpp_CAT(a, ...) _tpp_CAT_imp(a, __VA_ARGS__) +#define _tpp_CAT_imp(a, ...) a##__VA_ARGS__ + +// Internal implementation detail: Not for use outside of tracepoint-provider.h. +#define _tpp_SPLIT(cond, ...) _tpp_SPLIT_imp(cond, (__VA_ARGS__)) +#define _tpp_SPLIT_imp(cond, args) _tpp_PASTE2(_tpp_SPLIT_imp, cond) args +#define _tpp_SPLIT_imp0(false_val, ...) false_val +#define _tpp_SPLIT_imp1(false_val, ...) __VA_ARGS__ + +// Internal implementation detail: Not for use outside of tracepoint-provider.h. +#define _tpp_IS_PARENTHESIZED(...) \ + _tpp_SPLIT(0, _tpp_CAT(_tpp_IS_PARENTHESIZED_imp, _tpp_IS_PARENTHESIZED_imp0 __VA_ARGS__)) +#define _tpp_IS_PARENTHESIZED_imp_tpp_IS_PARENTHESIZED_imp0 0, +#define _tpp_IS_PARENTHESIZED_imp0(...) 1 +#define _tpp_IS_PARENTHESIZED_imp1 1, + +// Internal implementation detail: Not for use outside of tracepoint-provider.h. +#define _tpp_IS_EMPTY(...) _tpp_SPLIT( \ + _tpp_IS_PARENTHESIZED(__VA_ARGS__), \ + _tpp_IS_PARENTHESIZED(_tpp_PARENTHESIZE __VA_ARGS__()), \ + 0) + +// Internal implementation detail: Not for use outside of tracepoint-provider.h. +#define _tpp_NARGS(...) _tpp_NARGS_imp(_tpp_IS_EMPTY(__VA_ARGS__), (__VA_ARGS__)) +#define _tpp_NARGS_imp(is_empty, args) _tpp_PASTE2(_tpp_NARGS_imp, is_empty) args +#define _tpp_NARGS_imp0(...) _tpp_PASTE2(_tpp_NARGS_imp2( \ + __VA_ARGS__, \ + 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, \ + 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, \ + 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, \ + 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, \ + 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, \ + 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, \ + 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, \ + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, \ + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, \ + 9, 8, 7, 6, 5, 4, 3, 2, 1, ), ) +#define _tpp_NARGS_imp1() 0 +#define _tpp_NARGS_imp2( \ + a1, a2, a3, a4, a5, a6, a7, a8, a9, \ + a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, \ + a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, \ + a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, \ + a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, \ + a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, \ + a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, \ + a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, \ + a80, a81, a82, a83, a84, a85, a86, a87, a88, a89, \ + a90, a91, a92, a93, a94, a95, a96, a97, a98, a99, \ + size, ...) size + +#ifdef __EDG__ +#pragma endregion +#endif + +#ifdef __EDG__ +#pragma region Internal_foreach macro (for internal use only) +#endif + +// Internal implementation detail: Not for use outside of tracepoint-provider.h. +#define _tpp_FOREACH(macro, ...) _tpp_FOR_imp(_tpp_NARGS(__VA_ARGS__), (macro, __VA_ARGS__)) +#define _tpp_FOR_imp(n, macroAndArgs) _tpp_PASTE2(_tpp_FOR_imp, n) macroAndArgs +#define _tpp_FOR_imp0(f, ...) +#define _tpp_FOR_imp1(f, a0) f(0, a0) +#define _tpp_FOR_imp2(f, a0, a1) f(0, a0) f(1, a1) +#define _tpp_FOR_imp3(f, a0, a1, a2) f(0, a0) f(1, a1) f(2, a2) +#define _tpp_FOR_imp4(f, a0, a1, a2, a3) f(0, a0) f(1, a1) f(2, a2) f(3, a3) +#define _tpp_FOR_imp5(f, a0, a1, a2, a3, a4) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) +#define _tpp_FOR_imp6(f, a0, a1, a2, a3, a4, a5) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) +#define _tpp_FOR_imp7(f, a0, a1, a2, a3, a4, a5, a6) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) +#define _tpp_FOR_imp8(f, a0, a1, a2, a3, a4, a5, a6, a7) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) +#define _tpp_FOR_imp9(f, a0, a1, a2, a3, a4, a5, a6, a7, a8) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) +#define _tpp_FOR_imp10(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) +#define _tpp_FOR_imp11(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) +#define _tpp_FOR_imp12(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) +#define _tpp_FOR_imp13(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) +#define _tpp_FOR_imp14(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) +#define _tpp_FOR_imp15(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) +#define _tpp_FOR_imp16(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) +#define _tpp_FOR_imp17(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) +#define _tpp_FOR_imp18(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) +#define _tpp_FOR_imp19(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) +#define _tpp_FOR_imp20(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) +#define _tpp_FOR_imp21(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) +#define _tpp_FOR_imp22(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) +#define _tpp_FOR_imp23(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) +#define _tpp_FOR_imp24(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) +#define _tpp_FOR_imp25(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) +#define _tpp_FOR_imp26(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) +#define _tpp_FOR_imp27(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) +#define _tpp_FOR_imp28(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) +#define _tpp_FOR_imp29(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) +#define _tpp_FOR_imp30(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) +#define _tpp_FOR_imp31(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) +#define _tpp_FOR_imp32(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) +#define _tpp_FOR_imp33(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) +#define _tpp_FOR_imp34(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) +#define _tpp_FOR_imp35(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) +#define _tpp_FOR_imp36(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) +#define _tpp_FOR_imp37(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) +#define _tpp_FOR_imp38(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) +#define _tpp_FOR_imp39(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) +#define _tpp_FOR_imp40(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) +#define _tpp_FOR_imp41(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) +#define _tpp_FOR_imp42(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) +#define _tpp_FOR_imp43(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) +#define _tpp_FOR_imp44(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) +#define _tpp_FOR_imp45(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) +#define _tpp_FOR_imp46(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) +#define _tpp_FOR_imp47(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) +#define _tpp_FOR_imp48(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) +#define _tpp_FOR_imp49(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) +#define _tpp_FOR_imp50(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) +#define _tpp_FOR_imp51(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) +#define _tpp_FOR_imp52(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) +#define _tpp_FOR_imp53(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) +#define _tpp_FOR_imp54(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) +#define _tpp_FOR_imp55(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) +#define _tpp_FOR_imp56(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) +#define _tpp_FOR_imp57(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) +#define _tpp_FOR_imp58(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) +#define _tpp_FOR_imp59(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) +#define _tpp_FOR_imp60(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) +#define _tpp_FOR_imp61(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) +#define _tpp_FOR_imp62(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) +#define _tpp_FOR_imp63(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) +#define _tpp_FOR_imp64(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) +#define _tpp_FOR_imp65(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) +#define _tpp_FOR_imp66(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) +#define _tpp_FOR_imp67(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) +#define _tpp_FOR_imp68(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) +#define _tpp_FOR_imp69(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) +#define _tpp_FOR_imp70(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) +#define _tpp_FOR_imp71(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) +#define _tpp_FOR_imp72(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) +#define _tpp_FOR_imp73(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) +#define _tpp_FOR_imp74(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) +#define _tpp_FOR_imp75(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) +#define _tpp_FOR_imp76(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) +#define _tpp_FOR_imp77(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) +#define _tpp_FOR_imp78(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) +#define _tpp_FOR_imp79(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) +#define _tpp_FOR_imp80(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) +#define _tpp_FOR_imp81(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) +#define _tpp_FOR_imp82(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) +#define _tpp_FOR_imp83(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) +#define _tpp_FOR_imp84(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) +#define _tpp_FOR_imp85(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) +#define _tpp_FOR_imp86(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) f(85, a85) +#define _tpp_FOR_imp87(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) f(85, a85) f(86, a86) +#define _tpp_FOR_imp88(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) f(85, a85) f(86, a86) f(87, a87) +#define _tpp_FOR_imp89(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87, a88) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) f(85, a85) f(86, a86) f(87, a87) f(88, a88) +#define _tpp_FOR_imp90(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87, a88, a89) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) f(85, a85) f(86, a86) f(87, a87) f(88, a88) f(89, a89) +#define _tpp_FOR_imp91(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87, a88, a89, a90) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) f(85, a85) f(86, a86) f(87, a87) f(88, a88) f(89, a89) f(90, a90) +#define _tpp_FOR_imp92(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87, a88, a89, a90, a91) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) f(85, a85) f(86, a86) f(87, a87) f(88, a88) f(89, a89) f(90, a90) f(91, a91) +#define _tpp_FOR_imp93(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87, a88, a89, a90, a91, a92) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) f(85, a85) f(86, a86) f(87, a87) f(88, a88) f(89, a89) f(90, a90) f(91, a91) f(92, a92) +#define _tpp_FOR_imp94(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87, a88, a89, a90, a91, a92, a93) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) f(85, a85) f(86, a86) f(87, a87) f(88, a88) f(89, a89) f(90, a90) f(91, a91) f(92, a92) f(93, a93) +#define _tpp_FOR_imp95(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87, a88, a89, a90, a91, a92, a93, a94) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) f(85, a85) f(86, a86) f(87, a87) f(88, a88) f(89, a89) f(90, a90) f(91, a91) f(92, a92) f(93, a93) f(94, a94) +#define _tpp_FOR_imp96(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87, a88, a89, a90, a91, a92, a93, a94, a95) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) f(85, a85) f(86, a86) f(87, a87) f(88, a88) f(89, a89) f(90, a90) f(91, a91) f(92, a92) f(93, a93) f(94, a94) f(95, a95) +#define _tpp_FOR_imp97(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87, a88, a89, a90, a91, a92, a93, a94, a95, a96) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) f(85, a85) f(86, a86) f(87, a87) f(88, a88) f(89, a89) f(90, a90) f(91, a91) f(92, a92) f(93, a93) f(94, a94) f(95, a95) f(96, a96) +#define _tpp_FOR_imp98(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87, a88, a89, a90, a91, a92, a93, a94, a95, a96, a97) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) f(85, a85) f(86, a86) f(87, a87) f(88, a88) f(89, a89) f(90, a90) f(91, a91) f(92, a92) f(93, a93) f(94, a94) f(95, a95) f(96, a96) f(97, a97) +#define _tpp_FOR_imp99(f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87, a88, a89, a90, a91, a92, a93, a94, a95, a96, a97, a98) f(0, a0) f(1, a1) f(2, a2) f(3, a3) f(4, a4) f(5, a5) f(6, a6) f(7, a7) f(8, a8) f(9, a9) f(10, a10) f(11, a11) f(12, a12) f(13, a13) f(14, a14) f(15, a15) f(16, a16) f(17, a17) f(18, a18) f(19, a19) f(20, a20) f(21, a21) f(22, a22) f(23, a23) f(24, a24) f(25, a25) f(26, a26) f(27, a27) f(28, a28) f(29, a29) f(30, a30) f(31, a31) f(32, a32) f(33, a33) f(34, a34) f(35, a35) f(36, a36) f(37, a37) f(38, a38) f(39, a39) f(40, a40) f(41, a41) f(42, a42) f(43, a43) f(44, a44) f(45, a45) f(46, a46) f(47, a47) f(48, a48) f(49, a49) f(50, a50) f(51, a51) f(52, a52) f(53, a53) f(54, a54) f(55, a55) f(56, a56) f(57, a57) f(58, a58) f(59, a59) f(60, a60) f(61, a61) f(62, a62) f(63, a63) f(64, a64) f(65, a65) f(66, a66) f(67, a67) f(68, a68) f(69, a69) f(70, a70) f(71, a71) f(72, a72) f(73, a73) f(74, a74) f(75, a75) f(76, a76) f(77, a77) f(78, a78) f(79, a79) f(80, a80) f(81, a81) f(82, a82) f(83, a83) f(84, a84) f(85, a85) f(86, a86) f(87, a87) f(88, a88) f(89, a89) f(90, a90) f(91, a91) f(92, a92) f(93, a93) f(94, a94) f(95, a95) f(96, a96) f(97, a97) f(98, a98) + +#ifdef __EDG__ +#pragma endregion +#endif + +#ifdef __EDG__ +#pragma region Internal_functions (for internal use only) +#endif + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + static inline uint32_t + _tppCreate1Vec(struct iovec* pVec, void const* pb, size_t cb) _tpp_NOEXCEPT _tpp_INLINE_ATTRIBUTES; + static inline uint32_t + _tppCreate1Vec(struct iovec* pVec, void const* pb, size_t cb) _tpp_NOEXCEPT + { + pVec[0].iov_base = (void*)pb; + pVec[0].iov_len = cb; + return (uint32_t)cb; + } + + static inline uint32_t + _tppCreate1RelLoc(struct iovec* pVec, void const* pb, uint16_t cb, uint32_t off, uint32_t* rel) _tpp_NOEXCEPT _tpp_INLINE_ATTRIBUTES; + static inline uint32_t + _tppCreate1RelLoc(struct iovec* pVec, void const* pb, uint16_t cb, uint32_t off, uint32_t* rel) _tpp_NOEXCEPT + { + // Before: *rel has the offset of rel_loc. + // After: *rel has (cch<<16) | (offset from rel_loc to string). + *rel = ((uint32_t)cb << 16) | ((off - *rel) & 0xFFFF); + return _tppCreate1Vec(pVec, pb, cb); + } + + static inline uint32_t + _tppCreate1Sz_char(struct iovec* pVec, char const* sz, uint32_t off, uint32_t* rel) _tpp_NOEXCEPT _tpp_INLINE_ATTRIBUTES; + static inline uint32_t + _tppCreate1Sz_char(struct iovec* pVec, char const* sz, uint32_t off, uint32_t* rel) _tpp_NOEXCEPT + { + typedef char _tppCHAR; + + _tppCHAR const* pch; + size_t cch; + if (!sz) + { + pch = ""; + cch = sizeof(""); + } + else + { + pch = sz; + for (cch = 0; pch[cch] != 0; cch += 1) {} + cch += 1; // nul-termination + if (cch > (65535 / sizeof(_tppCHAR))) + { + pch = "..."; + cch = sizeof("..."); + } + } + + uint16_t cb = (uint16_t)(cch * sizeof(_tppCHAR)); + return _tppCreate1RelLoc(pVec, pch, cb, off, rel); + } + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +// ********** NO FUNCTION DEFINITIONS BELOW THIS POINT *********************** + +#ifdef __EDG__ +#pragma endregion +#endif + +#ifdef __EDG__ +#pragma region Internal_implementation macros (for internal use only) +#endif + +/* +_tppApplyArgs and _tppApplyArgsN: Macro dispatchers. +_tppApplyArgs( macro, (handler, ...)) --> macro##handler(...) +_tppApplyArgsN(macro, n, (handler, ...)) --> macro##handler(n, ...) +*/ +#define _tppApplyArgs(macro, args) _tppApplyArgs_impA((macro, _tppApplyArgs_UNWRAP args)) +#define _tppApplyArgs_impA(args) _tppApplyArgs_impB args +#define _tppApplyArgs_impB(macro, handler, ...) _tppApplyArgs_CALL(macro, handler, (__VA_ARGS__)) +#define _tppApplyArgs_UNWRAP(...) __VA_ARGS__ +#define _tppApplyArgs_CALL(macro, handler, args) macro##handler args +#define _tppApplyArgsN(macro, n, args) _tppApplyArgsN_impA((macro, n, _tppApplyArgs_UNWRAP args)) +#define _tppApplyArgsN_impA(args) _tppApplyArgsN_impB args +#define _tppApplyArgsN_impB(macro, n, handler, ...) _tppApplyArgs_CALL(macro, handler, (n, __VA_ARGS__)) + +// Field type-name strings. +#define _tppFieldString(n, args) _tppApplyArgs(_tppFieldString, args) +#define _tppFieldString_tppArgByVal( FieldDeclString, Ctype, Value) " " FieldDeclString ";" +#define _tppFieldString_tppArgByRef( FieldDeclString, Ctype, ConstSize, ValuePtr) " " FieldDeclString ";" +#define _tppFieldString_tppArgRelLoc(FieldDeclString, Ctype, ValueSize, ValuePtr) " __rel_loc " FieldDeclString ";" +#define _tppFieldString_tppArgRelLocStr _tppFieldString_tppArgRelLoc + +// Count the iovecs needed for event field data. +#define _tppDataDescCount(n, args) _tppApplyArgs(_tppDataDescCount, args) +#define _tppDataDescCount_tppArgByVal( FieldDeclString, Ctype, Value) +1 +#define _tppDataDescCount_tppArgByRef( FieldDeclString, Ctype, ConstSize, ValuePtr) +1 +#define _tppDataDescCount_tppArgRelLoc(FieldDeclString, Ctype, ValueSize, ValuePtr) +2 +#define _tppDataDescCount_tppArgRelLocStr _tppDataDescCount_tppArgRelLoc + +// Temporary variables. +#define _tppDataDecls(n, args) _tppApplyArgsN(_tppDataDecls, n, args) +#define _tppDataDecls_tppArgByVal( N, FieldDeclString, Ctype, Value) Ctype _tppVal##N; +#define _tppDataDecls_tppArgByRef( N, FieldDeclString, Ctype, ConstSize, ValuePtr) Ctype const* _tppVal##N; +#define _tppDataDecls_tppArgRelLoc(N, FieldDeclString, Ctype, ValueSize, ValuePtr) Ctype const* _tppVal##N; uint32_t _tppRel##N; +#define _tppDataDecls_tppArgRelLocStr _tppDataDecls_tppArgRelLoc + +// Fixed-offset iovecs. +// All values are evaluated in a single comma-separated expression. +// This enables support for things like TPP_STRING(ReturnStdString().c_str()). +#define _tppDataDescVal(n, args) _tppApplyArgsN(_tppDataDescVal, n, args) +#define _tppDataDescVal_tppArgByVal(N, FieldDeclString, Ctype, Value) \ + _tppVal##N = (Value), \ + _tppOff += _tppCreate1Vec(&_tppVecs[_tppIdx++], &_tppVal##N, sizeof(Ctype)), +#define _tppDataDescVal_tppArgByRef(N, FieldDeclString, Ctype, ConstSize, ValuePtr) \ + _tppVal##N = (ValuePtr), \ + _tppOff += _tppCreate1Vec(&_tppVecs[_tppIdx++], _tppVal##N, ConstSize), +#define _tppDataDescVal_tppArgRelLoc(N, FieldDeclString, Ctype, ValueSize, ValuePtr) \ + _tppVal##N = (ValuePtr), \ + _tppOff += _tppCreate1Vec(&_tppVecs[_tppIdx++], &_tppRel##N, sizeof(_tppRel##N)), \ + _tppRel##N = _tppOff, +#define _tppDataDescVal_tppArgRelLocStr _tppDataDescVal_tppArgRelLoc + +// rel_loc iovecs. +#define _tppDataDescRel(n, args) _tppApplyArgsN(_tppDataDescRel, n, args) +#define _tppDataDescRel_tppArgByVal( N, FieldDeclString, Ctype, Value) +#define _tppDataDescRel_tppArgByRef( N, FieldDeclString, Ctype, ConstSize, ValuePtr) +#define _tppDataDescRel_tppArgRelLoc(N, FieldDeclString, Ctype, ValueSize, ValuePtr) \ + _tppOff += _tppCreate1RelLoc( &_tppVecs[_tppIdx++], _tppVal##N, ValueSize, _tppOff, &_tppRel##N), +#define _tppDataDescRel_tppArgRelLocStr(N, FieldDeclString, Ctype, ValueSize, ValuePtr) \ + _tppOff += _tppCreate1Sz_char(&_tppVecs[_tppIdx++], (char const*)_tppVal##N, _tppOff, &_tppRel##N), + +// TPP_FUNCTION function parameters. Note that the comma is in _tppFuncArg, not the handler. +#define _tppFuncArg(n, args) ,_tppApplyArgs(_tppFuncArg, args) +#define _tppFuncArg_tppArgByVal( FieldDeclString, Ctype, Value) Ctype Value +#define _tppFuncArg_tppArgByRef( FieldDeclString, Ctype, ConstSize, ValuePtr) Ctype const* ValuePtr +#define _tppFuncArg_tppArgRelLoc(FieldDeclString, Ctype, ValueSize, ValuePtr) uint16_t ValueSize, Ctype const* ValuePtr +#define _tppFuncArg_tppArgRelLocStr(FieldDeclString, Ctype, ValueSize, ValuePtr) Ctype const* ValuePtr + +// Function parameter list cases: func(void) or func(arg0 [, args...]) +#define _tppFunctionArgs(IsEmpty, Args) _tpp_PASTE2(_tppFunctionArgs, IsEmpty) Args +#define _tppFunctionArgs1(...) void +#define _tppFunctionArgs0(Arg0, ...) _tppApplyArgs(_tppFuncArg, Arg0) _tpp_FOREACH(_tppFuncArg, __VA_ARGS__) + +// Implement TPP_FUNCTION: +#define _tppFunctionImpl(ProviderSymbol, TracepointNameString, FunctionName, ...) \ + static tracepoint_state _tpp_PASTE2(_tppState_, FunctionName) = TRACEPOINT_STATE_INIT; \ + int _tpp_PASTE2(FunctionName, _enabled)(void) { \ + return TRACEPOINT_ENABLED(&_tpp_PASTE2(_tppState_, FunctionName)); \ + } \ + int FunctionName(_tppFunctionArgs(_tpp_IS_EMPTY(__VA_ARGS__), (__VA_ARGS__))) { \ + _tppCommonImpl(ProviderSymbol, TracepointNameString, _tpp_PASTE2(_tppState_, FunctionName), __VA_ARGS__) \ + return _tppWriteErr; \ + } \ + +// Implement TPP_WRITE: +#define _tppWriteImpl(ProviderSymbol, TracepointNameString, ...) ({ \ + static tracepoint_state _tppState = TRACEPOINT_STATE_INIT; \ + _tppCommonImpl(ProviderSymbol, TracepointNameString, _tppState, __VA_ARGS__) \ + _tppWriteErr; }) \ + +#define _tppCommonImpl(ProviderSymbol, TracepointNameString, TracepointState, ...) \ + static tracepoint_definition const _tppEvt = { \ + &TracepointState, \ + "" TracepointNameString _tpp_FOREACH(_tppFieldString, __VA_ARGS__) \ + }; \ + static tracepoint_definition const* _tppEvtPtr \ + __attribute__((section("_tppEventPtrs_" _tpp_STRINGIZE(ProviderSymbol)), used)) \ + = &_tppEvt; \ + int _tppWriteErr = 9 /*EBADF*/; \ + if (TRACEPOINT_ENABLED(&TracepointState)) { \ + struct iovec _tppVecs[1 _tpp_FOREACH(_tppDataDescCount, __VA_ARGS__)]; \ + _tppVecs[0].iov_len = 0; \ + unsigned _tppIdx = 1; /* first iovec is used by tracepoint_write */ \ + uint32_t _tppOff = 0; \ + (void)_tppOff; /* maybe unused (needed only if there is a rel_loc) */ \ + _tpp_FOREACH(_tppDataDecls, __VA_ARGS__) \ + ( /* Start of the eval-write expression. */ \ + _tpp_FOREACH(_tppDataDescVal, __VA_ARGS__) \ + _tpp_FOREACH(_tppDataDescRel, __VA_ARGS__) \ + _tppWriteErr = tracepoint_write(&TracepointState, _tppIdx, _tppVecs) \ + ) /* End of the eval-write expression. */; \ + } \ + +#ifdef __EDG__ +#pragma endregion +#endif + +#endif // _included_tracepoint_provider_h diff --git a/src/native/external/LinuxTracepoints/libtracepoint/include/tracepoint/tracepoint-state.h b/src/native/external/LinuxTracepoints/libtracepoint/include/tracepoint/tracepoint-state.h new file mode 100644 index 0000000000000..367cf2e2c901d --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint/include/tracepoint/tracepoint-state.h @@ -0,0 +1,133 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +/* +Type definitions and macros for the tracepoint interface. + +These definitions are separated from tracepoint.h to minimize namespace +pollution - these types and macros are needed in some scenarios where the +function prototypes from tracepoint.h do not need to be defined. +*/ + +#pragma once +#ifndef _included_tracepoint_state_h +#define _included_tracepoint_state_h 1 + +/* +Implementation detail - for use by the tracepoint implementation. +*/ +typedef struct tracepoint_list_node tracepoint_list_node; +struct tracepoint_list_node +{ + tracepoint_list_node* next; + tracepoint_list_node* prev; +}; + +/* +Macro returns a non-zero value if any consumer is listening to the specified +tracepoint. +*/ +#define TRACEPOINT_ENABLED(tp_state) __atomic_load_n(&(tp_state)->status_word, __ATOMIC_RELAXED) + +/* +Opaque provider state, typically a non-const static/global for each provider. + +Basic usage: + +- Allocate memory for use by the provider (usually a static or global). +- Initialize with TRACEPOINT_PROVIDER_STATE_INIT before use. +- Do not directly modify any fields while in use. +- Use with the tracepoint_close_provider, tracepoint_open_provider, or + tracepoint_connect APIs. +- Close the provider with tracepoint_close_provider before deallocating it. +- Normally you'll want to make sure no tracepoints are connected to a provider + before deallocating it. However, it's ok to deallocate a *closed* provider + with connected tracepoints as long as you deallocate all connected + tracepoints at the same time (e.g. if the provider and tracepoints are static + or global). + +More precise description of usage requirements: + +- Caller should make no assumptions about the semantics of any of the fields. + All fields should be considered opaque. +- The struct may become "valid" only when all of its fields have the values + specified in TRACEPOINT_PROVIDER_STATE_INIT. +- The struct becomes invalid when the caller modifies fields or deallocates the + memory of the struct. +- Only valid provider structs may be used in calls to tracepoint_close_provider, + tracepoint_open_provider, or tracepoint_connect. +- A provider struct that is used in a call to tracepoint_close_provider, + tracepoint_open_provider, or tracepoint_connect must remain valid until the + call returns. +- A provider struct that is open must remain valid until it is closed. +- Invalidating a provider also invalidates all tracepoints that are connected + to that provider. +*/ +typedef struct tracepoint_provider_state { + int data_file; // Opaque. Initial value must be -1. + unsigned ref_count; // Opaque. Initial value must be 0. + tracepoint_list_node tracepoint_list_head; // Opaque. Initial value must be {NULL,NULL}. +} tracepoint_provider_state; + +/* +Initializer for tracepoint_provider_state. +Usage: tracepoint_provider_state my_provider = TRACEPOINT_PROVIDER_STATE_INIT; +*/ +#define TRACEPOINT_PROVIDER_STATE_INIT { -1, 0, { (tracepoint_list_node*)0, (tracepoint_list_node*)0 } } + +/* +Partially-opaque tracepoint state, typically a non-const static/global for each +tracepoint. + +Basic usage: + +- Allocate memory for use by the tracepoint (usually a static or global). +- Initialize with TRACEPOINT_STATE_INIT before use. +- Do not directly modify any fields while in use. +- Use with the tracepoint_connect, tracepoint_write, or TRACEPOINT_ENABLED + APIs. +- Disconnect the tracepoint before deallocating it. +- It is ok to deallocate a tracepoint that is connected to a *closed* provider + if you also deallocate the connected provider and all of the other connected + tracepoints at the same time (e.g. if the provider and tracepoints are static + or global). + +More precise description of usage requirements: + +- Caller may assume that a call to tracepoint_write will be a no-op if + 0 == (status_mask & *status_byte). +- Caller may assume that provider_state is a pointer to the provider state of + the connected provider, or NULL if the tracepoint is disconnected. +- Caller should make no other assumptions about the semantics of any of the + fields. All fields should be considered opaque. +- The struct may become "valid" only when all of its fields have the values + specified in TRACEPOINT_STATE_INIT. +- The struct becomes invalid when the caller modifies fields or deallocates the + memory of the struct. +- Only valid tracepoint structs may be used in calls to tracepoint_connect, + tracepoint_write, or TRACEPOINT_ENABLED. +- A tracepoint struct that is used in a call to tracepoint_connect, + tracepoint_write, or TRACEPOINT_ENABLED must remain valid until the call + returns. +- A tracepoint struct that is connected to a provider may only be invalidated + by invalidating the provider. +*/ +typedef struct tracepoint_state { + unsigned status_word; // Initial value must be 0. + int write_index; // Opaque. Initial value must be -1. + tracepoint_provider_state const* provider_state; // Initial value must be NULL. + tracepoint_list_node tracepoint_list_link; // Opaque. Initial value must be {NULL,NULL}. +} tracepoint_state; + +/* +Initializer for tracepoint_state. +Usage: tracepoint_state my_tracepoint = TRACEPOINT_STATE_INIT; +*/ +#define TRACEPOINT_STATE_INIT { \ + 0, \ + -1, \ + (tracepoint_provider_state const*)0, \ + { (tracepoint_list_node*)0, (tracepoint_list_node*)0 } \ +} \ + +#endif // _included_tracepoint_state_h diff --git a/src/native/external/LinuxTracepoints/libtracepoint/include/tracepoint/tracepoint.h b/src/native/external/LinuxTracepoints/libtracepoint/include/tracepoint/tracepoint.h new file mode 100644 index 0000000000000..f0f4827efbc0e --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint/include/tracepoint/tracepoint.h @@ -0,0 +1,154 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +/* +Function prototypes for the tracepoint interface. +*/ + +#pragma once +#ifndef _included_tracepoint_h +#define _included_tracepoint_h 1 + +#include "tracepoint-state.h" +#include // struct iovec + +/* +Information about a tracepoint. +Used with tracepoint_open_provider_with_tracepoints. +*/ +typedef struct tracepoint_definition { + tracepoint_state* state; + char const* tp_name_args; +} tracepoint_definition; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + /* + Closes the specified tracepoint provider. Calling close on an + already-closed provider is a safe no-op. + + Disconnects any tracepoints that are connected to this provider and resets + them to TRACEPOINT_STATE_INIT. + */ + void tracepoint_close_provider( + tracepoint_provider_state* provider_state); + + /* + Opens the specified provider. Returns 0 for success, errno for failure. + + On failure, the provider remains closed. + + On success, disconnects any tracepoints that are already connected to this + provider and resets them to TRACEPOINT_STATE_INIT (this behavior is + necessary for init-on-first-use scenarios). + + PRECONDITION: + - It is an error to call tracepoint_open_provider(provider_state) if that + provider_state is already open. + - It is an error to call tracepoint_open_provider(provider_state) while + tracepoint_open_provider or tracepoint_close_provider for that + provider_state is running on another thread. + */ + int + tracepoint_open_provider( + tracepoint_provider_state* provider_state); + + /* + Opens the specified provider and connects the specified tracepoints. + + On failure, the provider remains closed. + + On success, disconnects any tracepoints that are already connected to this + provider and resets them to TRACEPOINT_STATE_INIT (this behavior is + necessary for init-on-first-use scenarios), then attempts to connect all + tracepoints in the tp_definition list (ignoring errors). + + This function is intended for use when the caller is using + __attribute__(section) to generate the tracepoint definition list. It sorts + and de-duplicates the list (moving NULLs to the end), then connects + tracepoints starting at tp_state_start and stopping at tp_state_stop or + NULL, whichever comes first. + + PRECONDITION: + - It is an error to call tracepoint_open_provider(provider_state) if that + provider_state is already open. + - It is an error to call tracepoint_open_provider(provider_state) while + tracepoint_open_provider or tracepoint_close_provider for that + provider_state is running on another thread. + */ + int + tracepoint_open_provider_with_tracepoints( + tracepoint_provider_state* provider_state, + tracepoint_definition const** tp_definition_start, + tracepoint_definition const** tp_definition_stop); + + /* + Connects the tracepoint to the specified provider. + + Returns 0 for success or if provider is closed, errno if an error was + reported during tracepoint registration. Tracepoint's connection will be + updated in either case. + + Tracepoint will remain connected to the provider until the provider is next + opened or closed or until another call to tracepoint_connect. + + - tp_state is the tracepoint to be connected. It is ok to specify a + tracepoint that is already connected to a provider, in which case the + tracepoint will be disconnected from the old provider (if any) and + connected to the new one. + + - provider_state is the new provider to connect to the tracepoint. It is ok + to specify NULL - the tracepoint will be disconnected and reset. It is ok + to specify a closed provider - the tracepoint will be connected to the + closed provider until it opens, at which point the tracepoint will be + disconnected and reset (this behavior is necessary for init-on-first-use + scenarios). + + - tp_name_args is a nul-terminated string with the tracepoint name, a + space, and the tracepoint arg spec string e.g. + "MyTracepoint u16 MyField1; u8 MyField2". + */ + int + tracepoint_connect( + tracepoint_state* tp_state, + tracepoint_provider_state* provider_state, + char const* tp_name_args); + + /* + Writes the specified tracepoint. + + Returns: + - 0 for success. + - EBADF if the tracepoint is disconnected or if nobody is listening for + this tracepoint. + - Other errno value for failure. + + Calling tracepoint_write on a disconnected tracepoint is a safe no-op. + Calling tracepoint_write on a tracepoint that is connected to a closed + provider is a safe no-op. + + For optimal performance, only call this function if + TRACEPOINT_ENABLED(tp_state) returns non-zero. + + data_vecs is an array of iovec structures that define the data payload. + data_vecs[0] is reserved for the use of tracepoint_write and must be + initialized to { NULL, 0 } before calling tracepoint_write. The + implementation will overwrite the contents of data_vecs[0]. + + PRECONDITION: + - data_count >= 1. + - data_vecs[0].iov_len == 0. + */ + int + tracepoint_write( + tracepoint_state const* tp_state, + unsigned data_count, + struct iovec* data_vecs); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif // _included_tracepoint_h diff --git a/src/native/external/LinuxTracepoints/libtracepoint/samples/CMakeLists.txt b/src/native/external/LinuxTracepoints/libtracepoint/samples/CMakeLists.txt new file mode 100644 index 0000000000000..7971a4966584c --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint/samples/CMakeLists.txt @@ -0,0 +1,9 @@ +add_executable(tracepoint-sample + tracepoint-sample.c) +target_link_libraries(tracepoint-sample + PUBLIC tracepoint) + +add_executable(tpp-sample + tpp-sample.c) +target_link_libraries(tpp-sample + PUBLIC tracepoint) diff --git a/src/native/external/LinuxTracepoints/libtracepoint/samples/tpp-sample.c b/src/native/external/LinuxTracepoints/libtracepoint/samples/tpp-sample.c new file mode 100644 index 0000000000000..4c27ddd4cc6eb --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint/samples/tpp-sample.c @@ -0,0 +1,66 @@ +#include + +/* +Demonstrates basic usage of the high-level tracepoint-provider.h API. + +The tracepoint-provider.h API is a developer-friendly wrapper for the +functionality provided by . +*/ + +// A provider is a collection of tracepoints that will be registered and +// unregistered as a group. +TPP_DEFINE_PROVIDER(MyProvider); + +// This defines a my_tracepoint1_func(...) function that generates a +// "my_tracepoint1" tracepoint with 3 fields. It also generates a +// my_tracepoint1_func_enabled() function that efficiently determines whether +// the tracepoint is registered and enabled. +TPP_FUNCTION(MyProvider, "my_tracepoint1", my_tracepoint1_func, + TPP_UINT8("field1", int_param), + TPP_STRING("field2", str_param), + TPP_CHAR_ARRAY("field3", 5, five_chars)); + +// Defines my_tracepoint2_func() and my_tracepoint2_func_enabled(). +TPP_FUNCTION(MyProvider, "my_tracepoint2", my_tracepoint2_func); + +static uint8_t get_field1_value(void) +{ + return 1; +} + +int main() +{ + // All tracepoints associated with MyProvider will be inactive until you + // register MyProvider. + TPP_REGISTER_PROVIDER(MyProvider); + + // If collecting the data for a tracepoint is expensive, + // check the enabled() function before collecting the data. + if (my_tracepoint1_func_enabled()) + { + int field1 = get_field1_value(); + char const* field2 = "value for field 2"; + char const* field3 = "5char"; + my_tracepoint1_func(field1, field2, field3); + } + + // While it's usually more efficient to check enabled() first, it's ok to + // call the function without checking enabled(). + my_tracepoint2_func(); + + // If you are only generating a tracepoint from one code path, use + // TPP_WRITE instead of TPP_FUNCTION. TPP_WRITE generates the tracepoint + // inline - no need for a separate function. The value expressions are only + // evaluated if the specified tracepoint is enabled, e.g. this only calls + // get_field1_value() if the tracepoint is registered and enabled. + TPP_WRITE(MyProvider, "my_tracepoint3", + TPP_UINT8("field1", get_field1_value()), + TPP_STRING("field2", "value for field 2"), + TPP_CHAR_ARRAY("field3", 5, "5char")); + + TPP_WRITE(MyProvider, "my_tracepoint4"); + + // Unregister is especially important for shared objects that can unload. + TPP_UNREGISTER_PROVIDER(MyProvider); + return 0; +} diff --git a/src/native/external/LinuxTracepoints/libtracepoint/samples/tracepoint-sample.c b/src/native/external/LinuxTracepoints/libtracepoint/samples/tracepoint-sample.c new file mode 100644 index 0000000000000..fb5afcacaaddd --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint/samples/tracepoint-sample.c @@ -0,0 +1,151 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +/* +Demonstrates basic usage of the low-level tracepoint.h interface. + +The tracepoint.h interface is not intended to be used directly. Developers +that want to add tracepoints to their code will likely prefer the high-level +API provided by . +*/ + +#include +#include + +// A tracepoint_provider_state represents a connection to the tracing system. It +// is usually a global so that all code in the component can share the +// connection. +tracepoint_provider_state provider = TRACEPOINT_PROVIDER_STATE_INIT; + +int main() +{ + int err; + + // A tracepoint_state represents a named user_events tracepoint. + tracepoint_state unopened_tracepoint = TRACEPOINT_STATE_INIT; + + // The tracepoint is inert before it is opened. + err = TRACEPOINT_ENABLED(&unopened_tracepoint); + printf("TRACEPOINT_ENABLED with unopened tracepoint: %d\n", err); // Expect 0. + err = tracepoint_write(&unopened_tracepoint, 1, &(struct iovec){}); // No-op. + printf("tracepoint_write with unopened tracepoint: %d\n", err); // Expect EBADF. + + // The provider is inert before it is opened. + tracepoint_close_provider(&provider); // No-op. + err = tracepoint_connect(&unopened_tracepoint, &provider, "TracepointName"); // No-op. + printf("tracepoint_connect with unopened provider: %d\n", err); // Expect 0. + + // Provider should be opened at component initialization. + // Since tracing is not usually the core functionality of an application, and + // since tracing with an unopened provider is a safe no-op, the error code + // returned by tracepoint_open_provider will usually be ignored in retail + // code. It is used mostly for debugging. + err = tracepoint_open_provider(&provider); + printf("tracepoint_open_provider: %d\n", err); + + // A tracepoint can be connected at any time but is typically connected at + // component initialization. + // Tracepoint has a name and a list of fieldname-fieldtype pairs. + tracepoint_state simple_tracepoint = TRACEPOINT_STATE_INIT; + err = tracepoint_connect(&simple_tracepoint, &provider, "simple_tracepoint u32 field1"); + printf("tracepoint_connect(simple_tracepoint): %d\n", err); + + tracepoint_state empty_tracepoint = TRACEPOINT_STATE_INIT; + err = tracepoint_connect(&empty_tracepoint, &provider, "empty_tracepoint"); + printf("tracepoint_connect(empty_tracepoint): %d\n", err); + + tracepoint_state tp_data_loc = TRACEPOINT_STATE_INIT; + err = tracepoint_connect(&tp_data_loc, &provider, "tp_data_loc u32 field1; __data_loc char[] field2; u32 field3;"); + printf("tracepoint_connect(tp_data_loc): %d\n", err); + + tracepoint_state tp_rel_loc = TRACEPOINT_STATE_INIT; + err = tracepoint_connect(&tp_rel_loc, &provider, "tp_rel_loc u32 field1; __rel_loc char[] field2; u32 field3;"); + printf("tracepoint_connect(tp_rel_loc): %d\n", err); + + printf("\n"); + for (int iteration = 1;; iteration += 1) + { + printf("Writing tracepoints:\n"); + + // Nothing bad happens if you call tracepoint_write when the tracepoint + // is not enabled. We check TRACEPOINT_ENABLED as an optimization. + printf("TRACEPOINT_ENABLED(&simple_tracepoint): %d\n", + TRACEPOINT_ENABLED(&simple_tracepoint)); + if (TRACEPOINT_ENABLED(&simple_tracepoint)) + { + struct iovec data_vecs[] = { + {}, // write_index will go here + { &iteration, sizeof(iteration)} // u32 field1 + }; + err = tracepoint_write(&simple_tracepoint, 2, data_vecs); + printf("tracepoint_write(simple_tracepoint): %d\n", err); + } + + printf("TRACEPOINT_ENABLED(&empty_tracepoint): %d\n", + TRACEPOINT_ENABLED(&empty_tracepoint)); + if (TRACEPOINT_ENABLED(&empty_tracepoint)) + { + struct iovec data_vecs[] = { + {}, // write_index will go here + }; + err = tracepoint_write(&empty_tracepoint, 1, data_vecs); + printf("tracepoint_write(empty_tracepoint): %d\n", err); + } + + printf("TRACEPOINT_ENABLED(&tp_data_loc): %d\n", + TRACEPOINT_ENABLED(&tp_data_loc)); + if (TRACEPOINT_ENABLED(&tp_data_loc)) + { + unsigned field1 = iteration + 100; + unsigned field3 = iteration + 300; + unsigned loc = 0xb0014; // Length 0x000b, abs_offset 0x0014. + static char loc_data[] = "field2-str"; + struct iovec data_vecs[] = { + {}, // write_index will go here + { &field1, sizeof(field1)}, // 0x08: u32 field1 + { &loc, sizeof(loc)}, // 0x0C: __rel_loc char[] field2 + { &field3, sizeof(field3)}, // 0x10: u32 field3 + { loc_data, sizeof(loc_data)}, // 0x14: char[] str + }; + err = tracepoint_write(&tp_data_loc, sizeof(data_vecs) / sizeof(data_vecs[0]), data_vecs); + printf("tracepoint_write(tp_data_loc): %d\n", err); + } + + printf("TRACEPOINT_ENABLED(&tp_rel_loc): %d\n", + TRACEPOINT_ENABLED(&tp_rel_loc)); + if (TRACEPOINT_ENABLED(&tp_rel_loc)) + { + unsigned field1 = iteration + 100; + unsigned field3 = iteration + 300; + unsigned loc = 0xb0004; // Length 0x000b, rel_offset 0x0004. + static char loc_data[] = "field2-str"; + struct iovec data_vecs[] = { + {}, // write_index will go here + { &field1, sizeof(field1)}, // 0x08: u32 field1 + { &loc, sizeof(loc)}, // 0x0C: __rel_loc char[] field2 + { &field3, sizeof(field3)}, // 0x10: u32 field3 + { loc_data, sizeof(loc_data)}, // 0x14: char[] str + }; + err = tracepoint_write(&tp_rel_loc, sizeof(data_vecs) / sizeof(data_vecs[0]), data_vecs); + printf("tracepoint_write(tp_rel_loc): %d\n", err); + } + + printf("Press enter to iterate, x + enter to exit...\n"); + char ch = (char)getchar(); + if (ch == 'x' || ch == 'X') + { + break; + } + + while (ch != '\n') + { + ch = (char)getchar(); + } + } + + // Provider should be closed at component cleanup (before any connected + // tracepoint_state variables go out of scope). + tracepoint_close_provider(&provider); + + return 0; +} diff --git a/src/native/external/LinuxTracepoints/libtracepoint/src/CMakeLists.txt b/src/native/external/LinuxTracepoints/libtracepoint/src/CMakeLists.txt new file mode 100644 index 0000000000000..f00852e23a124 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint/src/CMakeLists.txt @@ -0,0 +1,24 @@ +# tracepoint = libtracepoint, TRACEPOINT_HEADERS + +add_library(tracepoint + tracepoint.c) +target_link_libraries(tracepoint + PUBLIC tracepoint-headers) +install(TARGETS tracepoint + EXPORT tracepointTargets) +install(EXPORT tracepointTargets + FILE "tracepointTargets.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/tracepoint") +configure_package_config_file( + "${CMAKE_CURRENT_SOURCE_DIR}/tracepointConfig.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/tracepointConfig.cmake" + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/tracepoint" + NO_SET_AND_CHECK_MACRO + NO_CHECK_REQUIRED_COMPONENTS_MACRO) +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/tracepointConfigVersion.cmake" + COMPATIBILITY SameMinorVersion) +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/tracepointConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/tracepointConfigVersion.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/tracepoint") diff --git a/src/native/external/LinuxTracepoints/libtracepoint/src/tracepoint.c b/src/native/external/LinuxTracepoints/libtracepoint/src/tracepoint.c new file mode 100644 index 0000000000000..beb70c32768e2 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint/src/tracepoint.c @@ -0,0 +1,548 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +/* +An implementation of the tracepoint.h interface. +This implementation writes directly to user_events. +*/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifndef _tp_FUNC_ATTRIBUTES +#define _tp_FUNC_ATTRIBUTES +#endif // _tp_FUNC_ATTRIBUTES + +#define STRCMP_PREFIX(prefix, str) strncmp(prefix, str, sizeof(prefix) - 1) + +//#include + +/* + * Describes an event registration and stores the results of the registration. + * This structure is passed to the DIAG_IOCSREG ioctl, callers at a minimum + * must set the size and name_args before invocation. + */ +struct user_reg { + + /* 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 to use, if any */ + __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_unreg { + /* 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 '*' + +/* Request to register a user_event */ +#define DIAG_IOCSREG _IOWR(DIAG_IOC_MAGIC, 0, struct user_reg *) + +/* Request to delete a user_event */ +#define DIAG_IOCSDEL _IOW(DIAG_IOC_MAGIC, 1, char *) + +/* Requests to unregister a user_event */ +#define DIAG_IOCSUNREG _IOW(DIAG_IOC_MAGIC, 2, struct user_unreg*) + +/* +Guards all stores to any tracepoint_provider_state or tracepoint_state. + +Also guards loads from the tracepoint_provider_node Next/Prev fields. + +All other fields may be read outside the lock via atomic_load, so they must be +updated within the lock via atomic_store. +*/ +static pthread_mutex_t s_providers_mutex = PTHREAD_MUTEX_INITIALIZER; + +static int +get_failure_errno(void) +{ + int err = errno; + assert(err > 0); + if (err <= 0) + { + err = ENOENT; + } + + return err; +} + +static int +is_space_char(char ch) +{ + return ch == ' ' || ch == '\t'; +} + +static int +is_nonspace_char(char ch) +{ + return ch != '\0' && !is_space_char(ch); +} + +static int +user_events_data_update(int* staticFileOrError) +{ + int newFileOrError; + FILE* mountsFile; + + // Find the mount path for tracefs: + + /* + Goals: + - Minimal external dependencies. + - Efficient (minimal impact to startup time). + - Works correctly if the system is properly configured. + - System administrator can control how this ends up behaving (i.e. by + setting permissions on the user_events_data file). + - Can be made to work in a container. + */ + + // Start by probing the most likely absolute path. + // This improves performance and also makes it simpler to set up a container: + // The container creator will have to project the user_events_data file but + // won't need to make a dummy /proc/mounts file for us to parse. + newFileOrError = open("/sys/kernel/tracing/user_events_data", O_WRONLY); + if (0 <= newFileOrError) + { + // Success. + } + else if (NULL == (mountsFile = fopen("/proc/mounts", "r"))) + { + newFileOrError = -get_failure_errno(); + } + else + { + char path[274]; // 256 + sizeof("/user_events_data") + path[0] = 0; + + for (;;) + { + char line[4097]; + if (!fgets(line, sizeof(line), mountsFile)) + { + break; + } + + // line is "device_name mount_point file_system other_stuff..." + + size_t line_pos = 0; + + // device_name + while (is_nonspace_char(line[line_pos])) + { + line_pos += 1; + } + + // whitespace + while (is_space_char(line[line_pos])) + { + line_pos += 1; + } + + // mount_point + size_t const mount_begin = line_pos; + while (is_nonspace_char(line[line_pos])) + { + line_pos += 1; + } + + size_t const mount_end = line_pos; + + // whitespace + while (is_space_char(line[line_pos])) + { + line_pos += 1; + } + + // file_system + size_t const fs_begin = line_pos; + while (is_nonspace_char(line[line_pos])) + { + line_pos += 1; + } + + size_t const fs_end = line_pos; + + if (!is_space_char(line[line_pos])) + { + // Ignore line if no whitespace after file_system. + continue; + } + + char const* path_suffix; + size_t path_suffix_len; // includes NUL + char keepLooking; + + const char* const pchTracefs = "tracefs"; + size_t const cchTracefs = sizeof("tracefs") - 1; + const char* const pchDebugfs = "debugfs"; + size_t const cchDebugfs = sizeof("debugfs") - 1; + + size_t const fs_len = fs_end - fs_begin; + if (fs_len == cchTracefs && 0 == memcmp(line + fs_begin, pchTracefs, cchTracefs)) + { + // "tracefsMountPoint/user_events_data" + path_suffix = "/user_events_data"; + path_suffix_len = sizeof("/user_events_data"); // includes NUL + keepLooking = 0; // prefer "tracefs" over "debugfs". + } + else if (path[0] == 0 && + fs_len == cchDebugfs && 0 == memcmp(line + fs_begin, pchDebugfs, cchDebugfs)) + { + // "debugfsMountPoint/tracing/user_events_data" + path_suffix = "/tracing/user_events_data"; + path_suffix_len = sizeof("/tracing/user_events_data"); // includes NUL + keepLooking = 1; // prefer "tracefs" over "debugfs". + } + else + { + continue; + } + + size_t const mount_len = mount_end - mount_begin; + size_t const path_len = mount_len + path_suffix_len; // includes NUL + if (path_len > sizeof(path)) + { + continue; + } + + // path = mountpoint + suffix + memcpy(path, line + mount_begin, mount_len); + memcpy(path + mount_len, path_suffix, path_suffix_len); // includes NUL + + if (!keepLooking) + { + break; + } + } + + fclose(mountsFile); + + if (path[0] == 0) + { + // No "tracefs" or "debugfs" mount point found. + newFileOrError = -ENOTSUP; + } + else + { + // path is now something like "/sys/kernel/tracing/user_events_data\0" or + // "/sys/kernel/debug/tracing/user_events_data\0". + newFileOrError = open(path, O_WRONLY); + if (0 > newFileOrError) + { + newFileOrError = -get_failure_errno(); + } + } + } + + int oldFileOrError = -EAGAIN; + for (;;) + { + if (__atomic_compare_exchange_n( + staticFileOrError, + &oldFileOrError, + newFileOrError, + 0, + __ATOMIC_RELAXED, + __ATOMIC_RELAXED)) + { + // The cmpxchg set *staticFileOrError = newFileOrError. + return newFileOrError; + } + + // The cmpxchg set oldFileOrError = *staticFileOrError. + + if (oldFileOrError >= 0 || newFileOrError < 0) + { + // Prefer the existing contents of staticFileOrError. + if (newFileOrError >= 0) + { + close(newFileOrError); + } + + return oldFileOrError; + } + } +} + +// On success, returns a non-negative file descriptor. +// On failure, returns -errno. +static int +user_events_data_get() +{ + static int staticFileOrError = -EAGAIN; // Intentionally leaked. + int fileOrError = __atomic_load_n(&staticFileOrError, __ATOMIC_RELAXED); + return fileOrError != -EAGAIN + ? fileOrError + : user_events_data_update(&staticFileOrError); +} + +static void +event_unregister(tracepoint_state* tp_state) +{ + if (tp_state->write_index >= 0) + { + struct user_unreg unreg = { 0 }; + unreg.size = sizeof(struct user_unreg); + unreg.disable_bit = 0; + unreg.disable_addr = (uintptr_t)&tp_state->status_word; + ioctl(tp_state->provider_state->data_file, DIAG_IOCSUNREG, &unreg); + ((tracepoint_provider_state*)tp_state->provider_state)->ref_count -= 1; // For debugging purposes. + } +} + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + void + tracepoint_close_provider( + tracepoint_provider_state* provider_state) _tp_FUNC_ATTRIBUTES; + void + tracepoint_close_provider( + tracepoint_provider_state* provider_state) + { + pthread_mutex_lock(&s_providers_mutex); + + if (provider_state->data_file != -1) + { + assert(provider_state->data_file > -1); + + // Need to unregister events when we're done. + tracepoint_list_node* node = provider_state->tracepoint_list_head.next; + if (node != NULL) + { + assert(node->prev == &provider_state->tracepoint_list_head); + while (node != &provider_state->tracepoint_list_head) + { + tracepoint_state* tp_state = (tracepoint_state*)((char*)node - offsetof(tracepoint_state, tracepoint_list_link)); + node = node->next; + assert(node->prev == &tp_state->tracepoint_list_link); + + assert(provider_state == tp_state->provider_state); + event_unregister(tp_state); + } + } + } + + assert(provider_state->ref_count == 0); // register count == unregister count? + tracepoint_close_provider_impl(provider_state); + + pthread_mutex_unlock(&s_providers_mutex); + } + + int + tracepoint_open_provider( + tracepoint_provider_state* provider_state) _tp_FUNC_ATTRIBUTES; + int + tracepoint_open_provider( + tracepoint_provider_state* provider_state) + { + int const fileOrError = user_events_data_get(); + int const err = fileOrError >= 0 ? 0 : -fileOrError; + + pthread_mutex_lock(&s_providers_mutex); + + if (provider_state->data_file != -1) + { + assert(provider_state->data_file == -1); // PRECONDITION + abort(); // PRECONDITION + } + + if (err == 0) + { + /* + This will clear any events that were already "connected". + - If the higher layer connects events after open, ensure a blank slate. + - If the higher layer connects events on-demand, this will reset them so they + can reconnect now that we're ready for them. + */ + tracepoint_open_provider_impl(provider_state, fileOrError); + } + + assert(provider_state->ref_count == 0); + pthread_mutex_unlock(&s_providers_mutex); + + 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) _tp_FUNC_ATTRIBUTES; + 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_connect2( + tracepoint_state* tp_state, + tracepoint_provider_state* provider_state, + char const* tp_name_args, + unsigned flags) _tp_FUNC_ATTRIBUTES; + int + tracepoint_connect2( + tracepoint_state* tp_state, + tracepoint_provider_state* provider_state, + char const* tp_name_args, + unsigned flags) + { + int err; + int write_index = -1; + + pthread_mutex_lock(&s_providers_mutex); + + event_unregister(tp_state); + + if (NULL == provider_state || + -1 == provider_state->data_file) + { + err = 0; + } + else + { + struct user_reg reg = { 0 }; + reg.size = sizeof(reg); + reg.enable_bit = 0; + reg.enable_size = sizeof(tp_state->status_word); + reg.flags = (__u16)flags; + reg.enable_addr = (uintptr_t)&tp_state->status_word; + reg.name_args = (uintptr_t)tp_name_args; + + if (0 > ioctl(provider_state->data_file, DIAG_IOCSREG, ®)) + { + err = errno; + } + else + { + assert(reg.write_index <= 0x7fffffff); + provider_state->ref_count += 1; + write_index = (int)reg.write_index; + err = 0; + } + } + + tracepoint_connect_impl(tp_state, provider_state, write_index); + + pthread_mutex_unlock(&s_providers_mutex); + return err; + } + + int + tracepoint_connect( + tracepoint_state* tp_state, + tracepoint_provider_state* provider_state, + char const* tp_name_args) _tp_FUNC_ATTRIBUTES; + int + tracepoint_connect( + tracepoint_state* tp_state, + tracepoint_provider_state* provider_state, + char const* tp_name_args) + { + return tracepoint_connect2(tp_state, provider_state, tp_name_args, 0); + } + + int + tracepoint_write( + tracepoint_state const* tp_state, + unsigned data_count, + struct iovec* data_vecs) _tp_FUNC_ATTRIBUTES; + int + tracepoint_write( + tracepoint_state const* tp_state, + unsigned data_count, + struct iovec* data_vecs) + { + assert((int)data_count >= 1); + assert(data_vecs[0].iov_len == 0); + + if (!TRACEPOINT_ENABLED(tp_state)) + { + return EBADF; + } + + tracepoint_provider_state const* provider_state = __atomic_load_n(&tp_state->provider_state, __ATOMIC_RELAXED); + if (provider_state == NULL) + { + return EBADF; + } + + // Workaround: Events don't show up correctly with 0 bytes of data. + // If event has 0 bytes of data, add a '\0' byte to avoid the problem. + struct { + int32_t write_index; + char workaround; + } data0 = { + __atomic_load_n(&tp_state->write_index, __ATOMIC_RELAXED), + 0 + }; + data_vecs[0].iov_base = &data0; + data_vecs[0].iov_len = sizeof(int32_t) + (data_count == 1); + + int data_file = __atomic_load_n(&provider_state->data_file, __ATOMIC_RELAXED); + int err = 0 > data_file || 0 <= writev(data_file, data_vecs, (int)data_count) + ? 0 + : errno; + return err; + } + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/src/native/external/LinuxTracepoints/libtracepoint/src/tracepointConfig.cmake.in b/src/native/external/LinuxTracepoints/libtracepoint/src/tracepointConfig.cmake.in new file mode 100644 index 0000000000000..54a1b77d853ac --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint/src/tracepointConfig.cmake.in @@ -0,0 +1,2 @@ +@PACKAGE_INIT@ +include("${CMAKE_CURRENT_LIST_DIR}/tracepointTargets.cmake") diff --git a/src/native/external/LinuxTracepoints/libtracepoint/tools/CMakeLists.txt b/src/native/external/LinuxTracepoints/libtracepoint/tools/CMakeLists.txt new file mode 100644 index 0000000000000..a824d9f3f2043 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint/tools/CMakeLists.txt @@ -0,0 +1,5 @@ +add_executable(tracepoint-register + tracepoint-register.cpp) +target_link_libraries(tracepoint-register + PUBLIC tracepoint) +install(TARGETS tracepoint-register) diff --git a/src/native/external/LinuxTracepoints/libtracepoint/tools/tracepoint-register.cpp b/src/native/external/LinuxTracepoints/libtracepoint/tools/tracepoint-register.cpp new file mode 100644 index 0000000000000..3f3f3d4c1801d --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint/tools/tracepoint-register.cpp @@ -0,0 +1,541 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace std::string_view_literals; + +// From eventheader/eventheader.h: +#define EVENTHEADER_COMMAND_TYPES "u8 eventheader_flags; u8 version; u16 id; u16 tag; u8 opcode; u8 level" + +static constexpr std::string_view EventHeaderCommandTypes = EVENTHEADER_COMMAND_TYPES; + +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 + 1 + EventHeaderCommandTypes.size(), // +1 for space. +}; + +// From tracepoint.c: +extern "C" int +tracepoint_connect2( + tracepoint_state* tp_state, + tracepoint_provider_state* provider_state, + char const* tp_name_args, + unsigned flags); + +// From uapi/linux/perf_event.h: +enum user_reg_flag { + USER_EVENT_REG_PERSIST = 1U << 0, +}; + +enum WaitSetting : unsigned char { + WaitUnspecified, + WaitNo, + WaitYes, +}; + +struct TracepointInfo +{ + std::string command; + tracepoint_state state; + + explicit + TracepointInfo(std::string&& _command) + : command(std::move(_command)) + , state(TRACEPOINT_STATE_INIT) + { + return; + } +}; + +struct Options +{ + bool verbose = false; +}; + +#define PROGRAM_NAME "tracepoint-register" +#define EXIT_SIGNALS SIGTERM, SIGINT +#define EXIT_SIGNALS_STR "SIGTERM, SIGINT" + +static char const* const UsageCommon = R"( +Usage: )" PROGRAM_NAME R"( [options...] UserEventDefinitions... +)"; + +// 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"( +Pre-registers user_events tracepoints so you can start recording (e.g. with +the Linux "perf" tool) before starting the program that generates the events. + +Requires write access to /sys/kernel/tracing/user_events_data. The -p option +requires the CAP_PERFMON capability. + +Options: + +-i, --input Read additional UserEventDefinitions from . Each + line in the file is treated as a UserEventDefinition. + Empty lines and lines starting with '#' are ignored. + +-p, --persist Use the USER_EVENT_REG_PERSIST flag when registering each + tracepoint so that the tracepoints remain available after + )" PROGRAM_NAME R"( exits (requires CAP_PERFMON). + +-w, --wait Do not exit until signalled ()" EXIT_SIGNALS_STR R"(). + Keeps tracepoints registered until exit. This is the + default if -p is not specified. + +-W, --nowait Exit immediately. This is the default if -p is specified. + +-v, --verbose Show diagnostic output. + +-h, --help Show this help message and exit. + +A UserEventDefinition must be formatted as follows (see +https://docs.kernel.org/trace/user_events.html#command-format for details): + + EventName[:Flags] Fields... + +Fields... is list of fields. Multiple fields are separated by "; ". If an +event has no fields, use ";" as the Fields... definition. Note that ";" is a +valid EventName character, so there must be whitespace between EventName and +the ";". + +At present, no flags are defined. + +UserEventDefinition examples: + +- MyEvent1 ; +- MyEvent2 u32 MyField1 +- MyEvent3:flag u32 MyField1; struct MyStruct2 MyField2 20 + +As a shortcut, an EventHeader tracepoint may be specified without specifying +the fields. A UserEventDefinition that contains no whitespace will be treated +as an EventHeader tracepoint and will be expanded to include the standard +EventHeader fields. + +EventHeader UserEventDefinition examples: + +- MyProvider_L2K1 +- MyProvider_L5K3ffGmygroup +- MyProvider_L5K3ffGmygroup:flag +)"; + +static bool +AsciiIsLowercaseHex(char ch) +{ + return + ('0' <= ch && ch <= '9') || + ('a' <= ch && ch <= 'f'); +} + +static bool +AsciiIsAlphanumeric(char ch) +{ + return + ('0' <= ch && ch <= '9') || + ('A' <= ch && ch <= 'Z') || + ('a' <= ch && ch <= 'z'); +} + +static bool +AsciiIsSpace(char ch) +{ + return ch == ' ' || ('\t' <= ch && ch <= '\r'); +} + +// fprintf(stderr, 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, 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::forward_list& tracepoints, std::string_view line) +{ + // Trim trailing whitespace. + size_t endPos = line.size(); + for (; endPos != 0; endPos -= 1) + { + auto const ch = line[endPos - 1]; + if (!AsciiIsSpace(ch)) + { + break; + } + } + + // Trim leading whitespace. + size_t startPos = 0; + for (; startPos != endPos; startPos += 1) + { + if (!AsciiIsSpace(line[startPos])) + { + break; + } + } + + auto const trimmedDef = line.substr(startPos, endPos - startPos); + if (trimmedDef.empty()) + { + return; + } + else if (trimmedDef[0] == '#') + { + return; + } + + std::string command; + + auto const firstInternalWhitespace = trimmedDef.find_first_of("\t\n\v\f\r "sv); + if (std::string_view::npos != firstInternalWhitespace) + { + // Traditional tracepoint definition. + // Trim trailing semicolons and whitespace, e.g. "EventName ;" or "EventName Field; ; ;". + // EventName may contain semicolons, so "EventName; ; ;" must trim to "EventName;". + size_t trimmedEnd; + for (trimmedEnd = trimmedDef.size(); trimmedEnd != firstInternalWhitespace; trimmedEnd -= 1) + { + auto const ch = trimmedDef[trimmedEnd - 1]; + if (ch != ';' && !AsciiIsSpace(ch)) + { + break; + } + } + + assert(trimmedEnd != 0); + command = trimmedDef.substr(0, trimmedEnd); + } + else + { + // EventHeader tracepoint definition. Needs validation and expansion. + + // name = trimmedDef up to last ':'. If no ':', name = trimmedDef. + auto const name = trimmedDef.substr(0, trimmedDef.rfind(':')); + + if (name.size() >= EVENTHEADER_NAME_MAX) + { + fprintf(stderr, "error: eventheader name \"%.*s\" is too long.\n", + (unsigned)name.size(), name.data()); + return; + } + + if (name.find(':') != std::string_view::npos) + { + fprintf(stderr, "error: eventheader name \"%.*s\" contains invalid char ':'.\n", + (unsigned)name.size(), name.data()); + return; + } + + auto pos = name.rfind('_'); + + if (pos == std::string_view::npos || + name.size() < pos + 3 || + name[pos + 1] != 'L' || + !AsciiIsLowercaseHex(name[pos + 2])) + { + fprintf(stderr, "error: eventheader name \"%.*s\" is missing the required \"_L\" suffix.\n", + (unsigned)name.size(), name.data()); + return; + } + + // Skip "_Lnnn" + pos += 3; + while (pos < name.size() && AsciiIsLowercaseHex(name[pos])) + { + pos += 1; + } + + if (name.size() < pos + 2 || + name[pos] != 'K' || + !AsciiIsLowercaseHex(name[pos + 1])) + { + fprintf(stderr, "error: eventheader name \"%.*s\" is missing the required \"K\" suffix.\n", + (unsigned)name.size(), name.data()); + return; + } + + // Skip "Knnn..." + pos += 2; + for (; pos < name.size(); pos += 1) + { + if (!AsciiIsAlphanumeric(name[pos])) + { + fprintf(stderr, "error: eventheader name \"%.*s\" contains non-alphanumeric characters in the \"_LK...\" suffix.\n", + (unsigned)name.size(), name.data()); + return; + } + } + + command.reserve(trimmedDef.size() + 1 + EventHeaderCommandTypes.size()); + command = trimmedDef; + command += ' '; + command += EventHeaderCommandTypes; + } + + PrintStderrIf(o.verbose, "verbose: add \"%s\"\n", + command.c_str()); + + tracepoints.emplace_front(std::move(command)); +} + +static bool +PushFrontDefsFromFile(Options const& o, std::forward_list& 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) + { + fprintf(stderr, "error: failed to open file \"%s\".\n", filename); + return false; + } + + std::string line; + + char buf[128]; + while (fgets(buf, sizeof(buf), file)) + { + line += buf; + if (line.back() == '\n') + { + PushFrontDef(o, tracepoints, line); + line.clear(); + } + } + + bool const ok = 0 == ferror(file); + fclose(file); + + if (!ok) + { + fprintf(stderr, "error: failed to read file \"%s\".\n", filename); + } + else + { + PushFrontDef(o, tracepoints, line); + } + + return ok; +} + +int +main(int argc, char* argv[]) +{ + int error; + tracepoint_provider_state providerState = TRACEPOINT_PROVIDER_STATE_INIT; + + try + { + std::forward_list tracepoints; + Options o; + auto waitSetting = WaitUnspecified; + bool persist = false; + bool showHelp = false; + bool usageError = false; + + for (int argi = 1; argi < argc; argi += 1) + { + char const* const arg = argv[argi]; + if (arg[0] != '-') + { + PushFrontDef(o, tracepoints, 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 'i': + argi += 1; + if (argi < argc) + { + PushFrontDefsFromFile(o, tracepoints, argv[argi]); + } + else + { + PrintStderr("error: missing filename for flag -i\n"); + usageError = true; + } + break; + case 'p': + persist = true; + break; + case 'w': + waitSetting = WaitYes; + break; + case 'W': + waitSetting = WaitNo; + 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, "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, "persist")) + { + persist = true; + } + else if (0 == strcmp(flag, "wait")) + { + waitSetting = WaitYes; + } + else if (0 == strcmp(flag, "nowait")) + { + waitSetting = WaitNo; + } + 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) + { + fputs(UsageCommon, stdout); + fputs(UsageLong, stdout); + error = EINVAL; + } + else if (usageError) + { + fputs(UsageCommon, stderr); + fputs(UsageShort, stderr); + error = EINVAL; + } + else if (tracepoints.empty()) + { + PrintStderr("error: no tracepoints specified, exiting.\n"); + error = EINVAL; + } + else + { + error = tracepoint_open_provider(&providerState); + if (0 != error) + { + PrintStderr("error: tracepoint_open_provider failed (%u).\n", + error); + } + else + { + tracepoints.reverse(); + for (auto& tracepoint : tracepoints) + { + unsigned const flags = persist ? USER_EVENT_REG_PERSIST : 0; + int connectResult = tracepoint_connect2(&tracepoint.state, &providerState, tracepoint.command.c_str(), flags); + if (connectResult != 0) + { + PrintStderr("warning: tracepoint_connect failed (%u) for \"%s\".\n", + connectResult, tracepoint.command.c_str()); + } + } + + if (waitSetting == WaitYes || + (waitSetting == WaitUnspecified && !persist)) + { + sigset_t exitSigSet; + sigemptyset(&exitSigSet); + static constexpr int ExitSigs[] = { EXIT_SIGNALS }; + for (auto exitSig : ExitSigs) + { + sigaddset(&exitSigSet, exitSig); + } + + PrintStderrIf(o.verbose, "verbose: waiting for { " EXIT_SIGNALS_STR " }.\n"); + + sigset_t oldSigSet; + if (sigprocmask(SIG_BLOCK, &exitSigSet, &oldSigSet)) + { + PrintStderr("error: sigprocmask returned %u\n", + errno); + } + else + { + int sig = 0; + sigwait(&exitSigSet, &sig); + sigprocmask(SIG_SETMASK, &oldSigSet, nullptr); + PrintStderrIf(o.verbose, "verbose: signal %u.\n", + sig); + } + } + } + + tracepoint_close_provider(&providerState); + } + } + catch (std::exception const& ex) + { + PrintStderr("fatal error: %s\n", ex.what()); + error = ENOMEM; + } + + return error; +} diff --git a/src/native/external/LinuxTracepoints/libtracepoint/utest/CMakeLists.txt b/src/native/external/LinuxTracepoints/libtracepoint/utest/CMakeLists.txt new file mode 100644 index 0000000000000..cffc998002131 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint/utest/CMakeLists.txt @@ -0,0 +1,10 @@ +add_executable(tracepoint-utest + tracepoint-utest.cpp) +target_link_libraries(tracepoint-utest + PUBLIC tracepoint) + +add_executable(tpp-utest + tpp-utest-c.c + tpp-utest-cpp.cpp) +target_link_libraries(tpp-utest + PUBLIC tracepoint) diff --git a/src/native/external/LinuxTracepoints/libtracepoint/utest/tpp-utest-c.c b/src/native/external/LinuxTracepoints/libtracepoint/utest/tpp-utest-c.c new file mode 100644 index 0000000000000..b0d300c446a34 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint/utest/tpp-utest-c.c @@ -0,0 +1,27 @@ +#define C_OR_CPP C +#include "tpp-utest.h" + +#include + +int TestC(void) +{ + int err = TPP_REGISTER_PROVIDER(TestProvider); + printf("TestProviderC register: %d\n", err); + + int ok = TestCommon(); + + TPP_UNREGISTER_PROVIDER(TestProvider); + return ok != 0 && err == 0; +} + +void PrintErr(char const* operation, int err) +{ + printf("%s: %d\n", operation, err); +} + +int main() +{ + TestC(); + TestCpp(); + return 0; +} diff --git a/src/native/external/LinuxTracepoints/libtracepoint/utest/tpp-utest-cpp.cpp b/src/native/external/LinuxTracepoints/libtracepoint/utest/tpp-utest-cpp.cpp new file mode 100644 index 0000000000000..4f129b1777ed7 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint/utest/tpp-utest-cpp.cpp @@ -0,0 +1,18 @@ +#define C_OR_CPP CPP +#include "tpp-utest.h" + +#include + +int TestCpp(void) +{ + int err = TPP_REGISTER_PROVIDER(TestProvider); + printf("TestProviderCpp register: %d\n", err); + + int ok = TestCommon(); + + TPP_UNREGISTER_PROVIDER(TestProvider); + return ok != 0 && err == 0; +} + +#include +static_assert(EBADF == 9, "EBADF != 9"); diff --git a/src/native/external/LinuxTracepoints/libtracepoint/utest/tpp-utest.h b/src/native/external/LinuxTracepoints/libtracepoint/utest/tpp-utest.h new file mode 100644 index 0000000000000..32d3c89a9cc83 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint/utest/tpp-utest.h @@ -0,0 +1,79 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include + +#define PASTE2(a, b) PASTE2_imp(a, b) +#define PASTE2_imp(a, b) a##b +#define STRINGIZE(x) STRINGIZE_imp(x) +#define STRINGIZE_imp(x) #x + +#define TestProvider PASTE2(TestProvider_, C_OR_CPP) +#define FUNC1 PASTE2(func1_, C_OR_CPP) +#define FUNC1_ENABLED PASTE2(FUNC1, _enabled) +#define FUNC2 PASTE2(func2_, C_OR_CPP) +#define FUNC2_ENABLED PASTE2(FUNC2, _enabled) +#define SUFFIX "_" STRINGIZE(C_OR_CPP) + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + void PrintErr(char const* operation, int err); + int TestC(void); + int TestCpp(void); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +TPP_DECLARE_PROVIDER(TestProviderC); +TPP_DECLARE_PROVIDER(TestProviderCpp); + +TPP_FUNCTION(TestProvider, "func1" SUFFIX, FUNC1); + +TPP_FUNCTION(TestProvider, "func2" SUFFIX, FUNC2, + TPP_INT32("data0", data0), + TPP_CUSTOM_REL_LOC("u32[] data1", int, data1_len, data1), + TPP_CUSTOM_REL_LOC("unsigned[] data2", int, data2_len, data2), + TPP_CUSTOM_REL_LOC("u32[] data3", int, data3_len, data3), + TPP_STRING("data4", data4), + TPP_CHAR_ARRAY("data5", 7, data5), + TPP_STRUCT_PTR("data6", "MY_STRUCT", 8, data6)); + +static int TestCommon(void) +{ + int ok = 1; + int err; + + int values[] = { 0x31323334, 0x35363738 }; + + err = TPP_WRITE(TestProvider, "write1" SUFFIX); + PrintErr("write1", err); + + err = TPP_WRITE(TestProvider, "write2" SUFFIX, + TPP_INT32("data0", values[0]), + TPP_CUSTOM_REL_LOC("u32[] data1", int, 0, values), + TPP_CUSTOM_REL_LOC("unsigned[] data2", int, sizeof(values[0]), values), + TPP_CUSTOM_REL_LOC("u32[] data3", int, sizeof(values), values), + TPP_STRING("data4", "ABC123"), + TPP_CHAR_ARRAY("data5", 7, "abc1234"), + TPP_STRUCT_PTR("data6", "MY_STRUCT", 8, &values[0])); + PrintErr("write2", err); + + if (FUNC1_ENABLED()) + { + err = FUNC1(); + PrintErr("func1", err); + } + + if (FUNC2_ENABLED()) + { + err = FUNC2(values[0], 0, values, sizeof(values[0]), values, sizeof(values), values, "ABC123", "abc1234", &values[0]); + PrintErr("func2", err); + } + + return ok; +} + +TPP_DEFINE_PROVIDER(TestProvider); diff --git a/src/native/external/LinuxTracepoints/libtracepoint/utest/tracepoint-utest.cpp b/src/native/external/LinuxTracepoints/libtracepoint/utest/tracepoint-utest.cpp new file mode 100644 index 0000000000000..af66e08e0d189 --- /dev/null +++ b/src/native/external/LinuxTracepoints/libtracepoint/utest/tracepoint-utest.cpp @@ -0,0 +1,252 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +/* +Tests that any implementation of the tracepoint interface should pass. +Does not check whether the events ever get enabled or go anywhere. +*/ + +#include +#include +#include + +static bool s_any_errors = false; + +static void +check_errno(unsigned line, int err, char const* op) +{ + if (err != 0) + { + s_any_errors = true; + fprintf(stderr, "tracepoint-utest.cpp(%u) : warning : errno %d from %s\n", + line, err, op); + } +} + +#define CHECK(op) check_errno(__LINE__, (op), #op) + +static void +verify_cond(unsigned line, bool condition, char const* format, ...) +{ + if (!condition) + { + s_any_errors = true; + + va_list args; + va_start(args, format); + fprintf(stderr, "tracepoint-utest.cpp(%u) : error : ", line); + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + va_end(args); + } +} + +static void +verify_provider(unsigned line, tracepoint_provider_state const& p, bool providerIsOpen) +{ + if (!providerIsOpen) + { + verify_cond(line, -1 == p.data_file, + "Closed provider data_file: expected -1, actual %d", p.data_file); + } +} + +static void +verify_tp_disconnected(unsigned line, tracepoint_state const& e) +{ + iovec emptyVec = {}; + tracepoint_write(&e, 1, &emptyVec); + + verify_cond(line, 0 == e.status_word, + "Disconnected event status_word: expected 0, actual %u", e.status_word); + verify_cond(line, -1 == e.write_index, + "Disconnected event write_index: expected -1, actual %d", e.write_index); + verify_cond(line, nullptr == e.provider_state, + "Disconnected event provider_state: expected NULL, actual %p", e.provider_state); + verify_cond(line, nullptr == e.tracepoint_list_link.next, + "Disconnected event next: expected NULL, actual %p", e.tracepoint_list_link.next); + verify_cond(line, nullptr == e.tracepoint_list_link.prev, + "Disconnected event prev: expected NULL, actual %p", e.tracepoint_list_link.prev); +} + +static void +verify_tp_closed(unsigned line, tracepoint_state const& e, tracepoint_provider_state const& p) +{ + iovec emptyVec = {}; + tracepoint_write(&e, 1, &emptyVec); + + verify_cond(line, &p == e.provider_state, + "Closed event provider_state: expected %p, actual %p", &p, e.provider_state); + auto enabled = TRACEPOINT_ENABLED(&e); + verify_cond(line, 0 == enabled, + "Closed event TRACEPOINT_ENABLED: expected 0, actual %u", enabled); +} + +static void +verify_tp_open(unsigned line, tracepoint_state const& e, tracepoint_provider_state const& p) +{ + iovec emptyVec = {}; + tracepoint_write(&e, 1, &emptyVec); + + verify_cond(line, &p == e.provider_state, + "Open event provider_state: expected %p, actual %p", &p, e.provider_state); + (void)TRACEPOINT_ENABLED(&e); // Verify dereferencable. +} + +static void +connect_and_verify(unsigned line, tracepoint_provider_state& p, bool providerIsOpen) +{ + tracepoint_state e0 = TRACEPOINT_STATE_INIT; + tracepoint_state e1 = TRACEPOINT_STATE_INIT; + + verify_provider(line, p, providerIsOpen); + verify_tp_disconnected(__LINE__, e0); + verify_tp_disconnected(__LINE__, e1); + + CHECK(tracepoint_connect(&e0, &p, "e0 ")); + CHECK(tracepoint_connect(&e1, &p, "e1 ")); + + verify_provider(line, p, providerIsOpen); + if (!providerIsOpen) + { + verify_tp_closed(line, e0, p); + verify_tp_closed(line, e1, p); + } + else + { + verify_tp_open(line, e0, p); + verify_tp_open(line, e1, p); + } + + tracepoint_close_provider(&p); + + verify_provider(line, p, false); + verify_tp_disconnected(line, e0); + verify_tp_disconnected(line, e1); + + CHECK(tracepoint_connect(&e0, nullptr, "e0 ")); + CHECK(tracepoint_connect(&e1, nullptr, "e1 ")); + + verify_provider(line, p, false); + verify_tp_disconnected(line, e0); + verify_tp_disconnected(line, e1); + + CHECK(tracepoint_connect(&e0, &p, "e0 ")); + CHECK(tracepoint_connect(&e1, &p, "e1 ")); + + verify_provider(line, p, false); + verify_tp_closed(line, e0, p); + verify_tp_closed(line, e1, p); + + CHECK(tracepoint_open_provider(&p)); + + verify_provider(line, p, true); + verify_tp_disconnected(line, e0); + verify_tp_disconnected(line, e1); + + CHECK(tracepoint_connect(&e0, &p, "e0 ")); + CHECK(tracepoint_connect(&e1, &p, "e1 ")); + + verify_provider(line, p, true); + verify_tp_open(line, e0, p); + verify_tp_open(line, e1, p); + + tracepoint_close_provider(&p); + + verify_provider(line, p, false); + verify_tp_disconnected(line, e0); + verify_tp_disconnected(line, e1); +} + +int main() +{ + tracepoint_provider_state p0 = TRACEPOINT_PROVIDER_STATE_INIT; + tracepoint_provider_state p1 = TRACEPOINT_PROVIDER_STATE_INIT; + tracepoint_provider_state p2 = TRACEPOINT_PROVIDER_STATE_INIT; + + // Verify fresh provider (sanity check) + verify_provider(__LINE__, p0, false); + + // Verify fresh-closed provider + tracepoint_close_provider(&p1); + verify_provider(__LINE__, p1, false); + + // Verify open-closed provider + CHECK(tracepoint_open_provider(&p2)); + verify_provider(__LINE__, p2, true); + tracepoint_close_provider(&p2); + verify_provider(__LINE__, p2, false); + + // Connect events to fresh provider. + connect_and_verify(__LINE__, p0, false); + + // Connect events to closed provider. + connect_and_verify(__LINE__, p1, false); + + // Connect events to open provider. + CHECK(tracepoint_open_provider(&p2)); + connect_and_verify(__LINE__, p2, true); + + tracepoint_state e0 = TRACEPOINT_STATE_INIT; + tracepoint_state e1 = TRACEPOINT_STATE_INIT; + + // Disconnected --> Disconnected + CHECK(tracepoint_connect(&e0, nullptr, "e0 ")); + verify_tp_disconnected(__LINE__, e0); + + // Disconnected --> p0 + CHECK(tracepoint_connect(&e0, &p0, "e0 ")); + verify_provider(__LINE__, p0, false); + verify_tp_closed(__LINE__, e0, p0); + CHECK(tracepoint_connect(&e1, &p0, "e1 ")); + verify_provider(__LINE__, p0, false); + verify_tp_closed(__LINE__, e0, p0); + verify_tp_closed(__LINE__, e1, p0); + + // p0 --> p0 + CHECK(tracepoint_connect(&e0, &p0, "e0 ")); + verify_provider(__LINE__, p0, false); + verify_tp_closed(__LINE__, e0, p0); + verify_tp_closed(__LINE__, e1, p0); + CHECK(tracepoint_connect(&e1, &p0, "e1 ")); + verify_provider(__LINE__, p0, false); + verify_tp_closed(__LINE__, e0, p0); + verify_tp_closed(__LINE__, e1, p0); + + // p0 --> p1 + CHECK(tracepoint_connect(&e0, &p1, "e0 ")); + verify_provider(__LINE__, p0, false); + verify_provider(__LINE__, p1, false); + verify_tp_closed(__LINE__, e0, p1); + verify_tp_closed(__LINE__, e1, p0); + CHECK(tracepoint_connect(&e1, &p1, "e1 ")); + verify_provider(__LINE__, p0, false); + verify_provider(__LINE__, p1, false); + verify_tp_closed(__LINE__, e0, p1); + verify_tp_closed(__LINE__, e1, p1); + + // p1 --> p0 + CHECK(tracepoint_connect(&e0, &p0, "e0 ")); + verify_provider(__LINE__, p0, false); + verify_provider(__LINE__, p1, false); + verify_tp_closed(__LINE__, e0, p0); + verify_tp_closed(__LINE__, e1, p1); + CHECK(tracepoint_connect(&e1, &p0, "e1 ")); + verify_provider(__LINE__, p0, false); + verify_provider(__LINE__, p1, false); + verify_tp_closed(__LINE__, e0, p0); + verify_tp_closed(__LINE__, e1, p0); + + // p0 --> Disconnected + CHECK(tracepoint_connect(&e0, nullptr, "e0 ")); + verify_provider(__LINE__, p0, false); + verify_tp_disconnected(__LINE__, e0); + verify_tp_closed(__LINE__, e1, p0); + CHECK(tracepoint_connect(&e1, nullptr, "e1 ")); + verify_provider(__LINE__, p0, false); + verify_tp_disconnected(__LINE__, e0); + verify_tp_disconnected(__LINE__, e1); + + fprintf(stderr, "%s\n", s_any_errors ? "FAIL" : "OK"); + return s_any_errors; +} diff --git a/src/native/external/LinuxTracepoints/version.cmake b/src/native/external/LinuxTracepoints/version.cmake new file mode 100644 index 0000000000000..1ec5a1806490b --- /dev/null +++ b/src/native/external/LinuxTracepoints/version.cmake @@ -0,0 +1,8 @@ +include_guard() +if(NOT DEFINED LINUXTRACEPOINTS_VERSION) + set(LINUXTRACEPOINTS_VERSION 1.3.3) + set(EVENTHEADER_HEADERS_MINVER 1.3) # Referenced by libeventheader-decode-cpp + set(TRACEPOINT_MINVER 1.3) # Referenced by libeventheader-tracepoint + set(TRACEPOINT_DECODE_MINVER 1.3) # Referenced by libeventheader-decode-cpp, libtracepoint-control-cpp + set(TRACEPOINT_HEADERS_MINVER 1.3) # Referenced by libeventheader-tracepoint +endif() diff --git a/src/native/external/cgmanifest.json b/src/native/external/cgmanifest.json index 80c0886d4b25b..89142922111d1 100644 --- a/src/native/external/cgmanifest.json +++ b/src/native/external/cgmanifest.json @@ -70,6 +70,16 @@ } }, "DevelopmentDependency": false + }, + { + "Component": { + "Type": "git", + "Git": { + "RepositoryUrl": "https://github.com/microsoft/LinuxTracepoints", + "CommitHash": "a817b91dfb08b2929ec6d48a211644e3394bf1c7" + } + }, + "DevelopmentDependency": false } ] }