diff --git a/.editorconfig b/.editorconfig
index cdfa4e3246..3b4f1d86da 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,4 +1,7 @@
# To learn more about .editorconfig see https://aka.ms/editorconfigdocs
+
+root = true
+
###############################
# Core EditorConfig Options #
###############################
diff --git a/src/OpenTelemetry.Exporter.Geneva/External/.editorconfig b/src/OpenTelemetry.Exporter.Geneva/External/.editorconfig
new file mode 100644
index 0000000000..c895abfaeb
--- /dev/null
+++ b/src/OpenTelemetry.Exporter.Geneva/External/.editorconfig
@@ -0,0 +1,2 @@
+[*.cs]
+generated_code = true
diff --git a/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/.editorconfig b/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/.editorconfig
new file mode 100644
index 0000000000..e0875140a8
--- /dev/null
+++ b/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/.editorconfig
@@ -0,0 +1,6 @@
+[*.cs]
+# Use DefaultDllImportSearchPaths attribute for P/Invokes
+dotnet_diagnostic.CA5392.severity = none
+
+# The ref modifier for argument corresponding to in parameter is equivalent to in. Consider using in instead.
+dotnet_diagnostic.CS9191.severity = none
diff --git a/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Provider/DataSegment.cs b/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Provider/DataSegment.cs
new file mode 100644
index 0000000000..7ff5ce81a6
--- /dev/null
+++ b/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Provider/DataSegment.cs
@@ -0,0 +1,24 @@
+#nullable enable
+
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+namespace Microsoft.LinuxTracepoints.Provider;
+
+using System.Runtime.InteropServices;
+
+///
+/// struct iovec, for use with calls to writev.
+///
+[StructLayout(LayoutKind.Sequential)]
+internal unsafe struct DataSegment
+{
+ public void* PinnedBase;
+ public nuint Length;
+
+ public DataSegment(void* pinnedBase, nuint length)
+ {
+ this.PinnedBase = pinnedBase;
+ this.Length = length;
+ }
+}
diff --git a/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Provider/PerfTracepoint.cs b/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Provider/PerfTracepoint.cs
new file mode 100644
index 0000000000..c352eed671
--- /dev/null
+++ b/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Provider/PerfTracepoint.cs
@@ -0,0 +1,411 @@
+#nullable enable
+
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+namespace Microsoft.LinuxTracepoints.Provider;
+
+using System;
+
+///
+/// Flags used when registering a user_events tracepoint.
+/// From LINUX/include/uapi/linux/user_events.h enum user_reg_flag.
+///
+[Flags]
+internal enum PerfUserEventReg : UInt16
+{
+ ///
+ /// No flags set (default).
+ ///
+ None,
+
+ ///
+ /// USER_EVENT_REG_PERSIST:
+ /// Event will not delete upon last reference closing.
+ ///
+ Persist = 1 << 0,
+
+ ///
+ /// USER_EVENT_REG_MULTI_FORMAT:
+ /// Event will be allowed to have multiple formats.
+ ///
+ MultiFormat = 1 << 1,
+}
+
+///
+/// Direct access to a user_events tracepoint -- caller is responsible for the registration
+/// string and for packing/marshalling event data.
+///
+/// For more information, see user_events.
+///
+/// The tracepoint is registered by the constructor.
+/// You'll generally construct all of your application's Tracepoint objects at application
+/// start or component initialization.
+///
+/// You'll use the IsEnabled property to determine
+/// whether any sessions are collecting the tracecpoint, and you'll use the Write method
+/// to write events.
+///
+/// Normal usage:
+///
+/// Tracepoint tp = new PerfTracepoint("MyEventName int MyField1; int MyField2");
+///
+/// // To log an event where preparing the data is very simple:
+/// tp.Write(data...);
+///
+/// // To log an event where preparing the data is expensive:
+/// if (tp.IsEnabled) // Skip preparing data and calling Write if the tracepoint is not enabled.
+/// {
+/// var data = ...; // Prepare data that needs to be logged.
+/// tp.Write(data...);
+/// }
+///
+/// Note that tracepoint registration can fail, and Write operations can also fail.
+/// The RegisterResult property and the error code returned by the Write method are provided
+/// for debugging and diagnostics, but you'll usually ignore these in normal operation (most
+/// applications should continue to work even if tracing isn't working).
+///
+internal class PerfTracepoint : IDisposable
+{
+ private readonly TracepointHandle handle;
+
+ ///
+ /// As a performance optimization, avoid one level of indirection during calls to IsEnabled
+ /// by caching the enablement array. The array always has Length == 1. The contents of this
+ /// array should be considered read-only and MUST NOT be modified (the array data is updated
+ /// by the Linux kernel when the tracepoint is enabled or disabled).
+ ///
+ /// When handle.IsInvalid, one array is shared by all invalid handles and is a normal allocation.
+ /// When !handle.IsInvalid, the array is unique for for each handle and is a pinned allocation.
+ ///
+ private readonly Int32[] enablementArray;
+
+ ///
+ /// Given a user_events command string, attempts to register a user_events tracepoint.
+ ///
+ /// If registration succeeds, the new tracepoint will be valid and active:
+ /// IsEnabled is dynamic, RegisterResult == 0, Write sends data to the kernel.
+ ///
+ /// If registration fails, the new tracepoint will be invalid and inactive:
+ /// IsEnabled == false, RegisterResult != 0, Write does nothing and immediately returns EBADF.
+ ///
+ ///
+ /// user_events command string,
+ /// e.g. "MyEventName int arg1; u32 arg2". String should use only Latin-1 characters (each
+ /// char in the string should have value 255 or below).
+ ///
+ ///
+ /// user_reg flags,
+ /// e.g. USER_EVENT_REG_PERSIST or USER_EVENT_REG_MULTI_FORMAT.
+ ///
+ public PerfTracepoint(ReadOnlySpan nameArgs, PerfUserEventReg flags = 0)
+ {
+ var h = TracepointHandle.Register(nameArgs, flags);
+ this.handle = h;
+ this.enablementArray = h.DangerousGetEnablementArray();
+ }
+
+ ///
+ /// Given a NUL-terminated Latin1-encoded user_events command string, attempts to
+ /// register a user_events tracepoint.
+ ///
+ /// If registration succeeds, the new tracepoint will be valid and active:
+ /// IsEnabled is dynamic, RegisterResult == 0, Write sends data to the kernel.
+ ///
+ /// If registration fails, the new tracepoint will be invalid and inactive:
+ /// IsEnabled == false, RegisterResult != 0, Write does nothing and immediately returns EBADF.
+ ///
+ ///
+ /// user_events command string,
+ /// Latin-1 encoded, NUL-terminated, e.g. "MyEventName int arg1; u32 arg2\0".
+ ///
+ ///
+ /// user_reg flags,
+ /// e.g. USER_EVENT_REG_PERSIST or USER_EVENT_REG_MULTI_FORMAT.
+ ///
+ ///
+ /// nulTerminatedNameArgs does not contain any NUL termination (no 0 bytes).
+ ///
+ public PerfTracepoint(ReadOnlySpan nulTerminatedNameArgs, PerfUserEventReg flags = 0)
+ {
+ if (0 > nulTerminatedNameArgs.LastIndexOf((byte)0))
+ {
+ throw new ArgumentException(
+ nameof(nulTerminatedNameArgs) + " must be NUL-terminated, i.e. must contain (byte)0.",
+ nameof(nulTerminatedNameArgs));
+ }
+
+ var h = TracepointHandle.Register(nulTerminatedNameArgs, flags);
+ this.handle = h;
+ this.enablementArray = h.DangerousGetEnablementArray();
+ }
+
+ ///
+ /// Returns true if this tracepoint is registered and one or more tracepoint collection sessions
+ /// are collecting this tracepoint.
+ ///
+ /// Returns false if this tracepoint is unregistered or if there are no tracepoint collection
+ /// sessions that are collecting this tracepoint. The tracepoint will be unregistered if
+ /// registration failed or if the tracepoint has been disposed.
+ ///
+ /// Note that this property is provided to support performance optimization, but use of this
+ /// property is optional. It's ok to call Write even if IsEnabled returns false. If your
+ /// tracepoint is not being collected, the Write method will do nothing and will immediately
+ /// return EBADF. This property is provided so that you can efficiently skip preparing your data
+ /// and calling the Write method if your tracepoint is not being collected.
+ ///
+ public bool IsEnabled => this.enablementArray[0] != 0;
+
+ ///
+ /// If tracepoint registration succeeded, returns 0.
+ /// Otherwise, returns an errno indicating the error.
+ ///
+ /// This property is for diagnostic purposes and should usually be ignored for normal
+ /// operation -- most programs should continue to operate even if trace registration
+ /// fails.
+ ///
+ public int RegisterResult => this.handle.RegisterResult;
+
+ ///
+ /// Unregisters the tracepoint. After calling Dispose(), IsEnabled will return false and
+ /// Write will do nothing and immediately return EBADF.
+ ///
+ public void Dispose()
+ {
+ this.Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// If !IsEnabled, immediately returns EBADF.
+ /// Otherwise, writes an event with no data.
+ ///
+ ///
+ /// 0 if event was written, errno otherwise.
+ /// Typically returns EBADF if no data collection sessions are listening for the tracepoint.
+ /// The return value is for debugging/diagnostic purposes and is usually ignored in normal operation
+ /// since most programs should continue to function even when tracing is not configured.
+ ///
+ public int Write()
+ {
+ if (this.enablementArray[0] == 0)
+ {
+ return TracepointHandle.DisabledEventError;
+ }
+
+ return this.handle.Write(stackalloc DataSegment[] {
+ default, // segment[0] is used for headers.
+ });
+ }
+
+ ///
+ /// If !IsEnabled, immediately returns EBADF.
+ /// Otherwise, writes an event with 1 chunk of data.
+ ///
+ ///
+ /// 0 if event was written, errno otherwise.
+ /// Typically returns EBADF if no data collection sessions are listening for the tracepoint.
+ /// The return value is for debugging/diagnostic purposes and is usually ignored in normal operation
+ /// since most programs should continue to function even when tracing is not configured.
+ ///
+ public int Write(
+ ReadOnlySpan v1)
+ where T1 : unmanaged
+ {
+ if (this.enablementArray[0] == 0)
+ {
+ return TracepointHandle.DisabledEventError;
+ }
+
+ unsafe
+ {
+ fixed (void*
+ p1 = v1)
+ {
+ return this.handle.Write(stackalloc DataSegment[] {
+ default, // segment[0] is used for headers.
+ new DataSegment(p1, (uint)v1.Length * (uint)sizeof(T1)),
+ });
+ }
+ }
+ }
+
+ ///
+ /// If !IsEnabled, immediately returns EBADF.
+ /// Otherwise, writes an event with 2 chunks of data.
+ ///
+ ///
+ /// 0 if event was written, errno otherwise.
+ /// Typically returns EBADF if no data collection sessions are listening for the tracepoint.
+ /// The return value is for debugging/diagnostic purposes and is usually ignored in normal operation
+ /// since most programs should continue to function even when tracing is not configured.
+ ///
+ public int Write(
+ ReadOnlySpan v1,
+ ReadOnlySpan v2)
+ where T1 : unmanaged
+ where T2 : unmanaged
+ {
+ if (this.enablementArray[0] == 0)
+ {
+ return TracepointHandle.DisabledEventError;
+ }
+
+ unsafe
+ {
+ fixed (void*
+ p1 = v1,
+ p2 = v2)
+ {
+ return this.handle.Write(stackalloc DataSegment[] {
+ default, // segment[0] is used for headers.
+ new DataSegment(p1, (uint)v1.Length * (uint)sizeof(T1)),
+ new DataSegment(p2, (uint)v2.Length * (uint)sizeof(T2)),
+ });
+ }
+ }
+ }
+
+ ///
+ /// If !IsEnabled, immediately returns EBADF.
+ /// Otherwise, writes an event with 3 chunks of data.
+ ///
+ ///
+ /// 0 if event was written, errno otherwise.
+ /// Typically returns EBADF if no data collection sessions are listening for the tracepoint.
+ /// The return value is for debugging/diagnostic purposes and is usually ignored in normal operation
+ /// since most programs should continue to function even when tracing is not configured.
+ ///
+ public int Write(
+ ReadOnlySpan v1,
+ ReadOnlySpan v2,
+ ReadOnlySpan v3)
+ where T1 : unmanaged
+ where T2 : unmanaged
+ where T3 : unmanaged
+ {
+ if (this.enablementArray[0] == 0)
+ {
+ return TracepointHandle.DisabledEventError;
+ }
+
+ unsafe
+ {
+ fixed (void*
+ p1 = v1,
+ p2 = v2,
+ p3 = v3)
+ {
+ return this.handle.Write(stackalloc DataSegment[] {
+ default, // segment[0] is used for headers.
+ new DataSegment(p1, (uint)v1.Length * (uint)sizeof(T1)),
+ new DataSegment(p2, (uint)v2.Length * (uint)sizeof(T2)),
+ new DataSegment(p3, (uint)v3.Length * (uint)sizeof(T3)),
+ });
+ }
+ }
+ }
+
+ ///
+ /// If !IsEnabled, immediately returns EBADF.
+ /// Otherwise, writes an event with 4 chunks of data.
+ ///
+ ///
+ /// 0 if event was written, errno otherwise.
+ /// Typically returns EBADF if no data collection sessions are listening for the tracepoint.
+ /// The return value is for debugging/diagnostic purposes and is usually ignored in normal operation
+ /// since most programs should continue to function even when tracing is not configured.
+ ///
+ public int Write(
+ ReadOnlySpan v1,
+ ReadOnlySpan v2,
+ ReadOnlySpan v3,
+ ReadOnlySpan v4)
+ where T1 : unmanaged
+ where T2 : unmanaged
+ where T3 : unmanaged
+ where T4 : unmanaged
+ {
+ if (this.enablementArray[0] == 0)
+ {
+ return TracepointHandle.DisabledEventError;
+ }
+
+ unsafe
+ {
+ fixed (void*
+ p1 = v1,
+ p2 = v2,
+ p3 = v3,
+ p4 = v4)
+ {
+ return this.handle.Write(stackalloc DataSegment[] {
+ default, // segment[0] is used for headers.
+ new DataSegment(p1, (uint)v1.Length * (uint)sizeof(T1)),
+ new DataSegment(p2, (uint)v2.Length * (uint)sizeof(T2)),
+ new DataSegment(p3, (uint)v3.Length * (uint)sizeof(T3)),
+ new DataSegment(p4, (uint)v4.Length * (uint)sizeof(T4)),
+ });
+ }
+ }
+ }
+
+ ///
+ /// If !IsEnabled, immediately returns EBADF.
+ /// Otherwise, writes an event with 5 chunks of data.
+ ///
+ ///
+ /// 0 if event was written, errno otherwise.
+ /// Typically returns EBADF if no data collection sessions are listening for the tracepoint.
+ /// The return value is for debugging/diagnostic purposes and is usually ignored in normal operation
+ /// since most programs should continue to function even when tracing is not configured.
+ ///
+ public int Write(
+ ReadOnlySpan v1,
+ ReadOnlySpan v2,
+ ReadOnlySpan v3,
+ ReadOnlySpan v4,
+ ReadOnlySpan v5)
+ where T1 : unmanaged
+ where T2 : unmanaged
+ where T3 : unmanaged
+ where T4 : unmanaged
+ where T5 : unmanaged
+ {
+ if (this.enablementArray[0] == 0)
+ {
+ return TracepointHandle.DisabledEventError;
+ }
+
+ unsafe
+ {
+ fixed (void*
+ p1 = v1,
+ p2 = v2,
+ p3 = v3,
+ p4 = v4,
+ p5 = v5)
+ {
+ return this.handle.Write(stackalloc DataSegment[] {
+ default, // segment[0] is used for headers.
+ new DataSegment(p1, (uint)v1.Length * (uint)sizeof(T1)),
+ new DataSegment(p2, (uint)v2.Length * (uint)sizeof(T2)),
+ new DataSegment(p3, (uint)v3.Length * (uint)sizeof(T3)),
+ new DataSegment(p4, (uint)v4.Length * (uint)sizeof(T4)),
+ new DataSegment(p5, (uint)v5.Length * (uint)sizeof(T5)),
+ });
+ }
+ }
+ }
+
+ ///
+ /// If disposing is true, calls this.handle.Dispose().
+ ///
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ this.handle.Dispose();
+ }
+ }
+}
diff --git a/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Provider/RawFileHandle.cs b/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Provider/RawFileHandle.cs
new file mode 100644
index 0000000000..7d80faa023
--- /dev/null
+++ b/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Provider/RawFileHandle.cs
@@ -0,0 +1,165 @@
+#nullable enable
+
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+namespace Microsoft.LinuxTracepoints.Provider;
+
+using System;
+using System.Runtime.InteropServices;
+
+///
+/// Wraps a raw Posix file descriptor returned from "open".
+/// Non-negative handle is a valid descriptor.
+/// Negative handle is a negative errno with the result of the "open" operation.
+///
+internal sealed class RawFileHandle
+ : SafeHandle
+{
+ private const int UnknownErrno = int.MaxValue;
+
+ ///
+ /// Initializes a new handle that is invalid.
+ /// Do not use this constructor. To create a valid handle, call the static Open method.
+ ///
+ public RawFileHandle()
+ : base(new IntPtr(-UnknownErrno), true)
+ {
+ return;
+ }
+
+ ///
+ /// Returns true if handle is negative (stores a negative errno).
+ ///
+ public override bool IsInvalid => (nint)this.handle < 0;
+
+ ///
+ /// If open succeeded, returns 0. Otherwise returns the errno from open.
+ ///
+ public int OpenResult
+ {
+ get
+ {
+ var h = (nint)this.handle;
+ return h >= 0 ? 0 : -(int)h;
+ }
+ }
+
+ ///
+ /// Calls "open" with the given path name and with flags = O_WRONLY.
+ /// On success, returns a valid handle. On failure, returns an invalid handle
+ /// (check OpenResult for the errno).
+ ///
+ public static RawFileHandle OpenWRONLY(ReadOnlySpan nulTerminatedPathName)
+ {
+ var result = new RawFileHandle();
+
+ // Need a finally block to make sure the thread is not interrupted
+ // between the open and the handle assignment.
+ try
+ {
+ // Nothing.
+ }
+ finally
+ {
+ unsafe
+ {
+ fixed (byte* pathNamePtr = nulTerminatedPathName)
+ {
+ const int O_WRONLY = 0x0001;
+ result.handle = (nint)NativeMethods.open(pathNamePtr, O_WRONLY, 0);
+ }
+ }
+ }
+
+ if (0 > (nint)result.handle)
+ {
+ var errno = Marshal.GetLastWin32Error();
+ if (errno <= 0)
+ {
+ errno = UnknownErrno;
+ }
+
+ result.handle = new IntPtr(-errno);
+ }
+
+ return result;
+ }
+
+ ///
+ /// Calls "writev" with the given data.
+ /// On success, returns the number of bytes written.
+ /// On error, returns -1 (check Marshal.GetLastWin32Error() for the errno).
+ ///
+ public nint WriteV(ReadOnlySpan iovecs)
+ {
+ var needRelease = false;
+ try
+ {
+ this.DangerousAddRef(ref needRelease);
+ unsafe
+ {
+ fixed (DataSegment* iovecsPtr = iovecs)
+ {
+ return NativeMethods.writev((Int32)(nint)this.handle, iovecsPtr, iovecs.Length);
+ }
+ }
+ }
+ finally
+ {
+ if (needRelease)
+ {
+ this.DangerousRelease();
+ }
+ }
+ }
+
+ ///
+ /// Calls "ioctl" with the given request and data.
+ /// On error, returns -1 (check Marshal.GetLastWin32Error() for the errno).
+ ///
+ public int Ioctl(uint request, ref T data)
+ where T : unmanaged
+ {
+ var needRelease = false;
+ try
+ {
+ this.DangerousAddRef(ref needRelease);
+ unsafe
+ {
+ fixed (void* dataPtr = &data)
+ {
+ return NativeMethods.ioctl((Int32)(nint)this.handle, new UIntPtr(request), dataPtr);
+ }
+ }
+ }
+ finally
+ {
+ if (needRelease)
+ {
+ this.DangerousRelease();
+ }
+ }
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ var h = unchecked((Int32)(nint)this.handle);
+ return 0 <= NativeMethods.close(h);
+ }
+
+ private unsafe static class NativeMethods
+ {
+ [DllImport("libc", SetLastError = true)]
+ public static extern int close(Int32 fd);
+
+ [DllImport("libc", SetLastError = true)]
+ public static extern int open(byte* pathname, Int32 flags, Int32 mode);
+
+ [DllImport("libc", SetLastError = true)]
+ public static extern int ioctl(Int32 fd, UIntPtr request, void* data);
+
+ [DllImport("libc", SetLastError = true)]
+ public static extern IntPtr writev(Int32 fd, DataSegment* iovec, Int32 iovecCount);
+ }
+}
diff --git a/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Provider/TracepointHandle.cs b/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Provider/TracepointHandle.cs
new file mode 100644
index 0000000000..e50d6092c5
--- /dev/null
+++ b/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Provider/TracepointHandle.cs
@@ -0,0 +1,631 @@
+#nullable enable
+
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+namespace Microsoft.LinuxTracepoints.Provider;
+
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using Debug = System.Diagnostics.Debug;
+using Interlocked = System.Threading.Interlocked;
+
+///
+/// Low-level owner of a tracepoint registration.
+/// Handle is the tracepoint's write_index.
+/// Uses a pinned allocation for the tracepoint's is-enabled buffer.
+///
+internal sealed class TracepointHandle : SafeHandle
+{
+ ///
+ /// The error to return from Write to a disabled event = EBADF = 9.
+ ///
+ public const int DisabledEventError = 9;
+
+ ///
+ /// The error to return from Write of an event that is too big = E2BIG = 7.
+ ///
+ public const int EventTooBigError = 7;
+
+ private const byte EnableSize = sizeof(UInt32);
+
+ private const int EBADF = 9;
+ private const int UnknownErrno = int.MaxValue;
+ private const int UnregisteredWriteIndex = -1;
+
+ private const int IOC_NRSHIFT = 0;
+ private const int IOC_TYPESHIFT = IOC_NRSHIFT + 8; // 8 = IOC_NRBITS
+ private const int IOC_SIZESHIFT = IOC_TYPESHIFT + 8; // 8 = IOC_TYPEBITS
+ private const int IOC_DIRSHIFT = IOC_SIZESHIFT + 14; // 14 = IOC_SIZEBITS
+ private const uint IOC_WRITE = 1;
+ private const uint IOC_READ = 2;
+ private const uint DIAG_IOC_MAGIC = '*';
+
+ private static RawFileHandle? userEventsDataStatic;
+ private static Int32[]? emptyEnabledPinned; // From normal heap.
+
+ ///
+ /// When this.IsInvalid, enabledPinned is a shared emptyEnabledPinned from the normal heap.
+ /// When !this.IsInvalid, enabledPinned is a unique allocation from the pinned object heap.
+ ///
+ private Int32[] enabledPinned;
+
+ ///
+ /// Initializes a new handle that is invalid.
+ ///
+#pragma warning disable CA1419 // Provide a parameterless constructor. (We don't need the runtime to create instances of TracepointHandle.)
+ private TracepointHandle()
+#pragma warning restore CA1419
+ : base((nint)UnregisteredWriteIndex, true)
+ {
+ var enabledPinned = emptyEnabledPinned ??
+ Utility.InterlockedInitSingleton(ref emptyEnabledPinned, new Int32[1]);
+ Debug.Assert(0 == enabledPinned[0]);
+ this.enabledPinned = enabledPinned;
+ }
+
+ ///
+ /// If registration succeeded, returns 0.
+ /// If registration failed, returns the errno from the failed open/ioctl.
+ ///
+ public int RegisterResult { get; private set; } = UnknownErrno;
+
+ ///
+ /// Returns true if this tracepoint is enabled, false otherwise.
+ /// Value may change at any time while the tracepoint is registered.
+ ///
+ public bool IsEnabled => 0 != this.enabledPinned[0];
+
+ ///
+ /// Returns true if registration was successful.
+ /// (Remains true even after handle is closed/disposed.)
+ ///
+ public override bool IsInvalid => (nint)UnregisteredWriteIndex == this.handle;
+
+ ///
+ /// Given an all-Latin1 user_events command string (no characters with value > 255),
+ /// attempts to register it. Returns a TracepointHandle with the result.
+ /// Syntax for nameArgs is given here:
+ /// https://docs.kernel.org/trace/user_events.html#command-format,
+ /// e.g. "MyEventName int arg1; u32 arg2".
+ ///
+ /// If registration succeeds, the returned handle will be valid and active:
+ /// IsInvalid == false, IsEnabled is dynamic, RegisterResult == 0,
+ /// Write is meaningful.
+ ///
+ /// If registration fails, the returned handle will be invalid and inactive:
+ /// IsInvalid == true, IsEnabled == false, RegisterResult != 0,
+ /// Write will always return EBADF.
+ ///
+ public static TracepointHandle Register(ReadOnlySpan nameArgs, PerfUserEventReg flags = 0)
+ {
+ Span nulTerminatedNameArgs = stackalloc byte[nameArgs.Length + 1];
+ for (var i = 0; i < nameArgs.Length; i += 1)
+ {
+ nulTerminatedNameArgs[i] = unchecked((byte)nameArgs[i]);
+ }
+
+ nulTerminatedNameArgs[nameArgs.Length] = 0;
+ return Register(nulTerminatedNameArgs, flags);
+ }
+
+ ///
+ /// Given a NUL-terminated Latin1 user_events command string, attempts to register it.
+ /// Returns a TracepointHandle with the result.
+ /// Syntax for nameArgs is given here:
+ /// https://docs.kernel.org/trace/user_events.html#command-format,
+ /// e.g. "MyEventName int arg1; u32 arg2".
+ ///
+ /// If registration succeeds, the returned handle will be valid and active:
+ /// IsInvalid == false, IsEnabled is meaningful, RegisterResult == 0,
+ /// Write is meaningful.
+ ///
+ /// If registration fails, the returned handle will be invalid and inactive:
+ /// IsInvalid == true, IsEnabled == false, RegisterResult != 0,
+ /// Write will always return EBADF.
+ ///
+ public static TracepointHandle Register(ReadOnlySpan nulTerminatedNameArgs, PerfUserEventReg flags = 0)
+ {
+ Debug.Assert(0 <= nulTerminatedNameArgs.LastIndexOf((byte)0));
+
+ var tracepoint = new TracepointHandle();
+
+ var userEventsData = userEventsDataStatic ?? InitUserEventsDataStatic();
+ if (userEventsData.OpenResult != 0)
+ {
+ tracepoint.InitFailed(userEventsData.OpenResult);
+ }
+ else
+ {
+ var enabledPinned = GC.AllocateArray(1, pinned: true);
+ Debug.Assert(0 == enabledPinned[0]);
+
+ int ioctlResult;
+ unsafe
+ {
+ fixed (Int32* enabledPtr = enabledPinned)
+ {
+ fixed (byte* nameArgsPtr = nulTerminatedNameArgs)
+ {
+ var reg = new user_reg
+ {
+ size = user_reg.SizeOfStruct,
+ enable_size = EnableSize,
+ flags = flags,
+ enable_addr = (nuint)enabledPtr,
+ name_args = (nuint)nameArgsPtr,
+ };
+
+ // Need a finally block to make sure the thread is not interrupted
+ // between the ioctl and the SetHandle.
+ try
+ {
+ // Nothing.
+ }
+ finally
+ {
+ var DIAG_IOCSREG =
+ ((IOC_WRITE | IOC_READ) << IOC_DIRSHIFT) |
+ (DIAG_IOC_MAGIC << IOC_TYPESHIFT) |
+ (0u << IOC_NRSHIFT) |
+ ((uint)IntPtr.Size << IOC_SIZESHIFT);
+ ioctlResult = userEventsData.Ioctl(DIAG_IOCSREG, ref reg);
+ if (ioctlResult >= 0)
+ {
+ tracepoint.enabledPinned = enabledPinned;
+ tracepoint.SetHandle((nint)reg.write_index);
+ }
+ }
+ }
+ }
+ }
+
+ if (ioctlResult >= 0)
+ {
+ tracepoint.InitSucceeded();
+ }
+ else
+ {
+ var errno = Marshal.GetLastWin32Error();
+ if (errno <= 0)
+ {
+ errno = UnknownErrno;
+ }
+
+ tracepoint.InitFailed(errno);
+ }
+ }
+
+ // All code paths should have called either InitSucceeded or InitFailed.
+ return tracepoint;
+ }
+
+ ///
+ /// Sends tracepoint data to the user_events_data file. Uses data[0] for headers.
+ /// Returns EBADF if closed. Does NOT check IsEnabled (caller should do that).
+ ///
+ /// Requires: data[0].Length == 0 (data[0] will be used for headers).
+ ///
+ public int Write(Span data)
+ {
+ // Precondition: slot for write_index in data[0].
+ Debug.Assert(data[0].Length == 0);
+
+ var userEventsData = userEventsDataStatic;
+ Debug.Assert(userEventsData != null); // Otherwise there would be no TracepointHandle instance.
+ Debug.Assert(userEventsData.OpenResult == 0); // Otherwise Enabled would be false.
+
+ var writeIndex = new WriteIndexPlus { WriteIndex = (Int32)(nint)this.handle, Padding = 0 };
+ unsafe
+ {
+ // Workaround: On old kernels, events with 0 bytes of data don't get written.
+ // If event has 0 bytes of data, add a byte to avoid the problem.
+ data[0] = new DataSegment(&writeIndex, sizeof(Int32) + (data.Length == 1 ? 1u : 0u));
+ }
+
+ if (this.IsClosed)
+ {
+ return DisabledEventError;
+ }
+
+ // Ignore race condition: if we're disposed between checking IsClosed and calling WriteV,
+ // we will write the data using a write_index that doesn't belong to us. That's not great,
+ // but it's not fatal and probably not worth degrading performance to avoid it. The
+ // write_index is unlikely to be recycled during the race condition, and even if it is
+ // recycled, the worst consequence would be a garbage event in a trace. Could avoid with:
+ // try { AddRef; WriteV; } catch { return EBADF; } finally { Release; }.
+
+ var writevResult = userEventsData.WriteV(data);
+ return writevResult >= 0 ? 0 : Marshal.GetLastWin32Error();
+ }
+
+ ///
+ /// Sends tracepoint with EventHeader to the user_events_data file. Uses data[0] for headers.
+ /// Returns EBADF if closed. Does NOT check IsEnabled (caller should do that).
+ ///
+ /// Fills in data[0] with writeIndex + eventHeader + activityIdBlock? + metadataHeader?. Sets the extension
+ /// block's flags based on metaLength.
+ ///
+ /// Requires: data[0].Length == 0 (data[0] will be used for headers).
+ ///
+ /// Requires: relatedId cannot be present unless activityId is present.
+ ///
+ /// Requires: If activityId is present or metaLength != 0 then
+ /// eventHeader.Flags must equal DefaultWithExtension.
+ ///
+ /// Requires: If metaLength != 0 then data[1] starts with metadata extension block data.
+ ///
+ public unsafe int WriteEventHeader(
+ EventHeader eventHeader,
+ Guid* activityId,
+ Guid* relatedId,
+ ushort metaLength,
+ Span data)
+ {
+ // Precondition: slot for write_index in data[0].
+ Debug.Assert(data[0].Length == 0);
+
+ // Precondition: relatedId cannot be present unless activityId is present.
+ Debug.Assert(relatedId == null || activityId != null);
+
+ // Precondition: eventHeader.Flags must match up with presence of first extension.
+ Debug.Assert((activityId == null && metaLength == 0) ||
+ eventHeader.Flags == (EventHeader.DefaultFlags | EventHeaderFlags.Extension));
+
+ // Precondition: metaLength implies metadata extension block data.
+ Debug.Assert(metaLength == 0 || data.Length > 1);
+
+ var userEventsData = userEventsDataStatic;
+ Debug.Assert(userEventsData != null); // Otherwise there would be no TracepointHandle instance.
+ Debug.Assert(userEventsData.OpenResult == 0); // Otherwise Enabled would be false.
+
+ const byte HeadersMax = sizeof(Int32) // writeIndex
+ + EventHeader.SizeOfStruct // eventHeader
+ + EventHeaderExtension.SizeOfStruct + 16 + 16 // activityId header + activityId + relatedId
+ + EventHeaderExtension.SizeOfStruct; // metadata header
+ var writeIndex = (Int32)(nint)this.handle;
+ unsafe
+ {
+ uint* headersUInt32 = stackalloc UInt32[HeadersMax / sizeof(UInt32)]; // Ensure 4-byte alignment.
+ byte* headers = (byte*)headersUInt32;
+ uint pos = 0;
+
+ *(Int32*)&headers[pos] = (Int32)(nint)this.handle;
+ pos += sizeof(Int32);
+
+ *(EventHeader*)&headers[pos] = eventHeader;
+ pos += EventHeader.SizeOfStruct;
+
+ if (activityId != null)
+ {
+ var kind = EventHeaderExtensionKind.ActivityId | (metaLength == 0 ? 0 : EventHeaderExtensionKind.ChainFlag);
+ if (relatedId != null)
+ {
+ *(EventHeaderExtension*)&headers[pos] = new EventHeaderExtension { Kind = kind, Size = 32 };
+ pos += EventHeaderExtension.SizeOfStruct;
+ Utility.WriteGuidBigEndian(new Span(&headers[pos], 16), *activityId);
+ pos += 16;
+ Utility.WriteGuidBigEndian(new Span(&headers[pos], 16), *relatedId);
+ pos += 16;
+ }
+ else
+ {
+ *(EventHeaderExtension*)&headers[pos] = new EventHeaderExtension { Kind = kind, Size = 16 };
+ pos += EventHeaderExtension.SizeOfStruct;
+ Utility.WriteGuidBigEndian(new Span(&headers[pos], 16), *activityId);
+ pos += 16;
+ }
+ }
+
+ if (metaLength != 0)
+ {
+ *(EventHeaderExtension*)&headers[pos] = new EventHeaderExtension
+ {
+ Kind = EventHeaderExtensionKind.Metadata, // Last one, so no chain flag.
+ Size = metaLength,
+ };
+ pos += EventHeaderExtension.SizeOfStruct;
+ }
+
+ data[0] = new DataSegment(headers, pos);
+ }
+
+ if (this.IsClosed)
+ {
+ return DisabledEventError;
+ }
+
+ // Ignore race condition: if we're disposed between checking IsClosed and calling WriteV,
+ // we will write the data using a write_index that doesn't belong to us. That's not great,
+ // but it's not fatal and probably not worth degrading performance to avoid it. The
+ // write_index is unlikely to be recycled during the race condition, and even if it is
+ // recycled, the worst consequence would be a garbage event in a trace. Could avoid with:
+ // try { AddRef; WriteV; } catch { return EBADF; } finally { Release; }.
+
+ var writevResult = userEventsData.WriteV(data);
+ return writevResult >= 0 ? 0 : Marshal.GetLastWin32Error();
+ }
+
+ ///
+ /// Returns an array of length 1 that contains the value that will be updated by the
+ /// kernel when the tracepoint is enabled or disabled. Caller MUST NOT modify the contents
+ /// of the array.
+ ///
+ /// When this.IsInvalid, the array is shared by all other invalid handles and is a normal allocation.
+ /// When !this.IsInvalid, there is a separate array for each handle and is a pinned allocation.
+ ///
+ public Int32[] DangerousGetEnablementArray()
+ {
+ return this.enabledPinned;
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ var userEventsData = userEventsDataStatic;
+ Debug.Assert(userEventsData != null); // Otherwise there would be no TracepointHandle instance.
+
+ var enabledPinned = this.enabledPinned;
+ int ioctlResult;
+ unsafe
+ {
+ Debug.Assert(!ReferenceEquals(enabledPinned, emptyEnabledPinned));
+ fixed (Int32* enabledPtr = enabledPinned)
+ {
+ var unreg = new user_unreg
+ {
+ size = user_unreg.SizeOfStruct,
+ disable_addr = (nuint)enabledPtr,
+ };
+
+ var DIAG_IOCSUNREG =
+ (IOC_WRITE << IOC_DIRSHIFT) |
+ (DIAG_IOC_MAGIC << IOC_TYPESHIFT) |
+ (2u << IOC_NRSHIFT) |
+ ((uint)IntPtr.Size << IOC_SIZESHIFT);
+ ioctlResult = userEventsData.Ioctl(DIAG_IOCSUNREG, ref unreg);
+ }
+ }
+
+ enabledPinned[0] = 0; // Force IsEnabled = false.
+ return 0 <= ioctlResult;
+ }
+
+ ///
+ /// Locates and opens the user_events_data file.
+ /// First call to this will update userEventsDataStatic with the result.
+ ///
+ /// The new value of userEventsDataStatic (never null).
+ private static RawFileHandle InitUserEventsDataStatic()
+ {
+ RawFileHandle? resultHandle = null;
+ RawFileHandle? newHandle = null;
+ try
+ {
+ newHandle = RawFileHandle.OpenWRONLY("/sys/kernel/tracing/user_events_data\0"u8);
+ if (newHandle.OpenResult == 0)
+ {
+ // Success.
+ }
+ else if (!File.Exists("/proc/mounts"))
+ {
+ // Give up.
+ }
+ else
+ {
+ Span path = stackalloc byte[274]; // 256 + sizeof("/user_events_data\0")
+ FileStream? mounts = null;
+ try
+ {
+ mounts = File.OpenRead("/proc/mounts");
+
+ Span line = stackalloc byte[4096];
+ bool eof = false;
+ while (!eof)
+ {
+ // ~fgets
+ int lineEnd;
+ for (lineEnd = 0; lineEnd < line.Length; lineEnd += 1)
+ {
+ var b = mounts.ReadByte();
+ if (b < 0)
+ {
+ eof = true;
+ break;
+ }
+ else if (b == '\n')
+ {
+ break;
+ }
+ else
+ {
+ line[lineEnd] = (byte)b;
+ }
+ }
+
+ // line is "device_name mount_point file_system other_stuff..."
+
+ int linePos = 0;
+
+ // device_name
+ while (linePos < lineEnd && IsNonspaceByte(line[linePos]))
+ {
+ linePos += 1;
+ }
+
+ // whitespace
+ while (linePos < lineEnd && IsSpaceByte(line[linePos]))
+ {
+ linePos += 1;
+ }
+
+ // mount_point
+ var mountBegin = linePos;
+ while (linePos < lineEnd && IsNonspaceByte(line[linePos]))
+ {
+ linePos += 1;
+ }
+
+ var mountEnd = linePos;
+
+ // whitespace
+ while (linePos < lineEnd && IsSpaceByte(line[linePos]))
+ {
+ linePos += 1;
+ }
+
+ // file_system
+ var fsBegin = linePos;
+ while (linePos < lineEnd && IsNonspaceByte(line[linePos]))
+ {
+ linePos += 1;
+ }
+
+ var fsEnd = linePos;
+
+ if (linePos == lineEnd || !IsSpaceByte(line[linePos]))
+ {
+ // Ignore line if no whitespace after file_system.
+ continue;
+ }
+
+ bool foundTraceFS;
+ var fs = line.Slice(fsBegin, fsEnd - fsBegin);
+ if (fs.SequenceEqual("tracefs"u8))
+ {
+ // "tracefsMountPoint/user_events_data"
+ foundTraceFS = true;
+ }
+ else if (path[0] == 0 && fs.SequenceEqual("debugfs"u8))
+ {
+ // "debugfsMountPoint/tracing/user_events_data"
+ foundTraceFS = false;
+ }
+ else
+ {
+ continue;
+ }
+
+ var pathSuffix0 = foundTraceFS
+ ? "/user_events_data\0"u8
+ : "/tracing/user_events_data\0"u8;
+
+ var mountLen = mountEnd - mountBegin;
+ var pathLen = mountLen + pathSuffix0.Length; // Includes NUL
+ if (pathLen > path.Length)
+ {
+ continue;
+ }
+
+ // path = mountpoint + suffix
+ line.Slice(mountBegin, mountLen).CopyTo(path);
+ pathSuffix0.CopyTo(path.Slice(mountLen)); // Includes NUL
+
+ if (foundTraceFS)
+ {
+ // Found a match, and it's tracefs, so stop looking.
+ break;
+ }
+ else
+ {
+ // Found a match, but it's debugfs. We prefer tracefs, so keep looking.
+ }
+ }
+ }
+ catch (ArgumentException) { }
+ catch (IOException) { }
+ catch (NotSupportedException) { }
+ catch (UnauthorizedAccessException) { }
+ finally
+ {
+ mounts?.Dispose();
+ }
+
+ if (path[0] != 0)
+ {
+ newHandle.Dispose();
+ newHandle = RawFileHandle.OpenWRONLY(path);
+ }
+ }
+
+ var oldHandle = Interlocked.CompareExchange(ref userEventsDataStatic, newHandle, null);
+ if (oldHandle != null)
+ {
+ resultHandle = oldHandle;
+ }
+ else
+ {
+ resultHandle = newHandle;
+ }
+ }
+ finally
+ {
+ if (newHandle != null && newHandle != resultHandle)
+ {
+ newHandle.Dispose();
+ }
+ }
+
+ return resultHandle;
+ }
+
+ private static bool IsSpaceByte(byte b)
+ {
+ return b == ' ' || b == '\t';
+ }
+
+ private static bool IsNonspaceByte(byte b)
+ {
+ return b != '\0' && !IsSpaceByte(b);
+ }
+
+ private void InitSucceeded()
+ {
+ this.RegisterResult = 0;
+ Debug.Assert(1 >= this.enabledPinned[0]);
+ Debug.Assert(!ReferenceEquals(this.enabledPinned, emptyEnabledPinned));
+ Debug.Assert(!this.IsClosed);
+ Debug.Assert(!this.IsInvalid);
+ }
+
+ private void InitFailed(int registerResult)
+ {
+ this.SetHandleAsInvalid();
+ this.RegisterResult = registerResult;
+ Debug.Assert(0 == this.enabledPinned[0]);
+ Debug.Assert(ReferenceEquals(this.enabledPinned, emptyEnabledPinned));
+ Debug.Assert(this.IsClosed);
+ Debug.Assert(this.IsInvalid);
+ }
+
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ private struct user_reg
+ {
+ public const int SizeOfStruct = 28;
+ public UInt32 size;
+ public byte enable_bit;
+ public byte enable_size;
+ public PerfUserEventReg flags;
+ public UInt64 enable_addr;
+ public UInt64 name_args;
+ public Int32 write_index;
+ }
+
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ private struct user_unreg
+ {
+ public const int SizeOfStruct = 16;
+ public UInt32 size;
+ public byte disable_bit;
+ public byte reserved;
+ public UInt16 reserved2;
+ public UInt64 disable_addr;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct WriteIndexPlus
+ {
+ public Int32 WriteIndex;
+ public UInt32 Padding;
+ }
+}
diff --git a/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Provider/Utility.cs b/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Provider/Utility.cs
new file mode 100644
index 0000000000..ef68419d51
--- /dev/null
+++ b/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Provider/Utility.cs
@@ -0,0 +1,41 @@
+#nullable enable
+
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+namespace Microsoft.LinuxTracepoints.Provider;
+
+using System;
+using System.Runtime.InteropServices;
+using Interlocked = System.Threading.Interlocked;
+using BinaryPrimitives = System.Buffers.Binary.BinaryPrimitives;
+
+internal static class Utility
+{
+ ///
+ /// Atomically: old = location; if (old != null) { return old; } else { location = value; return value; }
+ ///
+ public static T InterlockedInitSingleton(ref T? location, T value)
+ where T : class
+ {
+ return Interlocked.CompareExchange(ref location, value, null) ?? value;
+ }
+
+ public static void WriteGuidBigEndian(Span destination, Guid value)
+ {
+ if (BitConverter.IsLittleEndian)
+ {
+ unsafe
+ {
+ var p = (byte*)&value;
+ var p0 = (uint*)p;
+ var p1 = (ushort*)(p + 4);
+ var p2 = (ushort*)(p + 6);
+ *p0 = BinaryPrimitives.ReverseEndianness(*p0);
+ *p1 = BinaryPrimitives.ReverseEndianness(*p1);
+ *p2 = BinaryPrimitives.ReverseEndianness(*p2);
+ }
+ }
+ MemoryMarshal.Write(destination, ref value);
+ }
+}
diff --git a/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/README.md b/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/README.md
new file mode 100644
index 0000000000..3e4bb7b133
--- /dev/null
+++ b/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/README.md
@@ -0,0 +1,17 @@
+# LinuxTracepoints-Net
+
+The code in this folder came from the
+[LinuxTracepoints-Net](https://github.com/microsoft/LinuxTracepoints-Net) repo
+([commit
+974c475](https://github.com/microsoft/LinuxTracepoints-Net/blob/974c47522d053c915009ef5112840026eaf22adb)).
+
+Only the files required to build
+`Microsoft.LinuxTracepoints.Provider.PerfTracepoint` were included.
+
+The following changes were made:
+
+* `#nullable enabled` added at the top of all files. This is because
+ LinuxTracepoints-Net has `enabled` repo-wide but
+ GenevaExporter has `disabled` at the moment.
+
+* `public` types made `internal`.
diff --git a/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Types/EventHeader.cs b/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Types/EventHeader.cs
new file mode 100644
index 0000000000..a8db3a3140
--- /dev/null
+++ b/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Types/EventHeader.cs
@@ -0,0 +1,302 @@
+#nullable enable
+
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+/*--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 the same 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 needed 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.
+*/
+namespace Microsoft.LinuxTracepoints
+{
+ using System;
+ using System.Runtime.InteropServices;
+ using Tracing = System.Diagnostics.Tracing;
+
+ ///
+ ///
+ /// 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 EventHeaderExtension blocks. Otherwise the EventHeader is
+ /// followed by the event payload data.
+ ///
+ /// If an EventHeaderExtension.Kind has the Chain flag set then the
+ /// EventHeaderExtension 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 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.
+ ///
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct EventHeader
+ {
+ ///
+ /// The size of this structure in bytes (8).
+ ///
+ public const int SizeOfStruct = 8;
+
+ ///
+ /// Pointer-size and Endian flags as appropriate for the currently-running process,
+ /// no extension blocks present.
+ ///
+ public static readonly EventHeaderFlags DefaultFlags =
+ (IntPtr.Size == 8 ? EventHeaderFlags.Pointer64 : EventHeaderFlags.None) |
+ (BitConverter.IsLittleEndian ? EventHeaderFlags.LittleEndian : EventHeaderFlags.None);
+
+ ///
+ /// The encoding corresponding to IntPtr. If IntPtr.Size == 8, this is Value64.
+ /// Otherwise, this is Value32.
+ ///
+ public static readonly EventHeaderFieldEncoding IntPtrEncoding =
+ IntPtr.Size == 8 ? EventHeaderFieldEncoding.Value64 : EventHeaderFieldEncoding.Value32;
+
+ ///
+ /// Pointer64, LittleEndian, Extension.
+ ///
+ public EventHeaderFlags Flags;
+
+ ///
+ /// Increment Version whenever event layout changes.
+ ///
+ public byte Version;
+
+ ///
+ /// Stable id for this event, or 0 if none.
+ ///
+ public ushort Id;
+
+ ///
+ /// Provider-defined event tag, or 0 if none.
+ ///
+ public ushort Tag;
+
+ ///
+ /// EventOpcode raw value. (Stores the value of the Opcode property.)
+ ///
+ public byte OpcodeByte;
+
+ ///
+ /// EventLevel raw value. (Stores the value of the Level property.)
+ ///
+ public byte LevelByte;
+
+ ///
+ /// EventOpcode: info, start activity, stop activity, etc.
+ /// Throws OverflowException if set value > 255.
+ ///
+ ///
+ /// 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 ActivityStart and ActivityStop.
+ ///
+ /// 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 = ActivityStart 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 ActivityStart or ActivityStop, 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 = ActivityStop and with
+ /// an ActivityId header extension containing the activity id of the activity
+ /// that is ending (extension length will be 16).
+ ///
+ ///
+ /// Set value > 255
+ public Tracing.EventOpcode Opcode
+ {
+ readonly get => (Tracing.EventOpcode)OpcodeByte;
+ set => OpcodeByte = checked((byte)value);
+ }
+
+ ///
+ /// EventLevel: critical, error, warning, info, verbose.
+ /// Throws OverflowException if set value > 255.
+ ///
+ /// Set value > 255
+ public Tracing.EventLevel Level
+ {
+ readonly get => (Tracing.EventLevel)LevelByte;
+ set => LevelByte = checked((byte)value);
+ }
+
+ // Followed by: EventHeaderExtension block(s), then event payload.
+ }
+}
diff --git a/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Types/EventHeaderExtension.cs b/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Types/EventHeaderExtension.cs
new file mode 100644
index 0000000000..807217a5f0
--- /dev/null
+++ b/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Types/EventHeaderExtension.cs
@@ -0,0 +1,46 @@
+#nullable enable
+
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+namespace Microsoft.LinuxTracepoints
+{
+ using System.Runtime.InteropServices;
+
+ ///
+ ///
+ /// Additional information for an EventHeader event.
+ ///
+ /// If EventHeader.Flags has the Extension bit set then the EventHeader is
+ /// followed by one or more EventHeaderExtension blocks. Otherwise the EventHeader
+ /// is followed by the event payload data.
+ ///
+ /// If EventHeaderExtension.Kind has the Chain flag set then the
+ /// EventHeaderExtension block is followed immediately (no alignment/padding) by
+ /// another extension block. Otherwise it is followed immediately (no
+ /// alignment/padding) by the event payload data.
+ ///
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct EventHeaderExtension
+ {
+ ///
+ /// The size of this structure in bytes (4).
+ ///
+ public const int SizeOfStruct = 4;
+
+ ///
+ /// The size of the extension data in bytes.
+ /// The data immediately follows this structure with no padding/alignment.
+ ///
+ public ushort Size;
+
+ ///
+ /// The kind of extension. This determines the format of the extension data.
+ /// In addition, the Chain flag indicates whether another extension follows.
+ ///
+ public EventHeaderExtensionKind Kind;
+
+ // Followed by Size bytes of data. No padding/alignment.
+ }
+}
diff --git a/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Types/EventHeaderExtensionKind.cs b/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Types/EventHeaderExtensionKind.cs
new file mode 100644
index 0000000000..2d2e845f33
--- /dev/null
+++ b/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Types/EventHeaderExtensionKind.cs
@@ -0,0 +1,117 @@
+#nullable enable
+
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+namespace Microsoft.LinuxTracepoints
+{
+ ///
+ /// Values for EventHeaderExtension.Kind.
+ ///
+ internal enum EventHeaderExtensionKind : ushort
+ {
+ ///
+ /// Mask for the base extension kind (low 15 bits).
+ ///
+ ValueMask = 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).
+ ///
+ ChainFlag = 0x8000,
+
+ ///
+ /// Invalid extension kind.
+ ///
+ Invalid = 0,
+
+ ///
+ ///
+ /// Extension contains an event definition (i.e. event metadata).
+ ///
+ /// Event definition format:
+ /// -
+ /// char EventName[]; // Nul-terminated utf-8 string: "eventName{;attribName=attribValue}"
+ ///
-
+ /// 0 or more field definition blocks.
+ ///
+ /// Field definition block:
+ /// -
+ /// char FieldName[]; // Nul-terminated utf-8 string: "fieldName{;attribName=attribValue}"
+ ///
-
+ /// uint8_t Encoding; // Encoding is 0..31, with 3 flag bits.
+ ///
-
+ /// uint8_t Format; // Present if (Encoding & 128). Format is 0..127, with 1 flag bit.
+ ///
-
+ /// uint16_t Tag; // Present if (Format & 128). Contains provider-defined value.
+ ///
-
+ /// uint16_t ArrayLength; // Present if (Encoding & 32). Contains element count of constant-length array.
+ ///
+ /// Notes:
+ /// -
+ /// eventName and fieldName may not contain any ';' characters.
+ ///
-
+ /// eventName and fieldName 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".
+ ///
+ ///
+ 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 = ActivityStart. 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 ActivityStart or ActivityStop). 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 = ActivityStop. 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 expensive, this
+ /// may also be a 128-bit LUID (locally-unique id), generated using any method
+ /// that is unlikely to conflict with 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.
+ ///
+ ///
+ ActivityId,
+ }
+
+ ///
+ /// Extension methods for .
+ ///
+ internal static class EventHeaderExtensionKindExtensions
+ {
+ ///
+ /// Returns the format without any flags (format & ValueMask).
+ ///
+ public static EventHeaderExtensionKind BaseKind(this EventHeaderExtensionKind kind) =>
+ kind & EventHeaderExtensionKind.ValueMask;
+
+ ///
+ /// Returns true if ChainFlag is present (more extensions are present in event).
+ ///
+ public static bool HasChainFlag(this EventHeaderExtensionKind kind) =>
+ 0 != (kind & EventHeaderExtensionKind.ChainFlag);
+ }
+}
diff --git a/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Types/EventHeaderFieldEncoding.cs b/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Types/EventHeaderFieldEncoding.cs
new file mode 100644
index 0000000000..28cb80ff87
--- /dev/null
+++ b/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Types/EventHeaderFieldEncoding.cs
@@ -0,0 +1,269 @@
+#nullable enable
+
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+namespace Microsoft.LinuxTracepoints
+{
+ ///
+ ///
+ /// 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 the following information about the field:
+ /// -
+ /// How the decoder should determine the size of the field. For example,
+ /// Value32 indicates a 4-byte field, Value128 indicates a 16-byte
+ /// field, ZStringChar8 indicates that the field ends at the first char8 unit
+ /// with value 0, and BinaryLength16Char8 indicates that the first 16 bits of
+ /// the field are the uint16 Length and that the subsequent Length
+ /// char8 units are the field value.
+ ///
+ /// How the field should be formatted if the field's format is
+ /// , unrecognized, or unsupported. For
+ /// example, a Value32 encoding with Default or unrecognized format
+ /// should be treated as if it had UnsignedInt format. A
+ /// StringLength16Char8 encoding with Default or unrecognized format
+ /// should be treated as if it had StringUtf format. A
+ /// BinaryLength16Char8 encoding with Default or unrecognized format
+ /// should be treated as if it had HexBytes format.
+ ///
+ ///
+ /// The StringLength16Char8 and BinaryLength16Char8 are special. These
+ /// encodings can be used with both variable-length (e.g. HexBytes and
+ /// String) formats as well as with fixed-length (e.g. UnsignedInt,
+ /// Float, IPAddress) formats. When used with fixed-length formats,
+ /// the semantics depend on the field's variable Length (as determined from the first
+ /// two bytes of the field):
+ /// -
+ /// If the Length is 0, the field is formatted as null. For example, a field
+ /// with encoding = BinaryLength16Char8, format = SignedInt, and
+ /// Length = 0 would be formatted as a null value.
+ ///
-
+ /// If the Length is appropriate for the format, the field is formatted as if it had
+ /// the Value8, Value16, Value32, Value64, or Value128 encoding corresponding to its
+ /// size. For example, a field with encoding = BinaryLength16Char8,
+ /// format = SignedInt, and Length = 4 would be formatted as an Int32 field.
+ ///
-
+ /// If the Length is not appropriate for the format, the field is formatted as if it
+ /// had the default format for the encoding. For example, a field with
+ /// encoding = BinaryLength16Char8, format = SignedInt, and Length = 16
+ /// would be formatted as a HexBytes field since 16 is not a supported size
+ /// for the SignedInt format and the default format for
+ /// BinaryLength16Char8 is HexBytes.
+ ///
+ ///
+ /// The top 3 bits of the field encoding byte are flags:
+ /// -
+ /// CArrayFlag indicates that this field is a constant-length array, with the
+ /// element count specified as a 16-bit value in the event metadata (must not be
+ /// 0).
+ ///
-
+ /// VArrayFlag 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).
+ ///
-
+ /// ChainFlag indicates that a format byte is present after the encoding byte.
+ /// If Chain is not set, the format byte is omitted and is assumed to be 0.
+ ///
+ /// Setting both CArray and VArray is invalid (reserved).
+ ///
+ ///
+ internal enum EventHeaderFieldEncoding : byte
+ {
+ ///
+ /// Mask for the base encoding type (low 5 bits).
+ ///
+ ValueMask = 0x1F,
+
+ ///
+ /// Mask for the encoding flags: CArrayFlag, VArrayFlag, ChainFlag.
+ ///
+ FlagMask = 0xE0,
+
+ ///
+ /// Mask for the array flags: CArrayFlag, VArrayFlag.
+ ///
+ ArrayFlagMask = 0x60,
+
+ ///
+ /// Constant-length array: 16-bit element count in metadata (must not be 0).
+ ///
+ CArrayFlag = 0x20,
+
+ ///
+ /// Variable-length array: 16-bit element count in payload (may be 0).
+ ///
+ VArrayFlag = 0x40,
+
+ ///
+ /// If present in the field, this flag indicates that an EventHeaderFieldFormat
+ /// byte follows the EventHeaderFieldEncoding byte.
+ ///
+ ChainFlag = 0x80,
+
+ ///
+ /// Invalid encoding value.
+ ///
+ Invalid = 0,
+
+ ///
+ /// 0-byte value, logically groups subsequent N fields, N = format & 0x7F, N must not be 0.
+ ///
+ Struct,
+
+ ///
+ /// 1-byte value, default format UnsignedInt.
+ ///
+ Value8,
+
+ ///
+ /// 2-byte value, default format UnsignedInt.
+ ///
+ Value16,
+
+ ///
+ /// 4-byte value, default format UnsignedInt.
+ ///
+ Value32,
+
+ ///
+ /// 8-byte value, default format UnsignedInt.
+ ///
+ Value64,
+
+ ///
+ /// 16-byte value, default format HexBytes.
+ ///
+ Value128,
+
+ ///
+ /// zero-terminated uint8[], default format StringUtf.
+ ///
+ ZStringChar8,
+
+ ///
+ /// zero-terminated uint16[], default format StringUtf.
+ ///
+ ZStringChar16,
+
+ ///
+ /// zero-terminated uint32[], default format StringUtf.
+ ///
+ ZStringChar32,
+
+ ///
+ /// uint16 Length followed by uint8 Data[Length], default format StringUtf.
+ /// This should be treated exactly the same as BinaryLength16Char8 except that it
+ /// has a different default format.
+ ///
+ StringLength16Char8,
+
+ ///
+ /// uint16 Length followed by uint16 Data[Length], default format StringUtf.
+ ///
+ StringLength16Char16,
+
+ ///
+ /// uint16 Length followed by uint32 Data[Length], default format StringUtf.
+ ///
+ StringLength16Char32,
+
+ ///
+ /// uint16 Length followed by uint8 Data[Length], default format HexBytes.
+ /// This should be treated exactly the same as StringLength16Char8 except that it
+ /// has a different default format.
+ ///
+ BinaryLength16Char8,
+
+ ///
+ /// Invalid encoding value. Value will change in future versions of this header.
+ ///
+ Max,
+ }
+
+ ///
+ /// Extension methods for .
+ ///
+ internal static class EventHeaderFieldEncodingExtensions
+ {
+ ///
+ /// Returns the encoding without any flags (encoding & ValueMask).
+ ///
+ public static EventHeaderFieldEncoding WithoutFlags(this EventHeaderFieldEncoding encoding) =>
+ encoding & EventHeaderFieldEncoding.ValueMask;
+
+ ///
+ /// Returns the encoding without the chain flag (encoding & ~ChainFlag).
+ ///
+ public static EventHeaderFieldEncoding WithoutChainFlag(this EventHeaderFieldEncoding encoding) =>
+ encoding & ~EventHeaderFieldEncoding.ChainFlag;
+
+ ///
+ /// Returns the array flags of the encoding (VArrayFlag or CArrayFlag, if set).
+ ///
+ public static EventHeaderFieldEncoding ArrayFlags(this EventHeaderFieldEncoding encoding) =>
+ encoding & EventHeaderFieldEncoding.ArrayFlagMask;
+
+ ///
+ /// Returns true if any ArrayFlag is present (constant-length or variable-length array).
+ ///
+ public static bool IsArray(this EventHeaderFieldEncoding encoding) =>
+ 0 != (encoding & EventHeaderFieldEncoding.ArrayFlagMask);
+
+ ///
+ /// Returns true if CArrayFlag is present (constant-length array).
+ ///
+ public static bool IsCArray(this EventHeaderFieldEncoding encoding) =>
+ 0 != (encoding & EventHeaderFieldEncoding.CArrayFlag);
+
+ ///
+ /// Returns true if VArrayFlag is present (variable-length array).
+ ///
+ public static bool IsVArray(this EventHeaderFieldEncoding encoding) =>
+ 0 != (encoding & EventHeaderFieldEncoding.VArrayFlag);
+
+ ///
+ /// Returns true if ChainFlag is present (format byte is present in event).
+ ///
+ public static bool HasChainFlag(this EventHeaderFieldEncoding encoding) =>
+ 0 != (encoding & EventHeaderFieldEncoding.ChainFlag);
+
+ ///
+ /// Gets the default format for the encoding, or EventHeaderFieldFormat.Default if the encoding is invalid.
+ /// -
+ /// Value8, Value16, Value32, Value64: UnsignedInt.
+ ///
-
+ /// Value128: HexBytes.
+ ///
-
+ /// String: StringUtf.
+ ///
-
+ /// Other: Default.
+ ///
+ ///
+ public static EventHeaderFieldFormat DefaultFormat(this EventHeaderFieldEncoding encoding)
+ {
+ switch (encoding & EventHeaderFieldEncoding.ValueMask)
+ {
+ case EventHeaderFieldEncoding.Value8:
+ case EventHeaderFieldEncoding.Value16:
+ case EventHeaderFieldEncoding.Value32:
+ case EventHeaderFieldEncoding.Value64:
+ return EventHeaderFieldFormat.UnsignedInt;
+ case EventHeaderFieldEncoding.Value128:
+ return EventHeaderFieldFormat.HexBytes;
+ case EventHeaderFieldEncoding.ZStringChar8:
+ case EventHeaderFieldEncoding.ZStringChar16:
+ case EventHeaderFieldEncoding.ZStringChar32:
+ case EventHeaderFieldEncoding.StringLength16Char8:
+ case EventHeaderFieldEncoding.StringLength16Char16:
+ case EventHeaderFieldEncoding.StringLength16Char32:
+ return EventHeaderFieldFormat.StringUtf;
+ case EventHeaderFieldEncoding.BinaryLength16Char8:
+ return EventHeaderFieldFormat.HexBytes;
+ default:
+ return EventHeaderFieldFormat.Default;
+ }
+ }
+ }
+}
diff --git a/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Types/EventHeaderFieldFormat.cs b/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Types/EventHeaderFieldFormat.cs
new file mode 100644
index 0000000000..e6fa34e4fb
--- /dev/null
+++ b/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Types/EventHeaderFieldFormat.cs
@@ -0,0 +1,168 @@
+#nullable enable
+
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+#pragma warning disable CA1720 // Identifier contains type name
+
+namespace Microsoft.LinuxTracepoints
+{
+ ///
+ ///
+ /// 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 ChainFlag. 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.
+ ///
+ ///
+ internal enum EventHeaderFieldFormat : byte
+ {
+ ///
+ /// Mask for the base format type (low 7 bits).
+ ///
+ ValueMask = 0x7F,
+
+ ///
+ /// If present in the field, this flag indicates that a uint16
+ /// field tag follows the EventHeaderFieldFormat byte.
+ ///
+ ChainFlag = 0x80,
+
+ ///
+ /// Use the default format of the encoding.
+ ///
+ Default = 0,
+
+ ///
+ /// unsigned integer, event byte order.
+ /// Use with Value8..Value64 encodings.
+ ///
+ UnsignedInt,
+
+ ///
+ /// signed integer, event byte order.
+ /// Use with Value8..Value64 encodings.
+ ///
+ SignedInt,
+
+ ///
+ /// hex integer, event byte order.
+ /// Use with Value8..Value64 encodings.
+ ///
+ HexInt,
+
+ ///
+ /// errno, event byte order.
+ /// Use with Value32 encoding.
+ ///
+ Errno,
+
+ ///
+ /// process id, event byte order.
+ /// Use with Value32 encoding.
+ ///
+ Pid,
+
+ ///
+ /// signed integer, event byte order, seconds since 1970.
+ /// Use with Value32 or Value64 encodings.
+ ///
+ Time,
+
+ ///
+ /// 0 = false, 1 = true, event byte order.
+ /// Use with Value8..Value32 encodings.
+ ///
+ Boolean,
+
+ ///
+ /// floating point, event byte order.
+ /// Use with Value32..Value64 encodings.
+ ///
+ Float,
+
+ ///
+ /// binary, decoded as hex dump of bytes.
+ /// Use with any encoding.
+ ///
+ HexBytes,
+
+ ///
+ /// 8-bit char string, unspecified character set (usually treated as ISO-8859-1 or CP-1252).
+ /// Use with Value8 and Char8 encodings.
+ ///
+ String8,
+
+ ///
+ /// UTF string, event byte order, code unit size based on encoding.
+ /// Use with Value16..Value32 and Char8..Char32 encodings.
+ ///
+ StringUtf,
+
+ ///
+ /// UTF string, BOM used if present, otherwise behaves like StringUtf.
+ /// Use with Char8..Char32 encodings.
+ ///
+ StringUtfBom,
+
+ ///
+ /// XML string, otherwise behaves like StringUtfBom.
+ /// Use with Char8..Char32 encodings.
+ ///
+ StringXml,
+
+ ///
+ /// JSON string, otherwise behaves like StringUtfBom.
+ /// Use with Char8..Char32 encodings.
+ ///
+ StringJson,
+
+ ///
+ /// UUID, network byte order (RFC 4122 format).
+ /// Use with Value128 encoding.
+ ///
+ Uuid,
+
+ ///
+ /// IP port, network byte order (in_port_t layout).
+ /// Use with Value16 encoding.
+ ///
+ Port,
+
+ ///
+ /// IP address, network byte order (in_addr/in6_addr layout).
+ /// Use with Value32 or Value128 encoding.
+ ///
+ IPAddress,
+
+ ///
+ /// Deprecated: Alternate format for IPAddress.
+ /// Do not generate events with this format.
+ /// Decode this format as IPAddress.
+ ///
+ IPAddressObsolete,
+ }
+
+ ///
+ /// Extension methods for .
+ ///
+ internal static class EventHeaderFieldFormatExtensions
+ {
+ ///
+ /// Returns the format without any flags (format & ValueMask).
+ ///
+ public static EventHeaderFieldFormat WithoutFlags(this EventHeaderFieldFormat format) =>
+ format & EventHeaderFieldFormat.ValueMask;
+
+ ///
+ /// Returns true if ChainFlag is present (tag present in event).
+ ///
+ public static bool HasChainFlag(this EventHeaderFieldFormat format) =>
+ 0 != (format & EventHeaderFieldFormat.ChainFlag);
+ }
+}
diff --git a/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Types/EventHeaderFlags.cs b/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Types/EventHeaderFlags.cs
new file mode 100644
index 0000000000..af554483c9
--- /dev/null
+++ b/src/OpenTelemetry.Exporter.Geneva/External/LinuxTracepoints-Net/Types/EventHeaderFlags.cs
@@ -0,0 +1,36 @@
+#nullable enable
+
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+#pragma warning disable CA1711 // Identifiers should not have incorrect suffix
+
+namespace Microsoft.LinuxTracepoints
+{
+ ///
+ /// Values for EventHeader.Flags.
+ ///
+ [System.Flags]
+ internal enum EventHeaderFlags : byte
+ {
+ ///
+ /// Pointer32, BigEndian, no extensions.
+ ///
+ None = 0x00,
+
+ ///
+ /// Pointer is 64 bits, not 32 bits.
+ ///
+ Pointer64 = 0x01,
+
+ ///
+ /// Event uses little-endian, not big-endian.
+ ///
+ LittleEndian = 0x02,
+
+ ///
+ /// There is at least one EventHeaderExtension block.
+ ///
+ Extension = 0x04,
+ }
+}
diff --git a/src/OpenTelemetry.Exporter.Geneva/External/TraceLoggingDynamic.cs b/src/OpenTelemetry.Exporter.Geneva/External/TraceLoggingDynamic.cs
index 481e41c981..e2cea7bb1f 100644
--- a/src/OpenTelemetry.Exporter.Geneva/External/TraceLoggingDynamic.cs
+++ b/src/OpenTelemetry.Exporter.Geneva/External/TraceLoggingDynamic.cs
@@ -1,5 +1,3 @@
-// (Turns off StyleCop analysis in this file.)
-
/*
Dynamic ETW TraceLogging Provider API for .NET.
diff --git a/src/OpenTelemetry.Exporter.Geneva/OpenTelemetry.Exporter.Geneva.csproj b/src/OpenTelemetry.Exporter.Geneva/OpenTelemetry.Exporter.Geneva.csproj
index 8fb1cffd7a..bc27891120 100644
--- a/src/OpenTelemetry.Exporter.Geneva/OpenTelemetry.Exporter.Geneva.csproj
+++ b/src/OpenTelemetry.Exporter.Geneva/OpenTelemetry.Exporter.Geneva.csproj
@@ -20,6 +20,11 @@
+
+
+
+
+
diff --git a/src/OpenTelemetry.Exporter.Geneva/THIRD-PARTY-NOTICES.TXT b/src/OpenTelemetry.Exporter.Geneva/THIRD-PARTY-NOTICES.TXT
new file mode 100644
index 0000000000..3d74bb0bd8
--- /dev/null
+++ b/src/OpenTelemetry.Exporter.Geneva/THIRD-PARTY-NOTICES.TXT
@@ -0,0 +1,30 @@
+OpenTelemetry.Exporter.Geneva uses third-party libraries or other resources that
+may be distributed under licenses different than the
+OpenTelemetry.Exporter.Geneva software.
+
+The attached notices are provided for information only.
+
+License notice for LinuxTracepoints-Net
+-------------------------------
+
+MIT License
+
+Copyright (c) 2024 Microsoft
+
+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.