diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json new file mode 100644 index 0000000..fe4f0e9 --- /dev/null +++ b/.config/dotnet-tools.json @@ -0,0 +1,13 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-t4": { + "version": "3.0.0", + "commands": [ + "t4" + ], + "rollForward": false + } + } +} diff --git a/DynamicProbes.sln b/DynamicProbes.sln index 234efdb..238059e 100644 --- a/DynamicProbes.sln +++ b/DynamicProbes.sln @@ -16,6 +16,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmark", "benchmark\Benchmark.csproj", "{2F9E49F9-1B8C-4170-8FF1-313BEF57D91C}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests", "tests\UnitTests\UnitTests.csproj", "{C530FC8B-FA78-40E7-881E-3EDF52EEB59E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DynamicProbesDemo", "eg\DynamicProbesDemo.csproj", "{D11BF120-04A6-434D-A19E-5CD19D6ABFD6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -30,6 +34,14 @@ Global {2F9E49F9-1B8C-4170-8FF1-313BEF57D91C}.Debug|Any CPU.Build.0 = Debug|Any CPU {2F9E49F9-1B8C-4170-8FF1-313BEF57D91C}.Release|Any CPU.ActiveCfg = Release|Any CPU {2F9E49F9-1B8C-4170-8FF1-313BEF57D91C}.Release|Any CPU.Build.0 = Release|Any CPU + {C530FC8B-FA78-40E7-881E-3EDF52EEB59E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C530FC8B-FA78-40E7-881E-3EDF52EEB59E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C530FC8B-FA78-40E7-881E-3EDF52EEB59E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C530FC8B-FA78-40E7-881E-3EDF52EEB59E}.Release|Any CPU.Build.0 = Release|Any CPU + {D11BF120-04A6-434D-A19E-5CD19D6ABFD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D11BF120-04A6-434D-A19E-5CD19D6ABFD6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D11BF120-04A6-434D-A19E-5CD19D6ABFD6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D11BF120-04A6-434D-A19E-5CD19D6ABFD6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/benchmark/Program.cs b/benchmark/Program.cs index a6b0c9a..62adf63 100644 --- a/benchmark/Program.cs +++ b/benchmark/Program.cs @@ -1,7 +1,7 @@ using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Running; -using LibstapsdtPinvokes; +using DynamicProbes; namespace Benchmark; @@ -10,32 +10,39 @@ namespace Benchmark; [SimpleJob(RuntimeMoniker.NativeAot80)] public class UnobservedProbeFireBenchmarks { - nint provider; - nint probe1; - nint probe2; - nint probe3; - nint probe4; - nint probe5; - nint probe6; + ILoadedProvider? provider; + Probe probe1; + Probe probe2; + Probe probe3; + Probe probe4; + Probe probe5; + Probe probe6; [GlobalSetup] public void GlobalSetup() { - this.provider = Libstapsdt.ProviderInit("myprovider"); - this.probe1 = Libstapsdt.ProviderAddProbe(this.provider, "myprobe1", ArgType.Int64); - this.probe2 = Libstapsdt.ProviderAddProbe(this.provider, "myprobe2", ArgType.Int64, ArgType.Int64); - this.probe3 = Libstapsdt.ProviderAddProbe(this.provider, "myprobe3", ArgType.Int64, ArgType.Int64, ArgType.Int64); - this.probe4 = Libstapsdt.ProviderAddProbe(this.provider, "myprobe4", ArgType.Int64, ArgType.Int64, ArgType.Int64, ArgType.Int64); - this.probe5 = Libstapsdt.ProviderAddProbe(this.provider, "myprobe5", ArgType.Int64, ArgType.Int64, ArgType.Int64, ArgType.Int64, ArgType.Int64); - this.probe6 = Libstapsdt.ProviderAddProbe(this.provider, "myprobe6", ArgType.Int64, ArgType.Int64, ArgType.Int64, ArgType.Int64, ArgType.Int64, ArgType.Int64); - _ = Libstapsdt.ProviderLoad(this.provider); + var provider = Provider.Init("myprovider"); + try + { + this.probe1 = provider.AddProbe("myprobe1"); + this.probe2 = provider.AddProbe("myprobe2"); + this.probe3 = provider.AddProbe("myprobe3"); + this.probe4 = provider.AddProbe("myprobe4"); + this.probe5 = provider.AddProbe("myprobe5"); + this.probe6 = provider.AddProbe("myprobe6"); + this.provider = provider.Load(); + } + catch + { + provider.Dispose(); + throw; + } } [GlobalCleanup] public void GlobalCleanup() { - _ = Libstapsdt.ProviderUnload(this.provider); - Libstapsdt.ProviderDestroy(this.provider); + this.provider?.Dispose(); } const long Arg1 = 1234567890123456789; @@ -47,22 +54,22 @@ public void GlobalCleanup() [Benchmark] - public void ArgCount1() => Libstapsdt.ProbeFire(this.probe1, Arg1); + public void ArgCount1() => this.probe1.Fire(Arg1); [Benchmark] - public void ArgCount2() => Libstapsdt.ProbeFire(this.probe2, Arg1, Arg2); + public void ArgCount2() => this.probe2.Fire(Arg1, Arg2); [Benchmark] - public void ArgCount3() => Libstapsdt.ProbeFire(this.probe3, Arg1, Arg2, Arg3); + public void ArgCount3() => this.probe3.Fire(Arg1, Arg2, Arg3); [Benchmark] - public void ArgCount4() => Libstapsdt.ProbeFire(this.probe4, Arg1, Arg2, Arg3, Arg4); + public void ArgCount4() => this.probe4.Fire(Arg1, Arg2, Arg3, Arg4); [Benchmark] - public void ArgCount5() => Libstapsdt.ProbeFire(this.probe5, Arg1, Arg2, Arg3, Arg4, Arg5); + public void ArgCount5() => this.probe5.Fire(Arg1, Arg2, Arg3, Arg4, Arg5); [Benchmark] - public void ArgCount6() => Libstapsdt.ProbeFire(this.probe6, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6); + public void ArgCount6() => this.probe6.Fire(Arg1, Arg2, Arg3, Arg4, Arg5, Arg6); } public static class Program diff --git a/eg/DynamicProbesDemo.csproj b/eg/DynamicProbesDemo.csproj new file mode 100644 index 0000000..77ec44e --- /dev/null +++ b/eg/DynamicProbesDemo.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + linux-x64 + enable + enable + + + + + + + diff --git a/src/Program.cs b/eg/Program.cs similarity index 65% rename from src/Program.cs rename to eg/Program.cs index 34751a1..17617e0 100644 --- a/src/Program.cs +++ b/eg/Program.cs @@ -3,24 +3,19 @@ using System.Globalization; using System.Runtime.InteropServices; -using LibstapsdtPinvokes; +using DynamicProbes; try { var providerName = "myprovider"; var probeName = "myprobe"; - var provider = Libstapsdt.ProviderInit(providerName); + using var provider = Provider.Init(providerName); - _ = Libstapsdt.ProviderUseMemfd(provider, MemfdOption.Enabled); + // _ = Libstapsdt.ProviderUseMemfd(provider, MemfdOption.Enabled); - var probe = Libstapsdt.ProviderAddProbe(provider, probeName, ArgType.Int64, ArgType.UInt64); - if (probe == IntPtr.Zero) - throw new Exception("Could not initialize the probe"); - - var res = Libstapsdt.ProviderLoad(provider); - if (res != 0) - throw new Exception("Could not load provider"); + var probe = provider.AddProbe(probeName); + _ = provider.Load(); Console.WriteLine("Ready! Trace me with:"); Console.WriteLine($$""" sudo bpftrace -p {{Environment.ProcessId}} -e 'usdt:*:myprovider:myprobe { printf("Fired values: %ld %s\n", arg0, str(arg1)); }'"""); @@ -32,13 +27,13 @@ var isoTimeStringPtr = Marshal.StringToCoTaskMemUTF8(isoTimeString); try { - Libstapsdt.ProbeFire(probe, val, isoTimeStringPtr); + probe.Active?.Fire(val, isoTimeStringPtr); } finally { Marshal.FreeCoTaskMem(isoTimeStringPtr); } - Console.WriteLine("Probe fired! Probe is currently {0}", Libstapsdt.ProbeIsEnabled(probe) ? "watched" : "not watched"); + Console.WriteLine("Probe fired! Probe is currently {0}", probe.IsEnabled ? "watched" : "not watched"); Thread.Sleep(500); } } diff --git a/src/Args.cs b/src/Args.cs new file mode 100644 index 0000000..e397299 --- /dev/null +++ b/src/Args.cs @@ -0,0 +1,137 @@ +namespace DynamicProbes; + +public enum ArgType // NOTE! Keep in sync with Libstapsdt.ArgType! +{ + NoArg = Libstapsdt.ArgType.NoArg, +#pragma warning disable CA1720 // Identifier contains type name (by-design for familiarity) + UInt8 = Libstapsdt.ArgType.UInt8, + Int8 = Libstapsdt.ArgType.Int8, + UInt16 = Libstapsdt.ArgType.UInt16, + Int16 = Libstapsdt.ArgType.Int16, + UInt32 = Libstapsdt.ArgType.UInt32, + Int32 = Libstapsdt.ArgType.Int32, + UInt64 = Libstapsdt.ArgType.UInt64, + Int64 = Libstapsdt.ArgType.Int64, +#pragma warning disable CA1720 // Identifier contains type name +} + +public interface IArgType +{ + static abstract ArgType ArgType { get; } +} + +public interface IFireArgLong +{ + long UncheckedValue { get; } +} + +#pragma warning disable CA2225 // Operator overloads have named alternates (not needed) + +/// +/// as . +/// +public readonly record struct UInt8Arg(byte Value) : IArgType, IFireArgLong +{ + public static ArgType ArgType => ArgType.UInt8; + public static implicit operator UInt8Arg(byte value) => new(value); + long IFireArgLong.UncheckedValue => Value; + public override string ToString() => $"{Value}"; +} + +/// +/// as . +/// +public readonly record struct Int8Arg(sbyte Value) : IArgType, IFireArgLong +{ + public static ArgType ArgType => ArgType.Int8; + public static implicit operator Int8Arg(sbyte value) => new(value); + long IFireArgLong.UncheckedValue => Value; + public override string ToString() => $"{Value}"; +} + +/// +/// as . +/// +public readonly record struct UInt16Arg(ushort Value) : IArgType, IFireArgLong +{ + public static ArgType ArgType => ArgType.UInt16; + public static implicit operator UInt16Arg(ushort value) => new(value); + long IFireArgLong.UncheckedValue => Value; + public override string ToString() => $"{Value}"; +} + +/// +/// as . +/// +public readonly record struct Int16Arg(short Value) : IArgType, IFireArgLong +{ + public static ArgType ArgType => ArgType.Int16; + public static implicit operator Int16Arg(short value) => new(value); + long IFireArgLong.UncheckedValue => Value; + public override string ToString() => $"{Value}"; +} + +/// +/// as . +/// +public readonly record struct UInt32Arg(uint Value) : IArgType, IFireArgLong +{ + public static ArgType ArgType => ArgType.UInt32; + public static implicit operator UInt32Arg(uint value) => new(value); + long IFireArgLong.UncheckedValue => Value; + public override string ToString() => $"{Value}"; +} + +/// +/// as . +/// +public readonly record struct Int32Arg(int Value) : IArgType, IFireArgLong +{ + public static ArgType ArgType => ArgType.Int32; + public static implicit operator Int32Arg(int value) => new(value); + long IFireArgLong.UncheckedValue => Value; + public override string ToString() => $"{Value}"; +} + +/// +/// as . +/// +public readonly record struct UInt64Arg(ulong Value) : IArgType, IFireArgLong +{ + public static ArgType ArgType => ArgType.UInt64; + public static implicit operator UInt64Arg(ulong value) => new(value); + long IFireArgLong.UncheckedValue => unchecked((long)Value); + public override string ToString() => $"{Value}"; +} + +/// +/// as . +/// +public readonly record struct Int64Arg(long Value) : IArgType, IFireArgLong +{ + public static ArgType ArgType => ArgType.Int64; + public static implicit operator Int64Arg(long value) => new(value); + long IFireArgLong.UncheckedValue => Value; + public override string ToString() => $"{Value}"; +} + +/// +/// as an argument of type . +/// +public readonly record struct IntPtrArg(nint Value) : IArgType, IFireArgLong +{ + public static ArgType ArgType => ArgType.UInt64; + public static implicit operator IntPtrArg(nint value) => new(value); + long IFireArgLong.UncheckedValue => Value; +} + +/// +/// as an argument of type . +/// +public readonly record struct BoolArg(bool Value) : IArgType, IFireArgLong +{ + public static ArgType ArgType => ArgType.Int32; + public static implicit operator BoolArg(bool value) => new(value); + long IFireArgLong.UncheckedValue => Value ? 1 : 0; + public override string ToString() => Value.ToString(); +} diff --git a/src/DynamicProbes.csproj b/src/DynamicProbes.csproj index 49371cf..f1685d7 100644 --- a/src/DynamicProbes.csproj +++ b/src/DynamicProbes.csproj @@ -1,7 +1,6 @@ - Exe net8.0 linux-x64 enable @@ -21,4 +20,35 @@ + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + TextTemplatingFileGenerator + Probes.g.cs + + + + + + + + + + True + True + Probes.g.tt + + + + + + + + diff --git a/src/Generated/net8.0/Microsoft.Interop.LibraryImportGenerator/Microsoft.Interop.LibraryImportGenerator/LibraryImports.g.cs b/src/Generated/net8.0/Microsoft.Interop.LibraryImportGenerator/Microsoft.Interop.LibraryImportGenerator/LibraryImports.g.cs index 41c142b..39d1dfc 100644 --- a/src/Generated/net8.0/Microsoft.Interop.LibraryImportGenerator/Microsoft.Interop.LibraryImportGenerator/LibraryImports.g.cs +++ b/src/Generated/net8.0/Microsoft.Interop.LibraryImportGenerator/Microsoft.Interop.LibraryImportGenerator/LibraryImports.g.cs @@ -1,15 +1,18 @@ // -namespace LibstapsdtPinvokes +namespace Libstapsdt { - public static unsafe partial class Libstapsdt + static unsafe partial class Libstapsdt { [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Interop.LibraryImportGenerator", "8.0.10.36612")] [global::System.Runtime.CompilerServices.SkipLocalsInitAttribute] - public static partial nint ProviderInit(string name) + public static partial global::DynamicProbes.Provider ProviderInit(string name) { + bool __invokeSucceeded = default; byte* __name_native = default; - nint __retVal = default; + global::DynamicProbes.Provider __retVal = default; + nint __retVal_native = default; // Setup - Perform required setup. + global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller.ManagedToUnmanagedOut __retVal_native__marshaller = new(); scoped global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn __name_native__marshaller = new(); try { @@ -18,11 +21,23 @@ public static partial nint ProviderInit(string name) { // PinnedMarshal - Convert managed data to native data that requires the managed data to be pinned. __name_native = __name_native__marshaller.ToUnmanaged(); - __retVal = __PInvoke(__name_native); + __retVal_native = __PInvoke(__name_native); } + + __invokeSucceeded = true; + // UnmarshalCapture - Capture the native data into marshaller instances in case conversion to managed data throws an exception. + __retVal_native__marshaller.FromUnmanaged(__retVal_native); + // Unmarshal - Convert native data to managed data. + __retVal = __retVal_native__marshaller.ToManaged(); } finally { + if (__invokeSucceeded) + { + // CleanupCalleeAllocated - Perform cleanup of callee allocated resources. + __retVal_native__marshaller.Free(); + } + // CleanupCallerAllocated - Perform cleanup of caller allocated resources. __name_native__marshaller.Free(); } @@ -35,313 +50,476 @@ public static partial nint ProviderInit(string name) } } } -namespace LibstapsdtPinvokes +namespace Libstapsdt +{ + static unsafe partial class Libstapsdt + { + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Interop.LibraryImportGenerator", "8.0.10.36612")] + [global::System.Runtime.CompilerServices.SkipLocalsInitAttribute] + public static partial int ProviderUseMemfd(global::DynamicProbes.Provider provider, global::Libstapsdt.MemfdOption option) + { + nint __provider_native = default; + int __retVal = default; + // Setup - Perform required setup. + global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller.ManagedToUnmanagedIn __provider_native__marshaller = new(); + try + { + // Marshal - Convert managed data to native data. + __provider_native__marshaller.FromManaged(provider); + { + // PinnedMarshal - Convert managed data to native data that requires the managed data to be pinned. + __provider_native = __provider_native__marshaller.ToUnmanaged(); + __retVal = __PInvoke(__provider_native, option); + } + } + finally + { + // CleanupCallerAllocated - Perform cleanup of caller allocated resources. + __provider_native__marshaller.Free(); + } + + return __retVal; + // Local P/Invoke + [global::System.Runtime.InteropServices.DllImportAttribute("libstapsdt.so.0", EntryPoint = "providerUseMemfd", ExactSpelling = true)] + [global::System.Runtime.InteropServices.UnmanagedCallConvAttribute(CallConvs = new global::System.Type[] { typeof(global::System.Runtime.CompilerServices.CallConvCdecl) })] + static extern unsafe int __PInvoke(nint __provider_native, global::Libstapsdt.MemfdOption __option_native); + } + } +} +namespace Libstapsdt { - public static unsafe partial class Libstapsdt + static unsafe partial class Libstapsdt { - [global::System.Runtime.InteropServices.DllImportAttribute("libstapsdt.so.0", EntryPoint = "providerUseMemfd", ExactSpelling = true)] - public static extern partial int ProviderUseMemfd(nint provider, global::LibstapsdtPinvokes.MemfdOption option); + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Interop.LibraryImportGenerator", "8.0.10.36612")] + [global::System.Runtime.CompilerServices.SkipLocalsInitAttribute] + private static partial nint ProviderAddProbe(global::DynamicProbes.Provider provider, string name, int argCount) + { + nint __provider_native = default; + byte* __name_native = default; + nint __retVal = default; + // Setup - Perform required setup. + scoped global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn __name_native__marshaller = new(); + global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller.ManagedToUnmanagedIn __provider_native__marshaller = new(); + try + { + // Marshal - Convert managed data to native data. + __name_native__marshaller.FromManaged(name, stackalloc byte[global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn.BufferSize]); + __provider_native__marshaller.FromManaged(provider); + { + // PinnedMarshal - Convert managed data to native data that requires the managed data to be pinned. + __name_native = __name_native__marshaller.ToUnmanaged(); + __provider_native = __provider_native__marshaller.ToUnmanaged(); + __retVal = __PInvoke(__provider_native, __name_native, argCount); + } + } + finally + { + // CleanupCallerAllocated - Perform cleanup of caller allocated resources. + __name_native__marshaller.Free(); + __provider_native__marshaller.Free(); + } + + return __retVal; + // Local P/Invoke + [global::System.Runtime.InteropServices.DllImportAttribute("libstapsdt.so.0", EntryPoint = "providerAddProbe", ExactSpelling = true)] + [global::System.Runtime.InteropServices.UnmanagedCallConvAttribute(CallConvs = new global::System.Type[] { typeof(global::System.Runtime.CompilerServices.CallConvCdecl) })] + static extern unsafe nint __PInvoke(nint __provider_native, byte* __name_native, int __argCount_native); + } } } -namespace LibstapsdtPinvokes +namespace Libstapsdt { - public static unsafe partial class Libstapsdt + static unsafe partial class Libstapsdt { [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Interop.LibraryImportGenerator", "8.0.10.36612")] [global::System.Runtime.CompilerServices.SkipLocalsInitAttribute] - private static partial nint ProviderAddProbe(nint provider, string name, int argCount, global::LibstapsdtPinvokes.ArgType arg1) + private static partial nint ProviderAddProbe(global::DynamicProbes.Provider provider, string name, int argCount, global::Libstapsdt.ArgType arg1) { + nint __provider_native = default; byte* __name_native = default; nint __retVal = default; // Setup - Perform required setup. scoped global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn __name_native__marshaller = new(); + global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller.ManagedToUnmanagedIn __provider_native__marshaller = new(); try { // Marshal - Convert managed data to native data. __name_native__marshaller.FromManaged(name, stackalloc byte[global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn.BufferSize]); + __provider_native__marshaller.FromManaged(provider); { // PinnedMarshal - Convert managed data to native data that requires the managed data to be pinned. __name_native = __name_native__marshaller.ToUnmanaged(); - __retVal = __PInvoke(provider, __name_native, argCount, arg1); + __provider_native = __provider_native__marshaller.ToUnmanaged(); + __retVal = __PInvoke(__provider_native, __name_native, argCount, arg1); } } finally { // CleanupCallerAllocated - Perform cleanup of caller allocated resources. __name_native__marshaller.Free(); + __provider_native__marshaller.Free(); } return __retVal; // Local P/Invoke [global::System.Runtime.InteropServices.DllImportAttribute("libstapsdt.so.0", EntryPoint = "providerAddProbe", ExactSpelling = true)] [global::System.Runtime.InteropServices.UnmanagedCallConvAttribute(CallConvs = new global::System.Type[] { typeof(global::System.Runtime.CompilerServices.CallConvCdecl) })] - static extern unsafe nint __PInvoke(nint __provider_native, byte* __name_native, int __argCount_native, global::LibstapsdtPinvokes.ArgType __arg1_native); + static extern unsafe nint __PInvoke(nint __provider_native, byte* __name_native, int __argCount_native, global::Libstapsdt.ArgType __arg1_native); } } } -namespace LibstapsdtPinvokes +namespace Libstapsdt { - public static unsafe partial class Libstapsdt + static unsafe partial class Libstapsdt { [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Interop.LibraryImportGenerator", "8.0.10.36612")] [global::System.Runtime.CompilerServices.SkipLocalsInitAttribute] - private static partial nint ProviderAddProbe(nint provider, string name, int argCount, global::LibstapsdtPinvokes.ArgType arg1, global::LibstapsdtPinvokes.ArgType arg2) + private static partial nint ProviderAddProbe(global::DynamicProbes.Provider provider, string name, int argCount, global::Libstapsdt.ArgType arg1, global::Libstapsdt.ArgType arg2) { + nint __provider_native = default; byte* __name_native = default; nint __retVal = default; // Setup - Perform required setup. scoped global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn __name_native__marshaller = new(); + global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller.ManagedToUnmanagedIn __provider_native__marshaller = new(); try { // Marshal - Convert managed data to native data. __name_native__marshaller.FromManaged(name, stackalloc byte[global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn.BufferSize]); + __provider_native__marshaller.FromManaged(provider); { // PinnedMarshal - Convert managed data to native data that requires the managed data to be pinned. __name_native = __name_native__marshaller.ToUnmanaged(); - __retVal = __PInvoke(provider, __name_native, argCount, arg1, arg2); + __provider_native = __provider_native__marshaller.ToUnmanaged(); + __retVal = __PInvoke(__provider_native, __name_native, argCount, arg1, arg2); } } finally { // CleanupCallerAllocated - Perform cleanup of caller allocated resources. __name_native__marshaller.Free(); + __provider_native__marshaller.Free(); } return __retVal; // Local P/Invoke [global::System.Runtime.InteropServices.DllImportAttribute("libstapsdt.so.0", EntryPoint = "providerAddProbe", ExactSpelling = true)] [global::System.Runtime.InteropServices.UnmanagedCallConvAttribute(CallConvs = new global::System.Type[] { typeof(global::System.Runtime.CompilerServices.CallConvCdecl) })] - static extern unsafe nint __PInvoke(nint __provider_native, byte* __name_native, int __argCount_native, global::LibstapsdtPinvokes.ArgType __arg1_native, global::LibstapsdtPinvokes.ArgType __arg2_native); + static extern unsafe nint __PInvoke(nint __provider_native, byte* __name_native, int __argCount_native, global::Libstapsdt.ArgType __arg1_native, global::Libstapsdt.ArgType __arg2_native); } } } -namespace LibstapsdtPinvokes +namespace Libstapsdt { - public static unsafe partial class Libstapsdt + static unsafe partial class Libstapsdt { [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Interop.LibraryImportGenerator", "8.0.10.36612")] [global::System.Runtime.CompilerServices.SkipLocalsInitAttribute] - private static partial nint ProviderAddProbe(nint provider, string name, int argCount, global::LibstapsdtPinvokes.ArgType arg1, global::LibstapsdtPinvokes.ArgType arg2, global::LibstapsdtPinvokes.ArgType arg3) + private static partial nint ProviderAddProbe(global::DynamicProbes.Provider provider, string name, int argCount, global::Libstapsdt.ArgType arg1, global::Libstapsdt.ArgType arg2, global::Libstapsdt.ArgType arg3) { + nint __provider_native = default; byte* __name_native = default; nint __retVal = default; // Setup - Perform required setup. scoped global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn __name_native__marshaller = new(); + global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller.ManagedToUnmanagedIn __provider_native__marshaller = new(); try { // Marshal - Convert managed data to native data. __name_native__marshaller.FromManaged(name, stackalloc byte[global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn.BufferSize]); + __provider_native__marshaller.FromManaged(provider); { // PinnedMarshal - Convert managed data to native data that requires the managed data to be pinned. __name_native = __name_native__marshaller.ToUnmanaged(); - __retVal = __PInvoke(provider, __name_native, argCount, arg1, arg2, arg3); + __provider_native = __provider_native__marshaller.ToUnmanaged(); + __retVal = __PInvoke(__provider_native, __name_native, argCount, arg1, arg2, arg3); } } finally { // CleanupCallerAllocated - Perform cleanup of caller allocated resources. __name_native__marshaller.Free(); + __provider_native__marshaller.Free(); } return __retVal; // Local P/Invoke [global::System.Runtime.InteropServices.DllImportAttribute("libstapsdt.so.0", EntryPoint = "providerAddProbe", ExactSpelling = true)] [global::System.Runtime.InteropServices.UnmanagedCallConvAttribute(CallConvs = new global::System.Type[] { typeof(global::System.Runtime.CompilerServices.CallConvCdecl) })] - static extern unsafe nint __PInvoke(nint __provider_native, byte* __name_native, int __argCount_native, global::LibstapsdtPinvokes.ArgType __arg1_native, global::LibstapsdtPinvokes.ArgType __arg2_native, global::LibstapsdtPinvokes.ArgType __arg3_native); + static extern unsafe nint __PInvoke(nint __provider_native, byte* __name_native, int __argCount_native, global::Libstapsdt.ArgType __arg1_native, global::Libstapsdt.ArgType __arg2_native, global::Libstapsdt.ArgType __arg3_native); } } } -namespace LibstapsdtPinvokes +namespace Libstapsdt { - public static unsafe partial class Libstapsdt + static unsafe partial class Libstapsdt { [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Interop.LibraryImportGenerator", "8.0.10.36612")] [global::System.Runtime.CompilerServices.SkipLocalsInitAttribute] - private static partial nint ProviderAddProbe(nint provider, string name, int argCount, global::LibstapsdtPinvokes.ArgType arg1, global::LibstapsdtPinvokes.ArgType arg2, global::LibstapsdtPinvokes.ArgType arg3, global::LibstapsdtPinvokes.ArgType arg4) + private static partial nint ProviderAddProbe(global::DynamicProbes.Provider provider, string name, int argCount, global::Libstapsdt.ArgType arg1, global::Libstapsdt.ArgType arg2, global::Libstapsdt.ArgType arg3, global::Libstapsdt.ArgType arg4) { + nint __provider_native = default; byte* __name_native = default; nint __retVal = default; // Setup - Perform required setup. scoped global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn __name_native__marshaller = new(); + global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller.ManagedToUnmanagedIn __provider_native__marshaller = new(); try { // Marshal - Convert managed data to native data. __name_native__marshaller.FromManaged(name, stackalloc byte[global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn.BufferSize]); + __provider_native__marshaller.FromManaged(provider); { // PinnedMarshal - Convert managed data to native data that requires the managed data to be pinned. __name_native = __name_native__marshaller.ToUnmanaged(); - __retVal = __PInvoke(provider, __name_native, argCount, arg1, arg2, arg3, arg4); + __provider_native = __provider_native__marshaller.ToUnmanaged(); + __retVal = __PInvoke(__provider_native, __name_native, argCount, arg1, arg2, arg3, arg4); } } finally { // CleanupCallerAllocated - Perform cleanup of caller allocated resources. __name_native__marshaller.Free(); + __provider_native__marshaller.Free(); } return __retVal; // Local P/Invoke [global::System.Runtime.InteropServices.DllImportAttribute("libstapsdt.so.0", EntryPoint = "providerAddProbe", ExactSpelling = true)] [global::System.Runtime.InteropServices.UnmanagedCallConvAttribute(CallConvs = new global::System.Type[] { typeof(global::System.Runtime.CompilerServices.CallConvCdecl) })] - static extern unsafe nint __PInvoke(nint __provider_native, byte* __name_native, int __argCount_native, global::LibstapsdtPinvokes.ArgType __arg1_native, global::LibstapsdtPinvokes.ArgType __arg2_native, global::LibstapsdtPinvokes.ArgType __arg3_native, global::LibstapsdtPinvokes.ArgType __arg4_native); + static extern unsafe nint __PInvoke(nint __provider_native, byte* __name_native, int __argCount_native, global::Libstapsdt.ArgType __arg1_native, global::Libstapsdt.ArgType __arg2_native, global::Libstapsdt.ArgType __arg3_native, global::Libstapsdt.ArgType __arg4_native); } } } -namespace LibstapsdtPinvokes +namespace Libstapsdt { - public static unsafe partial class Libstapsdt + static unsafe partial class Libstapsdt { [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Interop.LibraryImportGenerator", "8.0.10.36612")] [global::System.Runtime.CompilerServices.SkipLocalsInitAttribute] - private static partial nint ProviderAddProbe(nint provider, string name, int argCount, global::LibstapsdtPinvokes.ArgType arg1, global::LibstapsdtPinvokes.ArgType arg2, global::LibstapsdtPinvokes.ArgType arg3, global::LibstapsdtPinvokes.ArgType arg4, global::LibstapsdtPinvokes.ArgType arg5) + private static partial nint ProviderAddProbe(global::DynamicProbes.Provider provider, string name, int argCount, global::Libstapsdt.ArgType arg1, global::Libstapsdt.ArgType arg2, global::Libstapsdt.ArgType arg3, global::Libstapsdt.ArgType arg4, global::Libstapsdt.ArgType arg5) { + nint __provider_native = default; byte* __name_native = default; nint __retVal = default; // Setup - Perform required setup. scoped global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn __name_native__marshaller = new(); + global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller.ManagedToUnmanagedIn __provider_native__marshaller = new(); try { // Marshal - Convert managed data to native data. __name_native__marshaller.FromManaged(name, stackalloc byte[global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn.BufferSize]); + __provider_native__marshaller.FromManaged(provider); { // PinnedMarshal - Convert managed data to native data that requires the managed data to be pinned. __name_native = __name_native__marshaller.ToUnmanaged(); - __retVal = __PInvoke(provider, __name_native, argCount, arg1, arg2, arg3, arg4, arg5); + __provider_native = __provider_native__marshaller.ToUnmanaged(); + __retVal = __PInvoke(__provider_native, __name_native, argCount, arg1, arg2, arg3, arg4, arg5); } } finally { // CleanupCallerAllocated - Perform cleanup of caller allocated resources. __name_native__marshaller.Free(); + __provider_native__marshaller.Free(); } return __retVal; // Local P/Invoke [global::System.Runtime.InteropServices.DllImportAttribute("libstapsdt.so.0", EntryPoint = "providerAddProbe", ExactSpelling = true)] [global::System.Runtime.InteropServices.UnmanagedCallConvAttribute(CallConvs = new global::System.Type[] { typeof(global::System.Runtime.CompilerServices.CallConvCdecl) })] - static extern unsafe nint __PInvoke(nint __provider_native, byte* __name_native, int __argCount_native, global::LibstapsdtPinvokes.ArgType __arg1_native, global::LibstapsdtPinvokes.ArgType __arg2_native, global::LibstapsdtPinvokes.ArgType __arg3_native, global::LibstapsdtPinvokes.ArgType __arg4_native, global::LibstapsdtPinvokes.ArgType __arg5_native); + static extern unsafe nint __PInvoke(nint __provider_native, byte* __name_native, int __argCount_native, global::Libstapsdt.ArgType __arg1_native, global::Libstapsdt.ArgType __arg2_native, global::Libstapsdt.ArgType __arg3_native, global::Libstapsdt.ArgType __arg4_native, global::Libstapsdt.ArgType __arg5_native); } } } -namespace LibstapsdtPinvokes +namespace Libstapsdt { - public static unsafe partial class Libstapsdt + static unsafe partial class Libstapsdt { [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Interop.LibraryImportGenerator", "8.0.10.36612")] [global::System.Runtime.CompilerServices.SkipLocalsInitAttribute] - private static partial nint ProviderAddProbe(nint provider, string name, int argCount, global::LibstapsdtPinvokes.ArgType arg1, global::LibstapsdtPinvokes.ArgType arg2, global::LibstapsdtPinvokes.ArgType arg3, global::LibstapsdtPinvokes.ArgType arg4, global::LibstapsdtPinvokes.ArgType arg5, global::LibstapsdtPinvokes.ArgType arg6) + private static partial nint ProviderAddProbe(global::DynamicProbes.Provider provider, string name, int argCount, global::Libstapsdt.ArgType arg1, global::Libstapsdt.ArgType arg2, global::Libstapsdt.ArgType arg3, global::Libstapsdt.ArgType arg4, global::Libstapsdt.ArgType arg5, global::Libstapsdt.ArgType arg6) { + nint __provider_native = default; byte* __name_native = default; nint __retVal = default; // Setup - Perform required setup. scoped global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn __name_native__marshaller = new(); + global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller.ManagedToUnmanagedIn __provider_native__marshaller = new(); try { // Marshal - Convert managed data to native data. __name_native__marshaller.FromManaged(name, stackalloc byte[global::System.Runtime.InteropServices.Marshalling.Utf8StringMarshaller.ManagedToUnmanagedIn.BufferSize]); + __provider_native__marshaller.FromManaged(provider); { // PinnedMarshal - Convert managed data to native data that requires the managed data to be pinned. __name_native = __name_native__marshaller.ToUnmanaged(); - __retVal = __PInvoke(provider, __name_native, argCount, arg1, arg2, arg3, arg4, arg5, arg6); + __provider_native = __provider_native__marshaller.ToUnmanaged(); + __retVal = __PInvoke(__provider_native, __name_native, argCount, arg1, arg2, arg3, arg4, arg5, arg6); } } finally { // CleanupCallerAllocated - Perform cleanup of caller allocated resources. __name_native__marshaller.Free(); + __provider_native__marshaller.Free(); } return __retVal; // Local P/Invoke [global::System.Runtime.InteropServices.DllImportAttribute("libstapsdt.so.0", EntryPoint = "providerAddProbe", ExactSpelling = true)] [global::System.Runtime.InteropServices.UnmanagedCallConvAttribute(CallConvs = new global::System.Type[] { typeof(global::System.Runtime.CompilerServices.CallConvCdecl) })] - static extern unsafe nint __PInvoke(nint __provider_native, byte* __name_native, int __argCount_native, global::LibstapsdtPinvokes.ArgType __arg1_native, global::LibstapsdtPinvokes.ArgType __arg2_native, global::LibstapsdtPinvokes.ArgType __arg3_native, global::LibstapsdtPinvokes.ArgType __arg4_native, global::LibstapsdtPinvokes.ArgType __arg5_native, global::LibstapsdtPinvokes.ArgType __arg6_native); + static extern unsafe nint __PInvoke(nint __provider_native, byte* __name_native, int __argCount_native, global::Libstapsdt.ArgType __arg1_native, global::Libstapsdt.ArgType __arg2_native, global::Libstapsdt.ArgType __arg3_native, global::Libstapsdt.ArgType __arg4_native, global::Libstapsdt.ArgType __arg5_native, global::Libstapsdt.ArgType __arg6_native); } } } -namespace LibstapsdtPinvokes +namespace Libstapsdt { - public static unsafe partial class Libstapsdt + static unsafe partial class Libstapsdt { - [global::System.Runtime.InteropServices.DllImportAttribute("libstapsdt.so.0", EntryPoint = "providerLoad", ExactSpelling = true)] - public static extern partial int ProviderLoad(nint provider); + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Interop.LibraryImportGenerator", "8.0.10.36612")] + [global::System.Runtime.CompilerServices.SkipLocalsInitAttribute] + public static partial int ProviderLoad(global::DynamicProbes.Provider provider) + { + nint __provider_native = default; + int __retVal = default; + // Setup - Perform required setup. + global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller.ManagedToUnmanagedIn __provider_native__marshaller = new(); + try + { + // Marshal - Convert managed data to native data. + __provider_native__marshaller.FromManaged(provider); + { + // PinnedMarshal - Convert managed data to native data that requires the managed data to be pinned. + __provider_native = __provider_native__marshaller.ToUnmanaged(); + __retVal = __PInvoke(__provider_native); + } + } + finally + { + // CleanupCallerAllocated - Perform cleanup of caller allocated resources. + __provider_native__marshaller.Free(); + } + + return __retVal; + // Local P/Invoke + [global::System.Runtime.InteropServices.DllImportAttribute("libstapsdt.so.0", EntryPoint = "providerLoad", ExactSpelling = true)] + [global::System.Runtime.InteropServices.UnmanagedCallConvAttribute(CallConvs = new global::System.Type[] { typeof(global::System.Runtime.CompilerServices.CallConvCdecl) })] + static extern unsafe int __PInvoke(nint __provider_native); + } } } -namespace LibstapsdtPinvokes +namespace Libstapsdt { - public static unsafe partial class Libstapsdt + static unsafe partial class Libstapsdt { [global::System.Runtime.InteropServices.DllImportAttribute("libstapsdt.so.0", EntryPoint = "providerUnload", ExactSpelling = true)] public static extern partial int ProviderUnload(nint provider); } } -namespace LibstapsdtPinvokes +namespace Libstapsdt +{ + static unsafe partial class Libstapsdt + { + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Interop.LibraryImportGenerator", "8.0.10.36612")] + [global::System.Runtime.CompilerServices.SkipLocalsInitAttribute] + public static partial int ProviderUnload(global::DynamicProbes.Provider provider) + { + nint __provider_native = default; + int __retVal = default; + // Setup - Perform required setup. + global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller.ManagedToUnmanagedIn __provider_native__marshaller = new(); + try + { + // Marshal - Convert managed data to native data. + __provider_native__marshaller.FromManaged(provider); + { + // PinnedMarshal - Convert managed data to native data that requires the managed data to be pinned. + __provider_native = __provider_native__marshaller.ToUnmanaged(); + __retVal = __PInvoke(__provider_native); + } + } + finally + { + // CleanupCallerAllocated - Perform cleanup of caller allocated resources. + __provider_native__marshaller.Free(); + } + + return __retVal; + // Local P/Invoke + [global::System.Runtime.InteropServices.DllImportAttribute("libstapsdt.so.0", EntryPoint = "providerUnload", ExactSpelling = true)] + [global::System.Runtime.InteropServices.UnmanagedCallConvAttribute(CallConvs = new global::System.Type[] { typeof(global::System.Runtime.CompilerServices.CallConvCdecl) })] + static extern unsafe int __PInvoke(nint __provider_native); + } + } +} +namespace Libstapsdt { - public static unsafe partial class Libstapsdt + static unsafe partial class Libstapsdt { [global::System.Runtime.InteropServices.DllImportAttribute("libstapsdt.so.0", EntryPoint = "providerDestroy", ExactSpelling = true)] public static extern partial void ProviderDestroy(nint provider); } } -namespace LibstapsdtPinvokes +namespace Libstapsdt { - public static unsafe partial class Libstapsdt + static unsafe partial class Libstapsdt { [global::System.Runtime.InteropServices.DllImportAttribute("libstapsdt.so.0", EntryPoint = "probeFire", ExactSpelling = true)] public static extern partial void ProbeFire(nint probe); } } -namespace LibstapsdtPinvokes +namespace Libstapsdt { - public static unsafe partial class Libstapsdt + static unsafe partial class Libstapsdt { [global::System.Runtime.InteropServices.DllImportAttribute("libstapsdt.so.0", EntryPoint = "probeFire", ExactSpelling = true)] public static extern partial void ProbeFire(nint probe, long arg1); } } -namespace LibstapsdtPinvokes +namespace Libstapsdt { - public static unsafe partial class Libstapsdt + static unsafe partial class Libstapsdt { [global::System.Runtime.InteropServices.DllImportAttribute("libstapsdt.so.0", EntryPoint = "probeFire", ExactSpelling = true)] public static extern partial void ProbeFire(nint probe, long arg1, long arg2); } } -namespace LibstapsdtPinvokes +namespace Libstapsdt { - public static unsafe partial class Libstapsdt + static unsafe partial class Libstapsdt { [global::System.Runtime.InteropServices.DllImportAttribute("libstapsdt.so.0", EntryPoint = "probeFire", ExactSpelling = true)] public static extern partial void ProbeFire(nint probe, long arg1, long arg2, long arg3); } } -namespace LibstapsdtPinvokes +namespace Libstapsdt { - public static unsafe partial class Libstapsdt + static unsafe partial class Libstapsdt { [global::System.Runtime.InteropServices.DllImportAttribute("libstapsdt.so.0", EntryPoint = "probeFire", ExactSpelling = true)] public static extern partial void ProbeFire(nint probe, long arg1, long arg2, long arg3, long arg4); } } -namespace LibstapsdtPinvokes +namespace Libstapsdt { - public static unsafe partial class Libstapsdt + static unsafe partial class Libstapsdt { [global::System.Runtime.InteropServices.DllImportAttribute("libstapsdt.so.0", EntryPoint = "probeFire", ExactSpelling = true)] public static extern partial void ProbeFire(nint probe, long arg1, long arg2, long arg3, long arg4, long arg5); } } -namespace LibstapsdtPinvokes +namespace Libstapsdt { - public static unsafe partial class Libstapsdt + static unsafe partial class Libstapsdt { [global::System.Runtime.InteropServices.DllImportAttribute("libstapsdt.so.0", EntryPoint = "probeFire", ExactSpelling = true)] public static extern partial void ProbeFire(nint probe, long arg1, long arg2, long arg3, long arg4, long arg5, long arg6); } } -namespace LibstapsdtPinvokes +namespace Libstapsdt { - public static unsafe partial class Libstapsdt + static unsafe partial class Libstapsdt { [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Interop.LibraryImportGenerator", "8.0.10.36612")] [global::System.Runtime.CompilerServices.SkipLocalsInitAttribute] diff --git a/src/Libstapsdt.cs b/src/Libstapsdt.cs index 6327a08..2c98c8f 100644 --- a/src/Libstapsdt.cs +++ b/src/Libstapsdt.cs @@ -1,86 +1,96 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using DynamicProbes; using ProbePtr = nint; -using SdtProviderPtr = nint; #pragma warning disable CA5392 // Use DefaultDllImportSearchPaths attribute for P/Invokes // ...but it has no effect on non-Windows platforms or the Mono runtime. -#pragma warning disable CA1401 // P/Invokes should not be visible - // ...the point of this library is to make P/Invokes visible. - -namespace LibstapsdtPinvokes; +namespace Libstapsdt; // Source: https://github.com/linux-usdt/libstapsdt/blob/0d53f987b0787362fd9c16a93cdad2c273d809fc/src/libstapsdt.h -public static partial class Libstapsdt +#if !NO_NATIVE_CODE + +static partial class Libstapsdt { const string LibstapsdtLibrary = "libstapsdt.so.0"; // P/Invoke function declarations [LibraryImport(LibstapsdtLibrary, EntryPoint = "providerInit", StringMarshalling = StringMarshalling.Utf8)] [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] - public static partial SdtProviderPtr ProviderInit(string name); + public static partial Provider ProviderInit(string name); [LibraryImport(LibstapsdtLibrary, EntryPoint = "providerUseMemfd")] [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] - public static partial int ProviderUseMemfd(SdtProviderPtr provider, MemfdOption option); + public static partial int ProviderUseMemfd(Provider provider, MemfdOption option); // Overloads for providerAddProbe for different argument counts - public static ProbePtr ProviderAddProbe(SdtProviderPtr provider, string name, ArgType arg1) => + public static ProbePtr ProviderAddProbe(Provider provider, string name) => + ProviderAddProbe(provider, name, 0); + + public static ProbePtr ProviderAddProbe(Provider provider, string name, ArgType arg1) => ProviderAddProbe(provider, name, 1, arg1); - public static ProbePtr ProviderAddProbe(SdtProviderPtr provider, string name, ArgType arg1, ArgType arg2) => + public static ProbePtr ProviderAddProbe(Provider provider, string name, ArgType arg1, ArgType arg2) => ProviderAddProbe(provider, name, 2, arg1, arg2); - public static ProbePtr ProviderAddProbe(SdtProviderPtr provider, string name, ArgType arg1, ArgType arg2, ArgType arg3) => + public static ProbePtr ProviderAddProbe(Provider provider, string name, ArgType arg1, ArgType arg2, ArgType arg3) => ProviderAddProbe(provider, name, 3, arg1, arg2, arg3); - public static ProbePtr ProviderAddProbe(SdtProviderPtr provider, string name, ArgType arg1, ArgType arg2, ArgType arg3, ArgType arg4) => + public static ProbePtr ProviderAddProbe(Provider provider, string name, ArgType arg1, ArgType arg2, ArgType arg3, ArgType arg4) => ProviderAddProbe(provider, name, 4, arg1, arg2, arg3, arg4); - public static ProbePtr ProviderAddProbe(SdtProviderPtr provider, string name, ArgType arg1, ArgType arg2, ArgType arg3, ArgType arg4, ArgType arg5) => + public static ProbePtr ProviderAddProbe(Provider provider, string name, ArgType arg1, ArgType arg2, ArgType arg3, ArgType arg4, ArgType arg5) => ProviderAddProbe(provider, name, 5, arg1, arg2, arg3, arg4, arg5); - public static ProbePtr ProviderAddProbe(SdtProviderPtr provider, string name, ArgType arg1, ArgType arg2, ArgType arg3, ArgType arg4, ArgType arg5, ArgType arg6) => + public static ProbePtr ProviderAddProbe(Provider provider, string name, ArgType arg1, ArgType arg2, ArgType arg3, ArgType arg4, ArgType arg5, ArgType arg6) => ProviderAddProbe(provider, name, 6, arg1, arg2, arg3, arg4, arg5, arg6); [LibraryImport(LibstapsdtLibrary, EntryPoint = "providerAddProbe", StringMarshalling = StringMarshalling.Utf8)] [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] - private static partial ProbePtr ProviderAddProbe(SdtProviderPtr provider, string name, int argCount, ArgType arg1); + private static partial ProbePtr ProviderAddProbe(Provider provider, string name, int argCount); + + [LibraryImport(LibstapsdtLibrary, EntryPoint = "providerAddProbe", StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + private static partial ProbePtr ProviderAddProbe(Provider provider, string name, int argCount, ArgType arg1); [LibraryImport(LibstapsdtLibrary, EntryPoint = "providerAddProbe", StringMarshalling = StringMarshalling.Utf8)] [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] - private static partial ProbePtr ProviderAddProbe(SdtProviderPtr provider, string name, int argCount, ArgType arg1, ArgType arg2); + private static partial ProbePtr ProviderAddProbe(Provider provider, string name, int argCount, ArgType arg1, ArgType arg2); [LibraryImport(LibstapsdtLibrary, EntryPoint = "providerAddProbe", StringMarshalling = StringMarshalling.Utf8)] [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] - private static partial ProbePtr ProviderAddProbe(SdtProviderPtr provider, string name, int argCount, ArgType arg1, ArgType arg2, ArgType arg3); + private static partial ProbePtr ProviderAddProbe(Provider provider, string name, int argCount, ArgType arg1, ArgType arg2, ArgType arg3); [LibraryImport(LibstapsdtLibrary, EntryPoint = "providerAddProbe", StringMarshalling = StringMarshalling.Utf8)] [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] - private static partial ProbePtr ProviderAddProbe(SdtProviderPtr provider, string name, int argCount, ArgType arg1, ArgType arg2, ArgType arg3, ArgType arg4); + private static partial ProbePtr ProviderAddProbe(Provider provider, string name, int argCount, ArgType arg1, ArgType arg2, ArgType arg3, ArgType arg4); [LibraryImport(LibstapsdtLibrary, EntryPoint = "providerAddProbe", StringMarshalling = StringMarshalling.Utf8)] [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] - private static partial ProbePtr ProviderAddProbe(SdtProviderPtr provider, string name, int argCount, ArgType arg1, ArgType arg2, ArgType arg3, ArgType arg4, ArgType arg5); + private static partial ProbePtr ProviderAddProbe(Provider provider, string name, int argCount, ArgType arg1, ArgType arg2, ArgType arg3, ArgType arg4, ArgType arg5); [LibraryImport(LibstapsdtLibrary, EntryPoint = "providerAddProbe", StringMarshalling = StringMarshalling.Utf8)] [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] - private static partial ProbePtr ProviderAddProbe(SdtProviderPtr provider, string name, int argCount, ArgType arg1, ArgType arg2, ArgType arg3, ArgType arg4, ArgType arg5, ArgType arg6); + private static partial ProbePtr ProviderAddProbe(Provider provider, string name, int argCount, ArgType arg1, ArgType arg2, ArgType arg3, ArgType arg4, ArgType arg5, ArgType arg6); [LibraryImport(LibstapsdtLibrary, EntryPoint = "providerLoad")] [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] - public static partial int ProviderLoad(SdtProviderPtr provider); // return -1 on error, 0 on success + public static partial int ProviderLoad(Provider provider); // return -1 on error, 0 on success [LibraryImport(LibstapsdtLibrary, EntryPoint = "providerUnload")] [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] - public static partial int ProviderUnload(SdtProviderPtr provider); // return -1 on error, 0 on success + public static partial int ProviderUnload(nint provider); // return -1 on error, 0 on success + + [LibraryImport(LibstapsdtLibrary, EntryPoint = "providerUnload")] + [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] + public static partial int ProviderUnload(Provider provider); // return -1 on error, 0 on success [LibraryImport(LibstapsdtLibrary, EntryPoint = "providerDestroy")] [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] - public static partial void ProviderDestroy(SdtProviderPtr provider); + public static partial void ProviderDestroy(nint provider); // Overloads for probeFire for different argument counts @@ -118,6 +128,8 @@ public static ProbePtr ProviderAddProbe(SdtProviderPtr provider, string name, Ar public static partial bool ProbeIsEnabled(ProbePtr probe); // return 1 if true, 0 if false } +#endif // !NO_NATIVE_CODE + enum SdtError // SDTError_t { NoError = -1, // noError @@ -128,9 +140,7 @@ enum SdtError // SDTError_t SharedLibraryCloseError = 4 // sharedLibraryCloseError } - -#pragma warning disable CA1720 // Identifiers should not contain type names -public enum ArgType // ArgType_t +enum ArgType // ArgType_t { NoArg = 0, // noarg UInt8 = 1, // uint8 @@ -143,9 +153,7 @@ public enum ArgType // ArgType_t Int64 = -8 // int64 } -#pragma warning restore CA1720 // Identifiers should not contain type names - -public enum MemfdOption // MemFDOption_t +enum MemfdOption // MemFDOption_t { Disabled = 0, // memfd_disabled Enabled = 1 // memfd_enabled diff --git a/src/Probes.cs b/src/Probes.cs new file mode 100644 index 0000000..afe66fd --- /dev/null +++ b/src/Probes.cs @@ -0,0 +1,9 @@ +namespace DynamicProbes; + +public readonly partial record struct Probe; +public readonly partial record struct Probe; +public readonly partial record struct Probe; +public readonly partial record struct Probe; +public readonly partial record struct Probe; +public readonly partial record struct Probe; +public readonly partial record struct Probe; diff --git a/src/Probes.g.cs b/src/Probes.g.cs new file mode 100644 index 0000000..f37876b --- /dev/null +++ b/src/Probes.g.cs @@ -0,0 +1,337 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool on and at: +// Thu, 19 Sep 2024 10:27:17 +02:00 +// +// Changes to this file will be lost if the code is re-generated. +// +//------------------------------------------------------------------------------ + +#nullable enable // required for auto-generated sources (see below why) + +// > Older code generation strategies may not be nullable aware. Setting the +// > project-level nullable context to "enable" could result in many +// > warnings that a user is unable to fix. To support this scenario any syntax +// > tree that is determined to be generated will have its nullable state +// > implicitly set to "disable", regardless of the overall project state. +// +// Source: https://github.com/dotnet/roslyn/blob/70e158ba6c2c99bd3c3fc0754af0dbf82a6d353d/docs/features/nullable-reference-types.md#generated-code + +using static Libstapsdt.Libstapsdt; + +namespace DynamicProbes; + +partial record struct Probe +{ + readonly Provider provider; + readonly nint probe; + + internal Probe(Provider provider, string name, IntPtr probe) + { + this.provider = provider; + Name = name; + this.probe = probe; + } + + public string Name { get; } + + bool IsProviderLoaded => this.provider.IsLoaded; + + public bool IsEnabled => IsProviderLoaded && ProbeIsEnabled(this.probe); + + public Probe? Active => IsEnabled ? this : null; + + public void Fire() + { + if (!IsProviderLoaded) + return; + ProbeFire(this.probe); + } + + public override string ToString() => $"{provider}:{Name}"; +} + +partial class Provider +{ + public Probe AddProbe(string name) + { + var @this = This; + return new(@this, name, Libstapsdt.Libstapsdt.ProviderAddProbe(@this, name)); + } +} + +partial record struct Probe + where T : IFireArgLong +{ + readonly Provider provider; + readonly nint probe; + + internal Probe(Provider provider, string name, IntPtr probe) + { + this.provider = provider; + Name = name; + this.probe = probe; + } + + public string Name { get; } + + bool IsProviderLoaded => this.provider.IsLoaded; + + public bool IsEnabled => IsProviderLoaded && ProbeIsEnabled(this.probe); + + public Probe? Active => IsEnabled ? this : null; + + public void Fire(T arg) + { + if (!IsProviderLoaded) + return; + ProbeFire(this.probe, arg.UncheckedValue); + } + + public override string ToString() => $"{provider}:{Name}"; +} + +partial class Provider +{ + public Probe AddProbe(string name) + where T : IArgType, IFireArgLong + { + var @this = This; + return new(@this, name, Libstapsdt.Libstapsdt.ProviderAddProbe(@this, name, (Libstapsdt.ArgType)T.ArgType)); + } +} + +partial record struct Probe + where T1 : IFireArgLong + where T2 : IFireArgLong +{ + readonly Provider provider; + readonly nint probe; + + internal Probe(Provider provider, string name, IntPtr probe) + { + this.provider = provider; + Name = name; + this.probe = probe; + } + + public string Name { get; } + + bool IsProviderLoaded => this.provider.IsLoaded; + + public bool IsEnabled => IsProviderLoaded && ProbeIsEnabled(this.probe); + + public Probe? Active => IsEnabled ? this : null; + + public void Fire(T1 arg1, T2 arg2) + { + if (!IsProviderLoaded) + return; + ProbeFire(this.probe, arg1.UncheckedValue, arg2.UncheckedValue); + } + + public override string ToString() => $"{provider}:{Name}"; +} + +partial class Provider +{ + public Probe AddProbe(string name) + where T1 : IArgType, IFireArgLong + where T2 : IArgType, IFireArgLong + { + var @this = This; + return new(@this, name, Libstapsdt.Libstapsdt.ProviderAddProbe(@this, name, (Libstapsdt.ArgType)T1.ArgType, (Libstapsdt.ArgType)T2.ArgType)); + } +} + +partial record struct Probe + where T1 : IFireArgLong + where T2 : IFireArgLong + where T3 : IFireArgLong +{ + readonly Provider provider; + readonly nint probe; + + internal Probe(Provider provider, string name, IntPtr probe) + { + this.provider = provider; + Name = name; + this.probe = probe; + } + + public string Name { get; } + + bool IsProviderLoaded => this.provider.IsLoaded; + + public bool IsEnabled => IsProviderLoaded && ProbeIsEnabled(this.probe); + + public Probe? Active => IsEnabled ? this : null; + + public void Fire(T1 arg1, T2 arg2, T3 arg3) + { + if (!IsProviderLoaded) + return; + ProbeFire(this.probe, arg1.UncheckedValue, arg2.UncheckedValue, arg3.UncheckedValue); + } + + public override string ToString() => $"{provider}:{Name}"; +} + +partial class Provider +{ + public Probe AddProbe(string name) + where T1 : IArgType, IFireArgLong + where T2 : IArgType, IFireArgLong + where T3 : IArgType, IFireArgLong + { + var @this = This; + return new(@this, name, Libstapsdt.Libstapsdt.ProviderAddProbe(@this, name, (Libstapsdt.ArgType)T1.ArgType, (Libstapsdt.ArgType)T2.ArgType, (Libstapsdt.ArgType)T3.ArgType)); + } +} + +partial record struct Probe + where T1 : IFireArgLong + where T2 : IFireArgLong + where T3 : IFireArgLong + where T4 : IFireArgLong +{ + readonly Provider provider; + readonly nint probe; + + internal Probe(Provider provider, string name, IntPtr probe) + { + this.provider = provider; + Name = name; + this.probe = probe; + } + + public string Name { get; } + + bool IsProviderLoaded => this.provider.IsLoaded; + + public bool IsEnabled => IsProviderLoaded && ProbeIsEnabled(this.probe); + + public Probe? Active => IsEnabled ? this : null; + + public void Fire(T1 arg1, T2 arg2, T3 arg3, T4 arg4) + { + if (!IsProviderLoaded) + return; + ProbeFire(this.probe, arg1.UncheckedValue, arg2.UncheckedValue, arg3.UncheckedValue, arg4.UncheckedValue); + } + + public override string ToString() => $"{provider}:{Name}"; +} + +partial class Provider +{ + public Probe AddProbe(string name) + where T1 : IArgType, IFireArgLong + where T2 : IArgType, IFireArgLong + where T3 : IArgType, IFireArgLong + where T4 : IArgType, IFireArgLong + { + var @this = This; + return new(@this, name, Libstapsdt.Libstapsdt.ProviderAddProbe(@this, name, (Libstapsdt.ArgType)T1.ArgType, (Libstapsdt.ArgType)T2.ArgType, (Libstapsdt.ArgType)T3.ArgType, (Libstapsdt.ArgType)T4.ArgType)); + } +} + +partial record struct Probe + where T1 : IFireArgLong + where T2 : IFireArgLong + where T3 : IFireArgLong + where T4 : IFireArgLong + where T5 : IFireArgLong +{ + readonly Provider provider; + readonly nint probe; + + internal Probe(Provider provider, string name, IntPtr probe) + { + this.provider = provider; + Name = name; + this.probe = probe; + } + + public string Name { get; } + + bool IsProviderLoaded => this.provider.IsLoaded; + + public bool IsEnabled => IsProviderLoaded && ProbeIsEnabled(this.probe); + + public Probe? Active => IsEnabled ? this : null; + + public void Fire(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) + { + if (!IsProviderLoaded) + return; + ProbeFire(this.probe, arg1.UncheckedValue, arg2.UncheckedValue, arg3.UncheckedValue, arg4.UncheckedValue, arg5.UncheckedValue); + } + + public override string ToString() => $"{provider}:{Name}"; +} + +partial class Provider +{ + public Probe AddProbe(string name) + where T1 : IArgType, IFireArgLong + where T2 : IArgType, IFireArgLong + where T3 : IArgType, IFireArgLong + where T4 : IArgType, IFireArgLong + where T5 : IArgType, IFireArgLong + { + var @this = This; + return new(@this, name, Libstapsdt.Libstapsdt.ProviderAddProbe(@this, name, (Libstapsdt.ArgType)T1.ArgType, (Libstapsdt.ArgType)T2.ArgType, (Libstapsdt.ArgType)T3.ArgType, (Libstapsdt.ArgType)T4.ArgType, (Libstapsdt.ArgType)T5.ArgType)); + } +} + +partial record struct Probe + where T1 : IFireArgLong + where T2 : IFireArgLong + where T3 : IFireArgLong + where T4 : IFireArgLong + where T5 : IFireArgLong + where T6 : IFireArgLong +{ + readonly Provider provider; + readonly nint probe; + + internal Probe(Provider provider, string name, IntPtr probe) + { + this.provider = provider; + Name = name; + this.probe = probe; + } + + public string Name { get; } + + bool IsProviderLoaded => this.provider.IsLoaded; + + public bool IsEnabled => IsProviderLoaded && ProbeIsEnabled(this.probe); + + public Probe? Active => IsEnabled ? this : null; + + public void Fire(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) + { + if (!IsProviderLoaded) + return; + ProbeFire(this.probe, arg1.UncheckedValue, arg2.UncheckedValue, arg3.UncheckedValue, arg4.UncheckedValue, arg5.UncheckedValue, arg6.UncheckedValue); + } + + public override string ToString() => $"{provider}:{Name}"; +} + +partial class Provider +{ + public Probe AddProbe(string name) + where T1 : IArgType, IFireArgLong + where T2 : IArgType, IFireArgLong + where T3 : IArgType, IFireArgLong + where T4 : IArgType, IFireArgLong + where T5 : IArgType, IFireArgLong + where T6 : IArgType, IFireArgLong + { + var @this = This; + return new(@this, name, Libstapsdt.Libstapsdt.ProviderAddProbe(@this, name, (Libstapsdt.ArgType)T1.ArgType, (Libstapsdt.ArgType)T2.ArgType, (Libstapsdt.ArgType)T3.ArgType, (Libstapsdt.ArgType)T4.ArgType, (Libstapsdt.ArgType)T5.ArgType, (Libstapsdt.ArgType)T6.ArgType)); + } +} diff --git a/src/Probes.g.tt b/src/Probes.g.tt new file mode 100644 index 0000000..99f665e --- /dev/null +++ b/src/Probes.g.tt @@ -0,0 +1,112 @@ +<#@ template debug="false" hostspecific="true" language="C#" #> +<#@ output extension=".cs" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="System.Globalization" #> +<#@ import namespace="System.Linq" #> +//------------------------------------------------------------------------------ +// +// This code was generated by a tool on and at: +// <#= DateTimeOffset.Now.ToString("ddd, dd MMM yyyy HH:mm:ss zzz", CultureInfo.InvariantCulture) #> +// +// Changes to this file will be lost if the code is re-generated. +// +//------------------------------------------------------------------------------ + +#nullable enable // required for auto-generated sources (see below why) + +// > Older code generation strategies may not be nullable aware. Setting the +// > project-level nullable context to "enable" could result in many +// > warnings that a user is unable to fix. To support this scenario any syntax +// > tree that is determined to be generated will have its nullable state +// > implicitly set to "disable", regardless of the overall project state. +// +// Source: https://github.com/dotnet/roslyn/blob/70e158ba6c2c99bd3c3fc0754af0dbf82a6d353d/docs/features/nullable-reference-types.md#generated-code + +using static Libstapsdt.Libstapsdt; + +namespace DynamicProbes; +<# + const string indent1 = " "; + const string indent2 = indent1 + indent1; + + foreach (var ns in from count in Enumerable.Range(0, 7) + select Enumerable.Range(1, count).ToArray()) + { + string[] typeParams = ns.Length switch + { + 0 => [], + 1 => ["T"], + _ => [..from n in ns select $"T{n}"], + }; + + string[] args = ns.Length switch + { + 0 => [], + 1 => ["arg"], + _ => [..from n in ns select $"arg{n}"], + }; + + var typeParamsList = typeParams.Length > 0 + ? $"<{string.Join(", ", typeParams)}>" + : string.Empty; + + string Constraints(string none, Func singleton, Func, string> many) + { + var result = typeParams switch + { + { Length: 0 } => none, + { Length: 1 } ts => singleton(ts[0]), + var ts => many(ts), + }; + return result.Replace("\n", Environment.NewLine); + }; + + var genericName = $"Probe{typeParamsList}"; +#> + +partial record struct <#= genericName #><#= + Constraints(string.Empty, + t => $"\n{indent1}where {t} : IFireArgLong", + ts => $"{string.Join(null, from t in ts select $"\n{indent1}where {t} : IFireArgLong")}") #> +{ + readonly Provider provider; + readonly nint probe; + + internal Probe(Provider provider, string name, IntPtr probe) + { + this.provider = provider; + Name = name; + this.probe = probe; + } + + public string Name { get; } + + bool IsProviderLoaded => this.provider.IsLoaded; + + public bool IsEnabled => IsProviderLoaded && ProbeIsEnabled(this.probe); + + public <#= genericName #>? Active => IsEnabled ? this : null; + + public void Fire(<#= string.Join(", ", typeParams.Zip(args, (type, arg) => $"{type} {arg}")) #>) + { + if (!IsProviderLoaded) + return; + ProbeFire(this.probe<#= string.Join(null, from arg in args select $", {arg}.UncheckedValue") #>); + } + + public override string ToString() => $"{provider}:{Name}"; +} + +partial class Provider +{ + public <#= genericName #> AddProbe<#= typeParamsList #>(string name)<#= + Constraints(string.Empty, + t => $"\n{indent2}where {t} : IArgType, IFireArgLong", + ts => string.Join(null, from t in ts select $"\n{indent2}where {t} : IArgType, IFireArgLong")) #> + { + var @this = This; + return new(@this, name, Libstapsdt.Libstapsdt.ProviderAddProbe(@this, name<#= string.Join(null, from t in typeParams select $", (Libstapsdt.ArgType){t}.ArgType") #>)); + } +} +<# } #> diff --git a/src/Provider.cs b/src/Provider.cs new file mode 100644 index 0000000..938807b --- /dev/null +++ b/src/Provider.cs @@ -0,0 +1,112 @@ +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using static Libstapsdt.Libstapsdt; + +namespace DynamicProbes; + +public interface ILoadedProvider : IDisposable +{ + Provider Unload(); +} + +public sealed partial class Provider : SafeHandle, ILoadedProvider +{ + string? name; + + public Provider() : this(0) { } + + Provider(nint ptr) : base(ptr, ownsHandle: true) { } + + public static Provider Init(string name) + { + var provider = ProviderInit(name); + if (provider.IsInvalid) + throw new ProviderException($"Provider initialization failed: {name}"); + provider.Name = name; + return provider; + } + + public string Name + { + get => this.name ?? string.Empty; + private set => this.name = value; + } + + public override string ToString() => Name; + + public ILoadedProvider Load() => Load(This); + + internal bool IsLoaded { get; private set; } + +#pragma warning disable CA1859 // Use concrete types when possible for improved performance (by-design) + static ILoadedProvider Load(Provider provider) +#pragma warning restore CA1859 // Use concrete types when possible for improved performance + { + if (provider.IsLoaded) + return provider; + + if (ProviderLoad(provider) != 0) + throw new ProviderException($"Provider loading failed: {provider.Name}"); + + provider.IsLoaded = true; + return provider; + } + + Provider ILoadedProvider.Unload() => Unload(This); + + static Provider Unload(Provider provider) + { + if (!provider.IsLoaded) + throw new InvalidOperationException(); + + if (ProviderUnload(provider) != 0) + throw new ProviderException($"Provider unloading failed: {provider.Name}"); + + provider.IsLoaded = false; + return provider; + } + + public override bool IsInvalid => this.handle == 0; + + protected override bool ReleaseHandle() + { + if (IsLoaded) + { + IsLoaded = false; + + // This member is called from either "Dispose" or the finalizer, so exceptions cannot be + // thrown and any error in unloading has to be ignored. + + if (ProviderUnload(this.handle) != 0) + Debug.WriteLine($"Provider unloading failed: {Name}"); + } + + ProviderDestroy(this.handle); + return true; + } + + Provider This + { + get + { + if (IsInvalid) + ThrowObjectDisposedException(); + return this; + } + } + + /// + /// Separating the throw into a separate method allows the JIT to optimize the method. + /// + [DoesNotReturn] + static void ThrowObjectDisposedException() => + throw new ObjectDisposedException(nameof(Provider)); +} + +public class ProviderException(string? message, Exception? inner) : + Exception(message, inner) +{ + public ProviderException() : this(null) { } + public ProviderException(string? message) : this(message, null) { } +} diff --git a/src/PublicAPI.Shipped.txt b/src/PublicAPI.Shipped.txt new file mode 100644 index 0000000..7dc5c58 --- /dev/null +++ b/src/PublicAPI.Shipped.txt @@ -0,0 +1 @@ +#nullable enable diff --git a/src/PublicAPI.Unshipped.txt b/src/PublicAPI.Unshipped.txt new file mode 100644 index 0000000..bc0b92b --- /dev/null +++ b/src/PublicAPI.Unshipped.txt @@ -0,0 +1,163 @@ +#nullable enable +DynamicProbes.IArgType.ArgType.get -> DynamicProbes.ArgType +DynamicProbes.BoolArg +DynamicProbes.BoolArg.BoolArg() -> void +DynamicProbes.BoolArg.BoolArg(bool Value) -> void +DynamicProbes.BoolArg.Value.get -> bool +DynamicProbes.BoolArg.Value.init -> void +DynamicProbes.Int16Arg +DynamicProbes.Int16Arg.Int16Arg() -> void +DynamicProbes.Int16Arg.Int16Arg(short Value) -> void +DynamicProbes.Int16Arg.Value.get -> short +DynamicProbes.Int16Arg.Value.init -> void +DynamicProbes.Int32Arg +DynamicProbes.Int32Arg.Int32Arg() -> void +DynamicProbes.Int32Arg.Int32Arg(int Value) -> void +DynamicProbes.Int32Arg.Value.get -> int +DynamicProbes.Int32Arg.Value.init -> void +DynamicProbes.Int64Arg +DynamicProbes.Int64Arg.Int64Arg() -> void +DynamicProbes.Int64Arg.Int64Arg(long Value) -> void +DynamicProbes.Int64Arg.Value.get -> long +DynamicProbes.Int64Arg.Value.init -> void +DynamicProbes.Int8Arg +DynamicProbes.Int8Arg.Int8Arg() -> void +DynamicProbes.Int8Arg.Int8Arg(sbyte Value) -> void +DynamicProbes.Int8Arg.Value.get -> sbyte +DynamicProbes.Int8Arg.Value.init -> void +DynamicProbes.IntPtrArg +DynamicProbes.IntPtrArg.IntPtrArg() -> void +DynamicProbes.IntPtrArg.IntPtrArg(nint Value) -> void +DynamicProbes.IntPtrArg.Value.get -> nint +DynamicProbes.IntPtrArg.Value.init -> void +DynamicProbes.Provider +DynamicProbes.Provider.Provider() -> void +DynamicProbes.Provider.AddProbe(string! name) -> DynamicProbes.Probe +DynamicProbes.Provider.AddProbe(string! name) -> DynamicProbes.Probe +DynamicProbes.Provider.AddProbe(string! name) -> DynamicProbes.Probe +DynamicProbes.Provider.AddProbe(string! name) -> DynamicProbes.Probe +DynamicProbes.Provider.AddProbe(string! name) -> DynamicProbes.Probe +DynamicProbes.Provider.AddProbe(string! name) -> DynamicProbes.Probe +DynamicProbes.Provider.AddProbe(string! name) -> DynamicProbes.Probe +DynamicProbes.UInt16Arg +DynamicProbes.UInt16Arg.UInt16Arg() -> void +DynamicProbes.UInt16Arg.UInt16Arg(ushort Value) -> void +DynamicProbes.UInt16Arg.Value.get -> ushort +DynamicProbes.UInt16Arg.Value.init -> void +DynamicProbes.UInt32Arg +DynamicProbes.UInt32Arg.UInt32Arg() -> void +DynamicProbes.UInt32Arg.UInt32Arg(uint Value) -> void +DynamicProbes.UInt32Arg.Value.get -> uint +DynamicProbes.UInt32Arg.Value.init -> void +DynamicProbes.UInt64Arg +DynamicProbes.UInt64Arg.UInt64Arg() -> void +DynamicProbes.UInt64Arg.UInt64Arg(ulong Value) -> void +DynamicProbes.UInt64Arg.Value.get -> ulong +DynamicProbes.UInt64Arg.Value.init -> void +DynamicProbes.UInt8Arg +DynamicProbes.UInt8Arg.UInt8Arg() -> void +DynamicProbes.UInt8Arg.UInt8Arg(byte Value) -> void +DynamicProbes.UInt8Arg.Value.get -> byte +DynamicProbes.UInt8Arg.Value.init -> void +override DynamicProbes.BoolArg.ToString() -> string! +override DynamicProbes.Int16Arg.ToString() -> string! +override DynamicProbes.Int32Arg.ToString() -> string! +override DynamicProbes.Int64Arg.ToString() -> string! +override DynamicProbes.Int8Arg.ToString() -> string! +override DynamicProbes.Provider.ToString() -> string! +override DynamicProbes.Provider.IsInvalid.get -> bool +DynamicProbes.Provider.Name.get -> string! +DynamicProbes.Provider.Load() -> DynamicProbes.ILoadedProvider! +override DynamicProbes.UInt16Arg.ToString() -> string! +override DynamicProbes.UInt32Arg.ToString() -> string! +override DynamicProbes.UInt64Arg.ToString() -> string! +override DynamicProbes.UInt8Arg.ToString() -> string! +static DynamicProbes.BoolArg.ArgType.get -> DynamicProbes.ArgType +static DynamicProbes.BoolArg.implicit operator DynamicProbes.BoolArg(bool value) -> DynamicProbes.BoolArg +static DynamicProbes.Int16Arg.ArgType.get -> DynamicProbes.ArgType +static DynamicProbes.Int16Arg.implicit operator DynamicProbes.Int16Arg(short value) -> DynamicProbes.Int16Arg +static DynamicProbes.Int32Arg.ArgType.get -> DynamicProbes.ArgType +static DynamicProbes.Int32Arg.implicit operator DynamicProbes.Int32Arg(int value) -> DynamicProbes.Int32Arg +static DynamicProbes.Int64Arg.ArgType.get -> DynamicProbes.ArgType +static DynamicProbes.Int64Arg.implicit operator DynamicProbes.Int64Arg(long value) -> DynamicProbes.Int64Arg +static DynamicProbes.Int8Arg.ArgType.get -> DynamicProbes.ArgType +static DynamicProbes.Int8Arg.implicit operator DynamicProbes.Int8Arg(sbyte value) -> DynamicProbes.Int8Arg +static DynamicProbes.IntPtrArg.ArgType.get -> DynamicProbes.ArgType +static DynamicProbes.IntPtrArg.implicit operator DynamicProbes.IntPtrArg(nint value) -> DynamicProbes.IntPtrArg +static DynamicProbes.Provider.Init(string! name) -> DynamicProbes.Provider! +DynamicProbes.ILoadedProvider +DynamicProbes.ILoadedProvider.Unload() -> DynamicProbes.Provider! +DynamicProbes.ProviderException +DynamicProbes.ProviderException.ProviderException() -> void +DynamicProbes.ProviderException.ProviderException(string? message) -> void +DynamicProbes.ProviderException.ProviderException(string? message, System.Exception? inner) -> void +DynamicProbes.IArgType +DynamicProbes.IFireArgLong +DynamicProbes.IFireArgLong.UncheckedValue.get -> long +DynamicProbes.Probe +DynamicProbes.Probe.Probe() -> void +DynamicProbes.Probe.Name.get -> string! +DynamicProbes.Probe.IsEnabled.get -> bool +DynamicProbes.Probe.Fire() -> void +DynamicProbes.Probe.Active.get -> DynamicProbes.Probe? +override DynamicProbes.Probe.ToString() -> string! +DynamicProbes.Probe +DynamicProbes.Probe.Probe() -> void +DynamicProbes.Probe.Name.get -> string! +DynamicProbes.Probe.IsEnabled.get -> bool +DynamicProbes.Probe.Fire(T arg) -> void +DynamicProbes.Probe.Active.get -> DynamicProbes.Probe? +override DynamicProbes.Probe.ToString() -> string! +DynamicProbes.Probe +DynamicProbes.Probe.Probe() -> void +DynamicProbes.Probe.Name.get -> string! +DynamicProbes.Probe.IsEnabled.get -> bool +DynamicProbes.Probe.Fire(T1 arg1, T2 arg2) -> void +DynamicProbes.Probe.Active.get -> DynamicProbes.Probe? +override DynamicProbes.Probe.ToString() -> string! +DynamicProbes.Probe +DynamicProbes.Probe.Probe() -> void +DynamicProbes.Probe.Name.get -> string! +DynamicProbes.Probe.IsEnabled.get -> bool +DynamicProbes.Probe.Fire(T1 arg1, T2 arg2, T3 arg3) -> void +DynamicProbes.Probe.Active.get -> DynamicProbes.Probe? +override DynamicProbes.Probe.ToString() -> string! +DynamicProbes.Probe +DynamicProbes.Probe.Probe() -> void +DynamicProbes.Probe.Name.get -> string! +DynamicProbes.Probe.IsEnabled.get -> bool +DynamicProbes.Probe.Fire(T1 arg1, T2 arg2, T3 arg3, T4 arg4) -> void +DynamicProbes.Probe.Active.get -> DynamicProbes.Probe? +override DynamicProbes.Probe.ToString() -> string! +DynamicProbes.Probe +DynamicProbes.Probe.Probe() -> void +DynamicProbes.Probe.Name.get -> string! +DynamicProbes.Probe.IsEnabled.get -> bool +DynamicProbes.Probe.Fire(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) -> void +DynamicProbes.Probe.Active.get -> DynamicProbes.Probe? +override DynamicProbes.Probe.ToString() -> string! +DynamicProbes.Probe +DynamicProbes.Probe.Probe() -> void +DynamicProbes.Probe.Name.get -> string! +DynamicProbes.Probe.IsEnabled.get -> bool +DynamicProbes.Probe.Fire(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) -> void +DynamicProbes.Probe.Active.get -> DynamicProbes.Probe? +override DynamicProbes.Probe.ToString() -> string! +DynamicProbes.ArgType +DynamicProbes.ArgType.NoArg = 0 -> DynamicProbes.ArgType +DynamicProbes.ArgType.UInt8 = 1 -> DynamicProbes.ArgType +DynamicProbes.ArgType.Int8 = -1 -> DynamicProbes.ArgType +DynamicProbes.ArgType.UInt16 = 2 -> DynamicProbes.ArgType +DynamicProbes.ArgType.Int16 = -2 -> DynamicProbes.ArgType +DynamicProbes.ArgType.UInt32 = 4 -> DynamicProbes.ArgType +DynamicProbes.ArgType.Int32 = -4 -> DynamicProbes.ArgType +DynamicProbes.ArgType.UInt64 = 8 -> DynamicProbes.ArgType +DynamicProbes.ArgType.Int64 = -8 -> DynamicProbes.ArgType +static DynamicProbes.UInt16Arg.ArgType.get -> DynamicProbes.ArgType +static DynamicProbes.UInt16Arg.implicit operator DynamicProbes.UInt16Arg(ushort value) -> DynamicProbes.UInt16Arg +static DynamicProbes.UInt32Arg.ArgType.get -> DynamicProbes.ArgType +static DynamicProbes.UInt32Arg.implicit operator DynamicProbes.UInt32Arg(uint value) -> DynamicProbes.UInt32Arg +static DynamicProbes.UInt64Arg.ArgType.get -> DynamicProbes.ArgType +static DynamicProbes.UInt64Arg.implicit operator DynamicProbes.UInt64Arg(ulong value) -> DynamicProbes.UInt64Arg +static DynamicProbes.UInt8Arg.ArgType.get -> DynamicProbes.ArgType +static DynamicProbes.UInt8Arg.implicit operator DynamicProbes.UInt8Arg(byte value) -> DynamicProbes.UInt8Arg diff --git a/tests/UnitTests/.editorconfig b/tests/UnitTests/.editorconfig new file mode 100644 index 0000000..ef3b2d0 --- /dev/null +++ b/tests/UnitTests/.editorconfig @@ -0,0 +1,4 @@ +[*Tests.cs] + +# CA1707: Identifiers should not contain underscores +dotnet_code_quality.CA1707.api_surface = private, internal diff --git a/tests/UnitTests/DynamicProbesExtensions.cs b/tests/UnitTests/DynamicProbesExtensions.cs new file mode 100644 index 0000000..6a54fb7 --- /dev/null +++ b/tests/UnitTests/DynamicProbesExtensions.cs @@ -0,0 +1,8 @@ +namespace DynamicProbes; + +partial class Provider +{ +#pragma warning disable CA1720 // Identifier contains type name (by-design) + public static Provider Fake(nint ptr) => new(ptr); +#pragma warning restore CA1720 // Identifier contains type name +} diff --git a/tests/UnitTests/Enum.cs b/tests/UnitTests/Enum.cs new file mode 100644 index 0000000..a540623 --- /dev/null +++ b/tests/UnitTests/Enum.cs @@ -0,0 +1,13 @@ +namespace UnitTests; + +/// +/// A transparent wrapper around an enum type that provides value equality +/// semantics. +/// +readonly record struct Enum(T Value) where T : Enum +{ + public static implicit operator Enum(T value) => new(value); + public static implicit operator T(Enum value) => value.Value; + + public override string ToString() => Value.ToString(); +} diff --git a/tests/UnitTests/EquatableArray.cs b/tests/UnitTests/EquatableArray.cs new file mode 100644 index 0000000..306ab5e --- /dev/null +++ b/tests/UnitTests/EquatableArray.cs @@ -0,0 +1,121 @@ +using System.Collections; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace UnitTests; + +static class EquatableArray +{ + public static EquatableArray Create(ReadOnlySpan items) + where T : IEquatable => new([..items]); +} + +/// +/// A thin wrapper around an that requires the +/// array items to be equatable and consequently provides value equality +/// semantics for entire array; this is, two arrays are equal if they have the +/// same length and all their items are equal. +/// +[DebuggerDisplay("Length = {Length}")] +[CollectionBuilder(typeof(EquatableArray), nameof(EquatableArray.Create))] +readonly record struct EquatableArray : IReadOnlyList, IList + where T : IEquatable +{ + readonly ImmutableArray items; + + public EquatableArray(ImmutableArray items) => this.items = items; + + public static implicit operator EquatableArray(ImmutableArray items) => new(items); + + public int Length => this.items.IsDefaultOrEmpty ? 0 : this.items.Length; + + public T this[int index] + { + get + { + if (this.items.IsDefaultOrEmpty) + ThrowIndexOutOfRangeException(); + return this.items[index]; + } + } + + [DoesNotReturn] + static void ThrowIndexOutOfRangeException() => +#pragma warning disable CA2201 // Do not raise reserved exception types (thrown from an indexer) + throw new IndexOutOfRangeException(); +#pragma warning restore CA2201 // Do not raise reserved exception types + + int IList.IndexOf(T item) => + !this.items.IsDefaultOrEmpty ? this.items.IndexOf(item) : -1; + + public bool Contains(T item) => + !this.items.IsDefaultOrEmpty && this.items.Contains(item); + + public bool Equals(EquatableArray other) => + this.items.SequenceEqual(other.items); + + public override int GetHashCode() + { + if (this.items.IsDefaultOrEmpty) + return 0; + + var hash = new HashCode(); + foreach (var item in this.items) + hash.Add(item); + + return hash.ToHashCode(); + } + + public Enumerator GetEnumerator() => new(this.items); + + public override string ToString() => $"[{string.Join(", ", this)}]"; + + T IList.this[int index] + { + get => this[index]; + set => throw new NotSupportedException(); + } + + void ICollection.CopyTo(T[] array, int arrayIndex) => + this.items.CopyTo(array, arrayIndex); + + int ICollection.Count => Length; + int IReadOnlyCollection.Count => Length; + bool ICollection.IsReadOnly => true; + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public struct Enumerator(ImmutableArray items) : IEnumerator + { + int index = 0; + + public T Current { get; private set; } + + readonly object? IEnumerator.Current => Current; + + public readonly void Dispose() { } + + public bool MoveNext() + { + if (items.IsDefaultOrEmpty || this.index >= items.Length) + return false; + + Current = items[this.index++]; + return true; + } + + public void Reset() => this.index = 0; + } + + // Unsupported members + + void ICollection.Add(T item) => throw new NotSupportedException(); + void ICollection.Clear() => throw new NotSupportedException(); + bool ICollection.Remove(T item) => throw new NotSupportedException(); + + void IList.Insert(int index, T item) => throw new NotSupportedException(); + void IList.RemoveAt(int index) => throw new NotSupportedException(); +} diff --git a/tests/UnitTests/Handlers.cs b/tests/UnitTests/Handlers.cs new file mode 100644 index 0000000..ffcb0c9 --- /dev/null +++ b/tests/UnitTests/Handlers.cs @@ -0,0 +1,208 @@ +#pragma warning disable IDE0130 // Namespace does not match folder structure (simplicity) +namespace UnitTests.Mocking.Methods; +#pragma warning restore IDE0130 // Namespace does not match folder structure + +/// +/// Represents the context of a call. +/// +sealed class CallContext +{ + public List<(TArgs, TResult)> Calls { get; } = []; +} + +/// +/// Represents a call handler/implementation. +/// +interface IHandler +{ + TResult Handle(CallContext context, TArgs args); +} + +/// +/// Represents a partial call handler/implementation. +/// +interface IPartialHandler +{ + IPartialHandler Do(Action action); + IHandler Return(TResult result); +} + +/// +/// Represents a call handler that never returns when there is no implementation and therefore +/// throws . +/// +interface INeverHandler : + IHandler, + IPartialHandler; + +/// +/// Represents a call handler that captures the arguments and results into the call context. +/// +interface ICapturingHandler : IHandler; + +/// +/// Provides a set of methods to create and manipulate handlers. +/// +static class Handler +{ + /// + /// Creates a handler from the specified function. + /// + public static IHandler + Create(Func, TArgs, TResult> func) => + new DelegatingHandler(func); + + sealed class DelegatingHandler(Func, TArgs, TResult> func) : + IHandler + { + public TResult Handle(CallContext context, TArgs args) => + func(context, args); + } + + /// + /// Creates a handler that throw . + /// + public static INeverHandler + Never(string? name = null) => + name is { } someName + ? new NeverHandler($"The handler for \"{someName}\" is not implemented.") + : NeverHandler.Instance; + + sealed class NeverHandler(string? message = null) : INeverHandler + { + public static readonly NeverHandler Instance = new(); + + public TResult Handle(CallContext context, TArgs args) => + throw new NotImplementedException(message); + + public IPartialHandler Do(Action action) => + new PartialHandler(action); + + public IHandler Return(TResult result) => + Create((_, _) => result); + } + + sealed class PartialHandler(Action action) : + IPartialHandler + { + readonly Action action = action; + + public IPartialHandler Do(Action action) => + new PartialHandler(args => + { + this.action(args); + action(args); + }); + + public IHandler Return(TResult result) => + Create((_, args) => + { + this.action(args); + return result; + }); + } + + /// + /// Creates a handler that asserts whether call arguments are equal to the expected value. + /// + public static IPartialHandler + Expect(this IPartialHandler handler, TArgs expected) + where TArgs : IEquatable => + handler.Do(args => Assert.Equal(expected, args)); + + /// + /// Creates a handler that asserts whether call arguments are equal to the expected value. + /// + public static IPartialHandler + ExpectRef(this IPartialHandler handler, Ref expected) + where TArgs : IEquatable => + handler.Do(args => Assert.Equal(expected.Value, args)); + + /// + /// Creates a handler that asserts whether call arguments meet a specific condition. + /// + public static IPartialHandler + AssertTrue(this IPartialHandler handler, + Func predicate, + string? message = null) => + handler.Do(args => Assert.True(predicate(args), message)); + + /// + /// Creates a handler that captures the arguments and results into the call context. + /// + public static IHandler Capture(this IHandler handler) => + handler as ICapturingHandler ?? new CapturingHandler(handler); + + sealed class CapturingHandler(IHandler handler) : + ICapturingHandler + { + public TResult Handle(CallContext context, TArgs args) + { + var result = handler.Handle(context, args); + context.Calls.Add((args, result)); + return result; + } + } + + /// + /// Creates a handler that iterates through a sequence of implementations. If the handler is + /// invoked when the entire sequence is exhausted, it throws . + /// + public static IHandler + Sequence(this INeverHandler never, + params Func, IHandler>[] factories) + { + var count = 0; + var handler = factories.Select(h => h(never)).GetEnumerator(); + + return Create((context, args) => + { + count++; + + if (!handler.MoveNext()) + { + try + { + _ = never.Handle(context, args); + } + catch (NotImplementedException ex) + { + var suffix = (count % 10) switch { 1 => "st", 2 => "nd", 3 => "rd", _ => "th" }; + throw new InvalidOperationException($"There is no handler for the {count}{suffix} invocation.", ex); + } + } + + return handler.Current.Handle(context, args); + }); + } +} + +/// +/// Connects a handler to a context with invocation infrastructure. +/// +sealed class HandlerCell +{ + readonly string name; + + public HandlerCell(string name) + { + this.name = name; + Handler = UnitTests.Mocking.Methods.Handler.Never(this.name); + } + + public int InvocationCount { get; private set; } + + public CallContext CallContext { get; } = new(); + public IHandler Handler { get; set; } + + public event EventHandler<(TArgs Args, TResult Result)>? Invoked; + + public TResult Invoke(TArgs args) + { + InvocationCount++; + var result = Handler.Handle(CallContext, args); + Invoked?.Invoke(this, (args, result)); + return result; + } +} diff --git a/tests/UnitTests/MockLibstapsdt.cs b/tests/UnitTests/MockLibstapsdt.cs new file mode 100644 index 0000000..40d7682 --- /dev/null +++ b/tests/UnitTests/MockLibstapsdt.cs @@ -0,0 +1,112 @@ +using DynamicProbes; +using UnitTests; +using UnitTests.Mocking.Methods; + +#pragma warning disable IDE0130 // Namespace does not match folder structure (by-design) +namespace Libstapsdt; +#pragma warning restore IDE0130 // Namespace does not match folder structure + +sealed record ProviderInitArgs(string Name); +sealed record ProviderDestroyArgs(nint Provider); +sealed record ProviderLoadArgs(Provider Provider); +sealed record ProviderUnloadArgs(nint Provider); +sealed record ProbeIsEnabledArgs(nint Probe); +sealed record ProviderAddProbeArgs(Provider Provider, string Name, EquatableArray> Args); +sealed record ProbeFireArgs(nint Probe, EquatableArray Args); + +sealed class LibstapsdtHandlers : IDisposable +{ + readonly List initializedProviders = []; + + public LibstapsdtHandlers() => + ProviderInit.Invoked += (_, args) => this.initializedProviders.Add(args.Result); + + public void Dispose() + { + foreach (var provider in this.initializedProviders) + provider.Dispose(); + + this.initializedProviders.Clear(); + } + + public HandlerCell ProviderInit { get; init; } = new(nameof(ProviderInit)); + + public HandlerCell ProviderDestroy { get; init; } = + new(nameof(ProviderDestroy)); + + public HandlerCell ProviderLoad { get; init; } = new(nameof(ProviderLoad)); + public HandlerCell ProviderUnload { get; init; } = new(nameof(ProviderUnload)); + + public HandlerCell ProbeIsEnabled { get; init; } = new(nameof(ProbeIsEnabled)); + public HandlerCell ProviderAddProbe { get; init; } = new(nameof(ProviderAddProbe)); + public HandlerCell ProbeFire { get; init; } = new(nameof(ProbeFire)); +} + +static class DefaultLibstapsdtHandlers +{ + public static readonly INeverHandler ProviderInit = Handler.Never(nameof(ProviderInit)); + + public static readonly INeverHandler ProviderDestroy = Handler.Never(nameof(ProviderDestroy)); + + public static readonly INeverHandler ProviderLoad = Handler.Never(nameof(ProviderLoad)); + public static readonly INeverHandler ProviderUnload = Handler.Never(nameof(ProviderUnload)); + + public static readonly INeverHandler ProbeIsEnabled = Handler.Never(nameof(ProbeIsEnabled)); + public static readonly INeverHandler ProviderAddProbe = Handler.Never(nameof(ProviderAddProbe)); + public static readonly INeverHandler ProbeFire = Handler.Never(nameof(ProbeFire)); + + public static readonly IHandler UnverifiedProviderDestroy = ProviderDestroy.Return(default); + public static readonly IHandler UnverifiedProviderUnload = ProviderUnload.Return(0); +} + +static class Libstapsdt +{ + [ThreadStatic] + static LibstapsdtHandlers? handlers; + + public static LibstapsdtHandlers Handlers + { + get => handlers ??= new(); + set => handlers = value; + } + + public static Provider ProviderInit(string name) => Handlers.ProviderInit.Invoke(new(name)); + public static void ProviderDestroy(nint provider) => Handlers.ProviderDestroy.Invoke(new(provider)); + + public static int ProviderUnload(nint provider) => Handlers.ProviderUnload.Invoke(new(provider)); + public static int ProviderUnload(Provider provider) => Handlers.ProviderUnload.Invoke(new(provider.DangerousGetHandle())); + public static int ProviderLoad(Provider provider) => Handlers.ProviderLoad.Invoke(new(provider)); + + public static bool ProbeIsEnabled(nint probe) => Handlers.ProbeIsEnabled.Invoke(new(probe)); + + public static nint ProviderAddProbe(Provider provider, string name) => + Handlers.ProviderAddProbe.Invoke(new(provider, name, [])); + public static nint ProviderAddProbe(Provider provider, string name, ArgType arg1) => + Handlers.ProviderAddProbe.Invoke(new(provider, name, [arg1])); + public static nint ProviderAddProbe(Provider provider, string name, ArgType arg1, ArgType arg2) => + Handlers.ProviderAddProbe.Invoke(new(provider, name, [arg1, arg2])); + public static nint ProviderAddProbe(Provider provider, string name, ArgType arg1, ArgType arg2, ArgType arg3) => + Handlers.ProviderAddProbe.Invoke(new(provider, name, [arg1, arg2, arg3])); + public static nint ProviderAddProbe(Provider provider, string name, ArgType arg1, ArgType arg2, ArgType arg3, ArgType arg4) => + Handlers.ProviderAddProbe.Invoke(new(provider, name, [arg1, arg2, arg3, arg4])); + public static nint ProviderAddProbe(Provider provider, string name, ArgType arg1, ArgType arg2, ArgType arg3, ArgType arg4, ArgType arg5) => + Handlers.ProviderAddProbe.Invoke(new(provider, name, [arg1, arg2, arg3, arg4, arg5])); + public static nint ProviderAddProbe(Provider provider, string name, ArgType arg1, ArgType arg2, ArgType arg3, ArgType arg4, ArgType arg5, ArgType arg6) => + Handlers.ProviderAddProbe.Invoke(new(provider, name, [arg1, arg2, arg3, arg4, arg5, arg6])); + + + public static void ProbeFire(nint probe) => + Handlers.ProbeFire.Invoke(new(probe, [])); + public static void ProbeFire(nint probe, long arg1) => + Handlers.ProbeFire.Invoke(new(probe, [arg1])); + public static void ProbeFire(nint probe, long arg1, long arg2) => + Handlers.ProbeFire.Invoke(new(probe, [arg1, arg2])); + public static void ProbeFire(nint probe, long arg1, long arg2, long arg3) => + Handlers.ProbeFire.Invoke(new(probe, [arg1, arg2, arg3])); + public static void ProbeFire(nint probe, long arg1, long arg2, long arg3, long arg4) => + Handlers.ProbeFire.Invoke(new(probe, [arg1, arg2, arg3, arg4])); + public static void ProbeFire(nint probe, long arg1, long arg2, long arg3, long arg4, long arg5) => + Handlers.ProbeFire.Invoke(new(probe, [arg1, arg2, arg3, arg4, arg5])); + public static void ProbeFire(nint probe, long arg1, long arg2, long arg3, long arg4, long arg5, long arg6) => + Handlers.ProbeFire.Invoke(new(probe, [arg1, arg2, arg3, arg4, arg5, arg6])); +} diff --git a/tests/UnitTests/ProbeTests.cs b/tests/UnitTests/ProbeTests.cs new file mode 100644 index 0000000..04136ab --- /dev/null +++ b/tests/UnitTests/ProbeTests.cs @@ -0,0 +1,80 @@ +using DynamicProbes; +using UnitTests.Mocking.Methods; +using static Libstapsdt.DefaultLibstapsdtHandlers; + +namespace UnitTests; + +#pragma warning disable CA1030 // Use events where appropriate + +public sealed class ProbeTests : IDisposable +{ + readonly Provider provider = Provider.Fake(42); + + public void Dispose() + { + this.provider.Dispose(); + } + + [Fact] + public void Fire_When_Unloaded_Has_No_Effect() + { + using var handlers = Libstapsdt.Libstapsdt.Handlers = new() + { + ProviderInit = { Handler = ProviderInit.Return(this.provider) }, + ProviderDestroy = { Handler = UnverifiedProviderDestroy }, + ProviderAddProbe = { Handler = ProviderAddProbe.Return(4242) }, + }; + + var probe = Provider.Init("foo").AddProbe("bar"); + + probe.Fire(); + } + + [Fact] + public void Fire_Fires() + { + using var handlers = Libstapsdt.Libstapsdt.Handlers = new() + { + ProviderInit = { Handler = ProviderInit.Return(this.provider) }, + ProviderDestroy = { Handler = UnverifiedProviderDestroy }, + ProviderAddProbe = { Handler = ProviderAddProbe.Return(4242) }, + ProviderLoad = { Handler = ProviderLoad.Return(0) }, + ProviderUnload = { Handler = ProviderUnload.Return(0) }, + ProbeFire = { Handler = ProbeFire.Expect(new(4242, [])).Return(default) }, + }; + + var provider = Provider.Init("foo"); + var probe = provider.AddProbe("bar"); + _ = provider.Load(); + + probe.Fire(); + } + + [Fact] + public void Fire_While_Provider_Is_Unloaded_Has_No_Effect() + { + using var handlers = Libstapsdt.Libstapsdt.Handlers = new() + { + ProviderInit = { Handler = ProviderInit.Return(this.provider) }, + ProviderDestroy = { Handler = UnverifiedProviderDestroy }, + ProviderAddProbe = { Handler = ProviderAddProbe.Return(4242) }, + ProviderLoad = { Handler = ProviderLoad.Return(0) }, + ProviderUnload = { Handler = ProviderUnload.Return(0) }, + ProbeFire = { Handler = ProbeFire.Expect(new(4242, [])).Return(default) }, + }; + + var provider = Provider.Init("foo"); + var probe = provider.AddProbe("bar"); + + var loadedProvider = provider.Load(); + probe.Fire(); + + _ = loadedProvider.Unload(); + probe.Fire(); + + _ = provider.Load(); + probe.Fire(); + + Assert.Equal(2, Libstapsdt.Libstapsdt.Handlers.ProbeFire.InvocationCount); + } +} diff --git a/tests/UnitTests/ProviderTests.cs b/tests/UnitTests/ProviderTests.cs new file mode 100644 index 0000000..d78a0f0 --- /dev/null +++ b/tests/UnitTests/ProviderTests.cs @@ -0,0 +1,232 @@ +using DynamicProbes; +using Libstapsdt; +using UnitTests.Mocking.Methods; +using static Libstapsdt.DefaultLibstapsdtHandlers; + +namespace UnitTests; + +readonly record struct ReturnValue(T Value) +{ + public override string ToString() => $"{Value}"; + + public static implicit operator T(ReturnValue value) => value.Value; +} + +public sealed class ProviderTests : IDisposable +{ + static ReturnValue ReturnValue(T value) => new(value); + + static class Mocks + { + public static IHandler ProviderInit(ReturnValue ptr) => + DefaultLibstapsdtHandlers.ProviderInit +#pragma warning disable CA2000 // Dispose objects before losing scope + .Return(Provider.Fake(ptr)); +#pragma warning restore CA2000 // Dispose objects before losing scope + + public static IHandler ProviderInit(string name, ReturnValue ptr) => + DefaultLibstapsdtHandlers.ProviderInit + .Expect(new(name)) +#pragma warning disable CA2000 // Dispose objects before losing scope + .Return(Provider.Fake(ptr)); +#pragma warning restore CA2000 // Dispose objects before losing scope + } + + readonly Provider nullProvider = new(); + + public void Dispose() + { + this.nullProvider.Dispose(); + } + + [Fact] + public void Init_Succeeds_When_ProviderInit_Indicates_Success() + { + using var handlers = Libstapsdt.Libstapsdt.Handlers = new() + { + ProviderInit = { Handler = Mocks.ProviderInit("foo", ReturnValue((nint)42)) }, + ProviderDestroy = { Handler = UnverifiedProviderDestroy }, + }; + + var provider = Provider.Init("foo"); + + Assert.NotNull(provider); + } + + [Fact] + public void Init_Throws_When_ProviderInit_Fails() + { + using var handlers = Libstapsdt.Libstapsdt.Handlers = new() + { + ProviderInit = { Handler = Mocks.ProviderInit("foo", ReturnValue((nint)0)) }, + }; + + static void Act() => Provider.Init("foo"); + + var ex = Assert.Throws(Act); + Assert.Equal("Provider initialization failed: foo", ex.Message); + } + + [Fact] + public void Init_Returns_Unloaded_Provider() + { + using var handlers = Libstapsdt.Libstapsdt.Handlers = new() + { + ProviderInit = { Handler = Mocks.ProviderInit(ReturnValue((nint)42)) }, + ProviderDestroy = { Handler = UnverifiedProviderDestroy }, + }; + + var provider = Provider.Init("foo"); + + Assert.False(provider.IsLoaded); + } + + [Fact] + public void Init_Returns_Provider_With_Initialized_Name() + { + using var handlers = Libstapsdt.Libstapsdt.Handlers = new() + { + ProviderInit = { Handler = Mocks.ProviderInit(ReturnValue((nint)42)) }, + ProviderDestroy = { Handler = UnverifiedProviderDestroy }, + }; + + var provider = Provider.Init("foo"); + Assert.Equal("foo", provider.Name); + } + + [Fact] + public void ToString_Returns_Name() + { + using var handlers = Libstapsdt.Libstapsdt.Handlers = new() + { + ProviderInit = { Handler = Mocks.ProviderInit(ReturnValue((nint)42)) }, + ProviderDestroy = { Handler = UnverifiedProviderDestroy }, + }; + + var provider = Provider.Init("foo"); + Assert.Equal("foo", provider.ToString()); + } + + [Fact] + public void Dispose_Destroys() + { + using var handlers = Libstapsdt.Libstapsdt.Handlers = new() + { + ProviderInit = { Handler = Mocks.ProviderInit(ReturnValue((nint)42)) }, + ProviderDestroy = { Handler = ProviderDestroy.Expect(new(42)).Return(default) }, + }; + + Provider.Init("foo").Dispose(); + } + + [Fact] + public void Disposed_Destroys_Once() + { + LibstapsdtHandlers handlers; + Libstapsdt.Libstapsdt.Handlers = handlers = new() + { + ProviderInit = { Handler = Mocks.ProviderInit(ReturnValue((nint)42)) }, + ProviderDestroy = { Handler = ProviderDestroy.Expect(new(42)).Return(default) }, + }; + + var provider = Provider.Init("foo"); + provider.Dispose(); + provider.Dispose(); + + Assert.Equal(1, handlers.ProviderDestroy.InvocationCount); + } + + [Fact] + public void Load_Loads_Provider() + { + using var handlers = Libstapsdt.Libstapsdt.Handlers = new() + { + ProviderInit = { Handler = Mocks.ProviderInit(ReturnValue((nint)42)) }, + ProviderDestroy = { Handler = UnverifiedProviderDestroy }, + ProviderLoad = { Handler = ProviderLoad.Return(0) }, + ProviderUnload = { Handler = ProviderUnload.Return(0) }, + }; + + var provider = Provider.Init("foo"); + var result = provider.Load(); + + Assert.Same(provider, result); + } + + [Fact] + public void Unload_Unloads_Provider() + { + using var handlers = Libstapsdt.Libstapsdt.Handlers = new() + { + ProviderInit = { Handler = Mocks.ProviderInit(ReturnValue((nint)42)) }, + ProviderDestroy = { Handler = UnverifiedProviderDestroy }, + ProviderLoad = { Handler = ProviderLoad.Return(0) }, + ProviderUnload = { Handler = ProviderUnload.Expect(new(42)).Return(0) }, + }; + + var provider = Provider.Init("foo"); + var result = provider.Load().Unload(); + + Assert.Same(provider, result); + } + + [Fact] + public void Unload_Fails_When_Unloaded() + { + using var handlers = Libstapsdt.Libstapsdt.Handlers = new() + { + ProviderInit = { Handler = Mocks.ProviderInit(ReturnValue((nint)42)) }, + ProviderDestroy = { Handler = UnverifiedProviderDestroy }, + ProviderLoad = { Handler = ProviderLoad.Return(0) }, + ProviderUnload = { Handler = ProviderUnload.Expect(new(42)).Return(0) }, + }; + + var provider = Provider.Init("foo"); + var loadedProvider = provider.Load(); + _ = loadedProvider.Unload(); + + void Act() => loadedProvider.Unload(); + + _ = Assert.Throws(Act); + } + + [Fact] + public void AddProbe_Returns_Initialized_Probe() + { + var providerAddProbeArgs = Ref.Create(new ProviderAddProbeArgs(this.nullProvider, "bar", [])); + + using var handlers = Libstapsdt.Libstapsdt.Handlers = new() + { + ProviderInit = { Handler = Mocks.ProviderInit(ReturnValue((nint)42)) }, + ProviderDestroy = { Handler = UnverifiedProviderDestroy }, + ProviderAddProbe = { Handler = ProviderAddProbe.ExpectRef(providerAddProbeArgs).Return(4242) }, + }; + + var provider = Provider.Init("foo"); + providerAddProbeArgs.Value = providerAddProbeArgs.Value with { Provider = provider }; + var probe = provider.AddProbe("bar"); + + Assert.Equal("bar", probe.Name); + Assert.Equal("foo:bar", probe.ToString()); + } + + [Fact] + public void AddProbe_With_1_Arg_Returns_Initialized_Probe() + { + var providerAddProbeArgs = Ref.Create(new ProviderAddProbeArgs(this.nullProvider, "bar", [Libstapsdt.ArgType.Int32])); + + using var handlers = Libstapsdt.Libstapsdt.Handlers = new() + { + ProviderInit = { Handler = Mocks.ProviderInit(ReturnValue((nint)42)) }, + ProviderDestroy = { Handler = UnverifiedProviderDestroy }, + ProviderAddProbe = { Handler = ProviderAddProbe.ExpectRef(providerAddProbeArgs).Return(4242) }, + }; + + var provider = Provider.Init("foo"); + providerAddProbeArgs.Value = providerAddProbeArgs.Value with { Provider = provider }; + var probe = provider.AddProbe("bar"); + + Assert.Equal("bar", probe.Name); + Assert.Equal("foo:bar", probe.ToString()); + } +} diff --git a/tests/UnitTests/Ref.cs b/tests/UnitTests/Ref.cs new file mode 100644 index 0000000..3a187cf --- /dev/null +++ b/tests/UnitTests/Ref.cs @@ -0,0 +1,11 @@ +namespace UnitTests; + +sealed class Ref(T value) +{ + public T Value { get; set; } = value; +} + +static class Ref +{ + public static Ref Create(T value) => new(value); +} diff --git a/tests/UnitTests/Unit.cs b/tests/UnitTests/Unit.cs new file mode 100644 index 0000000..b8dfac0 --- /dev/null +++ b/tests/UnitTests/Unit.cs @@ -0,0 +1,9 @@ +namespace UnitTests; + +/// +/// Represents the absence of a specific value. +/// +readonly record struct Unit +{ + public override string ToString() => "()"; +} diff --git a/tests/UnitTests/UnitTests.csproj b/tests/UnitTests/UnitTests.csproj new file mode 100644 index 0000000..4bcc18b --- /dev/null +++ b/tests/UnitTests/UnitTests.csproj @@ -0,0 +1,31 @@ + + + + net8.0 + enable + enable + false + true + $(DefineConstants);NO_NATIVE_CODE + + + + + + + + + + + + + + + + + + + + + +