+ /// The dotnet msbuild command allows access to a fully functional MSBuild.
The command has the exact same capabilities as the existing MSBuild command-line client for SDK-style projects only. The options are all the same. For more information about the available options, see the [MSBuild command-line reference](/visualstudio/msbuild/msbuild-command-line-reference).
The [dotnet build](dotnet-build.md) command is equivalent to dotnet msbuild -restore . When you don't want to build the project and you have a specific target you want to run, use dotnet build or >dotnet msbuild and specify the target.
+ /// For more details, visit the official website .
+ ///
+ /// DotNetMSBuild(DotNetMSBuildSettings toolSettings = null)
+ {
+ toolSettings = toolSettings ?? new DotNetMSBuildSettings();
+ using var process = ProcessTasks.StartProcess(toolSettings);
+ process.AssertZeroExitCode();
+ return process.Output;
+ }
+
+ ///
+ /// The dotnet msbuild command allows access to a fully functional MSBuild.
The command has the exact same capabilities as the existing MSBuild command-line client for SDK-style projects only. The options are all the same. For more information about the available options, see the [MSBuild command-line reference](/visualstudio/msbuild/msbuild-command-line-reference).
The [dotnet build](dotnet-build.md) command is equivalent to dotnet msbuild -restore . When you don't want to build the project and you have a specific target you want to run, use dotnet build or >dotnet msbuild and specify the target.
+ /// For more details, visit the official website .
+ ///
+ ///
+ /// This is a CLI wrapper with fluent API that allows to modify the following arguments:
+ ///
+ public static IReadOnlyCollection DotNetMSBuild(Configure configurator)
+ {
+ return DotNetMSBuild(configurator(new DotNetMSBuildSettings()));
+ }
+}
diff --git a/build/nuke/Extensions/DotNetSettingsExtensions.cs b/build/nuke/Extensions/DotNetSettingsExtensions.cs
new file mode 100644
index 0000000000..dfdd9f9e09
--- /dev/null
+++ b/build/nuke/Extensions/DotNetSettingsExtensions.cs
@@ -0,0 +1,22 @@
+using Nuke.Common.Tools.DotNet;
+using Nuke.Common.Tools.MSBuild;
+
+internal static class DotNetSettingsExtensions
+{
+ public static DotNetPublishSettings SetTargetPlatformAnyCPU(this DotNetPublishSettings settings)
+ => settings.SetTargetPlatform(MSBuildTargetPlatform.MSIL);
+
+ public static T SetTargetPlatformAnyCPU(this T settings)
+ where T : MSBuildSettings
+ => settings.SetTargetPlatform(MSBuildTargetPlatform.MSIL);
+
+ public static DotNetPublishSettings SetTargetPlatform(this DotNetPublishSettings settings, MSBuildTargetPlatform platform)
+ {
+ return platform is null
+ ? settings
+ : settings.SetProperty("Platform", GetTargetPlatform(platform));
+ }
+
+ private static string GetTargetPlatform(MSBuildTargetPlatform platform) =>
+ platform == MSBuildTargetPlatform.MSIL ? "AnyCPU" : platform.ToString();
+}
diff --git a/build/nuke/Projects.cs b/build/nuke/Projects.cs
new file mode 100644
index 0000000000..f126c8c7c7
--- /dev/null
+++ b/build/nuke/Projects.cs
@@ -0,0 +1,6 @@
+public static class Projects
+{
+ public const string ClrProfilerManaged = "OpenTelemetry.AutoInstrumentation.ClrProfiler.Managed";
+ public const string ClrProfilerManagedCore = "OpenTelemetry.AutoInstrumentation.ClrProfiler.Managed.Core";
+ public const string ClrProfilerNative = "Datadog.Trace.ClrProfiler.Native";
+}
diff --git a/build/nuke/TargetFramework.cs b/build/nuke/TargetFramework.cs
new file mode 100644
index 0000000000..8a4a955f64
--- /dev/null
+++ b/build/nuke/TargetFramework.cs
@@ -0,0 +1,47 @@
+using System;
+using System.ComponentModel;
+using System.Globalization;
+using System.Linq;
+using System.Reflection;
+using Nuke.Common;
+using Nuke.Common.Tooling;
+
+[TypeConverter(typeof(TargetFrameworkTypeConverter))]
+public class TargetFramework : Enumeration
+{
+ public static TargetFramework NET452 = new TargetFramework { Value = "net452" };
+ public static TargetFramework NET461 = new TargetFramework { Value = "net461" };
+ public static TargetFramework NETSTANDARD2_0 = new TargetFramework { Value = "netstandard2.0" };
+ public static TargetFramework NETCOREAPP2_1 = new TargetFramework { Value = "netcoreapp2.1" };
+ public static TargetFramework NETCOREAPP3_0 = new TargetFramework { Value = "netcoreapp3.0" };
+ public static TargetFramework NETCOREAPP3_1 = new TargetFramework { Value = "netcoreapp3.1" };
+ public static TargetFramework NET5_0 = new TargetFramework { Value = "net5.0" };
+
+ public static implicit operator string(TargetFramework framework)
+ {
+ return framework.Value;
+ }
+
+ public class TargetFrameworkTypeConverter : TypeConverter
+ {
+ private static readonly TargetFramework[] AllTargetFrameworks = typeof(TargetFramework)
+ .GetFields(BindingFlags.Static | BindingFlags.Public)
+ .Select(x => x.GetValue(null))
+ .Cast()
+ .ToArray();
+
+ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
+ {
+ if (value is string stringValue)
+ {
+ var matchingFields = AllTargetFrameworks
+ .Where(x => string.Equals(x.Value, stringValue, StringComparison.OrdinalIgnoreCase))
+ .ToList();
+ ControlFlow.Assert(matchingFields.Count == 1, "matchingFields.Count == 1");
+ return matchingFields.Single();
+ }
+
+ return base.ConvertFrom(context, culture, value);
+ }
+ }
+}
diff --git a/build/nuke/_build.csproj b/build/nuke/_build.csproj
new file mode 100644
index 0000000000..e9508df42c
--- /dev/null
+++ b/build/nuke/_build.csproj
@@ -0,0 +1,21 @@
+
+
+
+ Exe
+ net5.0
+
+ CS0649;CS0169
+ ..\..
+ ..\..
+ 1
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/nuke/_build.csproj.DotSettings b/build/nuke/_build.csproj.DotSettings
new file mode 100644
index 0000000000..c8947fcec7
--- /dev/null
+++ b/build/nuke/_build.csproj.DotSettings
@@ -0,0 +1,26 @@
+
+ DO_NOT_SHOW
+ DO_NOT_SHOW
+ DO_NOT_SHOW
+ DO_NOT_SHOW
+ Implicit
+ Implicit
+ ExpressionBody
+ 0
+ NEXT_LINE
+ True
+ False
+ 120
+ IF_OWNER_IS_SINGLE_LINE
+ WRAP_IF_LONG
+ False
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
diff --git a/build_poc.sh b/build_poc.sh
new file mode 100755
index 0000000000..658c6da51a
--- /dev/null
+++ b/build_poc.sh
@@ -0,0 +1,62 @@
+#!/bin/bash
+set -euxo pipefail
+
+uname_os() {
+ os=$(uname -s | tr '[:upper:]' '[:lower:]')
+ case "$os" in
+ cygwin_nt*) echo "windows" ;;
+ mingw*) echo "windows" ;;
+ msys_nt*) echo "windows" ;;
+ *) echo "$os" ;;
+ esac
+}
+
+native_sufix() {
+ os=$(uname_os)
+ case "$os" in
+ windows*) echo "dll" ;;
+ linux*) echo "so" ;;
+ darwin*) echo "dylib" ;;
+ *) echo "OS: ${os} is not supported" ; exit 1 ;;
+ esac
+}
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
+cd $DIR
+
+BUILD_TYPE=${buildConfiguration:-Debug}
+OUTDIR="$( pwd )/src/Datadog.Trace.ClrProfiler.Native/bin/${BUILD_TYPE}/x64"
+
+# build Loader
+dotnet build -c $BUILD_TYPE src/Datadog.Trace.ClrProfiler.Managed.Loader/Datadog.Trace.ClrProfiler.Managed.Loader.csproj
+
+# build Native
+os=$(uname_os)
+case "$os" in
+ windows*)
+ SDK_TARGET_FRAMEWORKS="net452 net461 netstandard2.0 netcoreapp3.1"
+ nuget restore "src\Datadog.Trace.ClrProfiler.Native\Datadog.Trace.ClrProfiler.Native.vcxproj" -SolutionDirectory .
+ msbuild.exe Datadog.Trace.proj -t:BuildCpp -p:Configuration=${BUILD_TYPE} -p:Platform=x64
+ ;;
+
+ *)
+ SDK_TARGET_FRAMEWORKS="netstandard2.0 netcoreapp3.1"
+ cd src/Datadog.Trace.ClrProfiler.Native
+
+ mkdir -p build
+ (cd build && cmake ../ -DCMAKE_BUILD_TYPE=${BUILD_TYPE} && make)
+
+ cd $DIR
+ SUFIX=$(native_sufix)
+ mkdir -p ${OUTDIR}
+ cp -f src/Datadog.Trace.ClrProfiler.Native/build/bin/Datadog.Trace.ClrProfiler.Native.${SUFIX} ${OUTDIR}/OpenTelemetry.AutoInstrumentation.ClrProfiler.Native.${SUFIX}
+esac
+
+# build Managed
+cd $DIR
+
+for framework in ${SDK_TARGET_FRAMEWORKS} ; do
+ mkdir -p "$OUTDIR/$framework"
+ dotnet publish -f $framework -c ${BUILD_TYPE} src/OpenTelemetry.AutoInstrumentation.ClrProfiler.Managed/OpenTelemetry.AutoInstrumentation.ClrProfiler.Managed.csproj -o "$OUTDIR/$framework"
+ dotnet publish -f $framework -c ${BUILD_TYPE} samples/Vendor.Distro/Vendor.Distro.csproj -o "$OUTDIR/$framework"
+done
diff --git a/docs/DEVELOPING.md b/docs/DEVELOPING.md
index 7070590c17..3cebd7cc3d 100644
--- a/docs/DEVELOPING.md
+++ b/docs/DEVELOPING.md
@@ -25,39 +25,6 @@
Microsoft provides [evaluation developer VMs](https://developer.microsoft.com/en-us/windows/downloads/virtual-machines) with Windows 10 and Visual Studio pre-installed.
-### Building from a command line
-
-From a _Developer Command Prompt for VS 2019_:
-
-```cmd
-rem Restore NuGet packages
-rem nuget.exe is required for command line restore because msbuild doesn't support packages.config
-rem (see https://github.com/NuGet/Home/issues/7386)
-nuget restore Datadog.Trace.sln
-
-rem Build C# projects (Platform: always AnyCPU)
-msbuild Datadog.Trace.proj /t:BuildCsharp /p:Configuration=Release
-
-rem Build NuGet packages
-dotnet pack src\Datadog.Trace\Datadog.Trace.csproj
-dotnet pack src\Datadog.Trace.OpenTracing\Datadog.Trace.OpenTracing.csproj
-
-rem Build C++ projects
-rem The native profiler depends on the Datadog.Trace.ClrProfiler.Managed.Loader C# project so be sure that is built first
-msbuild Datadog.Trace.proj /t:BuildCpp /p:Configuration=Release;Platform=x64
-msbuild Datadog.Trace.proj /t:BuildCpp /p:Configuration=Release;Platform=x86
-
-rem Build MSI installer for Windows x64 (supports both x64 and x86 apps)
-msbuild Datadog.Trace.proj /t:msi /p:Configuration=Release;Platform=x64
-
-rem Build MSI installer for Windows x86 (supports x86 apps only)
-msbuild Datadog.Trace.proj /t:msi /p:Configuration=Release;Platform=x86
-
-rem Build tracer home directory for Windows.
-rem Valid values for property `Platform` are `x64`, `x86`, and `All`.
-msbuild Datadog.Trace.proj /t:CreateHomeDirectory /p:Configuration=Release;Platform=All
-```
-
## Linux and MacOS
### Minimum requirements
@@ -72,19 +39,6 @@ To build everything and run integration tests
- [Docker](https://docs.docker.com/engine/install/)
- [Docker Compose](https://docs.docker.com/compose/install/)
-### Building
-
-```sh
-./build.sh
-```
-
-For Windows make sure to add `msbuild` to your `PATH`.
-You can do it by adding to `~/.bashrc` something more or less like bellow:
-
-```sh
-PATH="$PATH:/c/Program Files (x86)/Microsoft Visual Studio/2019/Professional/MSBuild/Current/Bin"
-```
-
## Visual Studio Code
This repository contains example configuration for VS Code located under `.vscode.example`. You can copy it to `.vscode`.
@@ -111,6 +65,40 @@ cp -r .devcontainer.example .devcontainer
The Development Container configuration mixes [Docker in Docker](https://github.com/microsoft/vscode-dev-containers/tree/master/containers/docker-in-docker) and [C# (.NET)](https://github.com/microsoft/vscode-dev-containers/tree/master/containers/dotnet) definitions. Thanks to it you can use `docker` and `docker-compose` inside the container.
+## Building from a command line
+
+This repository uses [Nuke](https://nuke.build/) for build automation.
+
+Support plugins are available for:
+ - JetBrains ReSharper https://nuke.build/resharper
+ - JetBrains Rider https://nuke.build/rider
+ - Microsoft VisualStudio https://nuke.build/visualstudio
+ - Microsoft VSCode https://nuke.build/vscode
+
+Restore dotnet tools to prepare build tools for solution. This will install dotnet nuke tool locally.
+
+```cmd
+dotnet tool restore
+```
+
+To see a list of possible targets and configurations run:
+
+```cmd
+dotnet nuke --help
+```
+
+To build using default target run:
+
+```cmd
+dotnet nuke
+```
+
+To build using specific target run:
+
+```cmd
+dotnet nuke --target TargetNameHere
+```
+
## Integration tests
You can use [Docker Compose](https://docs.docker.com/compose/) with Linux containers to build Linux binaries and run the test suites. This works on both Windows, Linux and MacOS hosts.
diff --git a/poc.sh b/poc.sh
index 520894ea7a..44e49e4d12 100755
--- a/poc.sh
+++ b/poc.sh
@@ -16,7 +16,7 @@ function finish {
trap finish EXIT
# build managed and native code
-./build.sh
+./build_poc.sh
# start mongodb
docker run -d --rm --name mongo \
diff --git a/src/Datadog.Trace.ClrProfiler.Native/Datadog.Trace.ClrProfiler.Native.DLL.vcxproj b/src/Datadog.Trace.ClrProfiler.Native/Datadog.Trace.ClrProfiler.Native.DLL.vcxproj
index 38e2d46e2b..805c191d7f 100644
--- a/src/Datadog.Trace.ClrProfiler.Native/Datadog.Trace.ClrProfiler.Native.DLL.vcxproj
+++ b/src/Datadog.Trace.ClrProfiler.Native/Datadog.Trace.ClrProfiler.Native.DLL.vcxproj
@@ -1,5 +1,5 @@
-
+
Debug
diff --git a/src/Datadog.Trace.ClrProfiler.Native/Datadog.Trace.ClrProfiler.Native.vcxproj b/src/Datadog.Trace.ClrProfiler.Native/Datadog.Trace.ClrProfiler.Native.vcxproj
index 46b122dc4b..e7b8f604cc 100644
--- a/src/Datadog.Trace.ClrProfiler.Native/Datadog.Trace.ClrProfiler.Native.vcxproj
+++ b/src/Datadog.Trace.ClrProfiler.Native/Datadog.Trace.ClrProfiler.Native.vcxproj
@@ -1,5 +1,5 @@
-
+
Debug
diff --git a/test/Datadog.Trace.ClrProfiler.Native.Tests/Datadog.Trace.ClrProfiler.Native.Tests.vcxproj b/test/Datadog.Trace.ClrProfiler.Native.Tests/Datadog.Trace.ClrProfiler.Native.Tests.vcxproj
new file mode 100644
index 0000000000..2a5153d24b
--- /dev/null
+++ b/test/Datadog.Trace.ClrProfiler.Native.Tests/Datadog.Trace.ClrProfiler.Native.Tests.vcxproj
@@ -0,0 +1,199 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ {5728056a-51aa-4ff5-ad0c-e86e44e36102}
+ Win32Proj
+ 10.0
+ Application
+ v142
+ Unicode
+ v4.5
+ ..\..\src\Datadog.Trace.ClrProfiler.Native\lib\
+ x64
+ x86
+ $(LIB_PATH)fmt_$(LIB_PLATFORM)-windows-static\include;$(LIB_PATH)spdlog\include
+ $(LIB_PATH)fmt_$(LIB_PLATFORM)-windows-static\lib\fmt.lib
+ $(LIB_PATH)fmt_$(LIB_PLATFORM)-windows-static\debug\lib\fmtd.lib
+
+
+
+ true
+
+
+ true
+
+
+
+
+
+
+
+ bin\$(Configuration)\$(Platform)\
+ obj\$(Configuration)\$(Platform)\
+
+
+ bin\$(Configuration)\x86\
+ obj\$(Configuration)\x86\
+
+
+ bin\$(Configuration)\x86\
+ obj\$(Configuration)\x86\
+
+
+ bin\$(Configuration)\$(Platform)\
+ obj\$(Configuration)\$(Platform)\
+
+
+ $(OutDir)
+
+
+
+
+
+
+
+
+
+
+
+
+ Create
+ Create
+ Create
+ Create
+
+
+
+
+
+
+
+
+ {4b243cf1-4269-45c6-a238-1a9bfa58b8cc}
+
+
+ {fdb5c8d0-018d-4ff9-9680-c6a5078f819b}
+
+
+ {91b6272f-5780-4c94-8071-dbba7b4f67f3}
+
+
+
+
+
+
+
+
+
+
+ Disabled
+ EnableFastChecks
+ MultiThreadedDebug
+ stdcpp17
+ true
+ pch.h
+ Level3
+ true
+ $(LIB_INCLUDES);%(AdditionalIncludeDirectories)
+
+
+ true
+ Console
+ $(LIB_INCLUDES);%(AdditionalLibraryDirectories)
+ $(LIB_BINARIES);%(AdditionalDependencies)
+
+
+
+
+ Disabled
+ EnableFastChecks
+ MultiThreadedDebug
+ stdcpp17
+ false
+ true
+ pch.h
+ Level3
+ true
+ $(LIB_INCLUDES);%(AdditionalIncludeDirectories)
+
+
+ true
+ Console
+ $(LIB_INCLUDES);%(AdditionalLibraryDirectories)
+ $(LIB_BINARIES);%(AdditionalDependencies)
+
+
+
+
+ MultiThreaded
+ Level3
+ stdcpp17
+ AnySuitable
+ true
+ Speed
+ true
+ true
+ true
+ pch.h
+ true
+ $(LIB_INCLUDES);%(AdditionalIncludeDirectories)
+
+
+ true
+ Console
+ true
+ true
+ $(LIB_INCLUDES);%(AdditionalLibraryDirectories)
+ $(LIB_BINARIES);%(AdditionalDependencies)
+
+
+
+
+ MultiThreaded
+ Level3
+ stdcpp17
+ AnySuitable
+ true
+ Speed
+ true
+ true
+ true
+ pch.h
+ true
+ $(LIB_INCLUDES);%(AdditionalIncludeDirectories)
+
+
+ true
+ Console
+ true
+ true
+ $(LIB_INCLUDES);%(AdditionalLibraryDirectories)
+ $(LIB_BINARIES);%(AdditionalDependencies)
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
\ No newline at end of file
diff --git a/test/Datadog.Trace.ClrProfiler.Native.Tests/Directory.Build.props b/test/Datadog.Trace.ClrProfiler.Native.Tests/Directory.Build.props
new file mode 100644
index 0000000000..8c119d5413
--- /dev/null
+++ b/test/Datadog.Trace.ClrProfiler.Native.Tests/Directory.Build.props
@@ -0,0 +1,2 @@
+
+
diff --git a/test/Datadog.Trace.ClrProfiler.Native.Tests/clr_helper_test.cpp b/test/Datadog.Trace.ClrProfiler.Native.Tests/clr_helper_test.cpp
new file mode 100644
index 0000000000..331bb26c22
--- /dev/null
+++ b/test/Datadog.Trace.ClrProfiler.Native.Tests/clr_helper_test.cpp
@@ -0,0 +1,454 @@
+#include "pch.h"
+
+#include "../../src/Datadog.Trace.ClrProfiler.Native/clr_helpers.h"
+#include "test_helpers.h"
+
+using namespace trace;
+
+class CLRHelperTest : public ::CLRHelperTestBase {};
+
+TEST_F(CLRHelperTest, EnumeratesTypeDefs) {
+ std::vector expected_types = {
+ L"Microsoft.CodeAnalysis.EmbeddedAttribute",
+ L"System.Runtime.CompilerServices.IsReadOnlyAttribute",
+ L"Samples.ExampleLibrary.Class1",
+ L"Samples.ExampleLibrary.GenericTests.ComprehensiveCaller`2",
+ L"Samples.ExampleLibrary.GenericTests.GenericTarget`2",
+ L"Samples.ExampleLibrary.GenericTests.PointStruct",
+ L"Samples.ExampleLibrary.GenericTests.StructContainer`1",
+ L"Samples.ExampleLibrary.FakeClient.Biscuit`1",
+ L"Samples.ExampleLibrary.FakeClient.Biscuit",
+ L"Samples.ExampleLibrary.FakeClient.DogClient`2",
+ L"Samples.ExampleLibrary.FakeClient.DogTrick`1",
+ L"Samples.ExampleLibrary.FakeClient.DogTrick",
+ L"<>c",
+ L"Cookie",
+ L"d__4`2",
+ L"Raisin"};
+
+ std::vector actual_types;
+
+ for (auto& def : EnumTypeDefs(metadata_import_)) {
+ std::wstring name(256, 0);
+ DWORD name_sz = 0;
+ DWORD flags = 0;
+ mdToken extends = 0;
+ auto hr = metadata_import_->GetTypeDefProps(
+ def, name.data(), (DWORD)(name.size()), &name_sz, &flags, &extends);
+ ASSERT_TRUE(SUCCEEDED(hr));
+
+ if (name_sz > 0) {
+ name = name.substr(0, name_sz - 1);
+ actual_types.push_back(name);
+ }
+ }
+
+ EXPECT_EQ(expected_types, actual_types);
+}
+
+TEST_F(CLRHelperTest, EnumeratesAssemblyRefs) {
+ std::vector expected_assemblies = {
+ L"System.Runtime",
+ L"System.Collections",
+ L"System.Threading.Tasks",
+ L"System.Diagnostics.Debug"};
+ std::vector actual_assemblies;
+ for (auto& ref : EnumAssemblyRefs(assembly_import_)) {
+ auto name = GetReferencedAssemblyMetadata(assembly_import_, ref).name;
+ if (!name.empty()) {
+ actual_assemblies.push_back(name);
+ }
+ }
+ EXPECT_EQ(expected_assemblies, actual_assemblies);
+}
+
+TEST_F(CLRHelperTest, FiltersEnabledIntegrations) {
+ Integration i1 = {L"integration-1",
+ {{{},
+ {L"Samples.ExampleLibrary",
+ L"SomeType",
+ L"SomeMethod",
+ L"ReplaceTargetMethod",
+ min_ver_,
+ max_ver_,
+ {},
+ empty_sig_type_},
+ {}}}};
+ Integration i2 = {
+ L"integration-2",
+ {{{},
+ {L"Assembly.Two", L"SomeType", L"SomeMethod", L"ReplaceTargetMethod", min_ver_, max_ver_, {}, empty_sig_type_},
+ {}}}};
+ Integration i3 = {
+ L"integration-3",
+ {{{}, {L"System.Runtime", L"", L"", L"ReplaceTargetMethod", min_ver_, max_ver_, {}, empty_sig_type_}, {}}}};
+ std::vector all = {i1, i2, i3};
+ std::vector expected = {i1, i3};
+ std::vector disabled_integrations = {WStr("integration-2")};
+ auto actual = FilterIntegrationsByName(all, disabled_integrations);
+ EXPECT_EQ(actual, expected);
+}
+
+TEST_F(CLRHelperTest, FiltersIntegrationsByCaller) {
+ Integration i1 = {
+ L"integration-1",
+ {{{L"Assembly.One", L"SomeType", L"SomeMethod", L"ReplaceTargetMethod", min_ver_, max_ver_, {}, empty_sig_type_},
+ {},
+ {}}}};
+ Integration i2 = {
+ L"integration-2",
+ {{{L"Assembly.Two", L"SomeType", L"SomeMethod", L"ReplaceTargetMethod", min_ver_, max_ver_, {}, empty_sig_type_},
+ {},
+ {}}}};
+ Integration i3 = {L"integration-3", {{{}, {}, {}}}};
+ auto all = FlattenIntegrations({i1, i2, i3}, false);
+ auto expected = FlattenIntegrations({i1, i3}, false);
+ ModuleID manifest_module_id{};
+ AppDomainID app_domain_id{};
+ trace::AssemblyInfo assembly_info = { 1, L"Assembly.One", manifest_module_id, app_domain_id, L"AppDomain1"};
+ auto actual = FilterIntegrationsByCaller(all, assembly_info);
+ EXPECT_EQ(actual, expected);
+}
+
+TEST_F(CLRHelperTest, FiltersIntegrationsByTarget) {
+ Integration i1 = {L"integration-1",
+ {{{},
+ {L"Samples.ExampleLibrary",
+ L"SomeType",
+ L"SomeMethod",
+ L"ReplaceTargetMethod",
+ min_ver_,
+ max_ver_,
+ {},
+ empty_sig_type_},
+ {}}}};
+ Integration i2 = {
+ L"integration-2",
+ {{{},
+ {L"Assembly.Two", L"SomeType", L"SomeMethod", L"ReplaceTargetMethod", min_ver_, max_ver_, {}, empty_sig_type_},
+ {}}}};
+ Integration i3 = {
+ L"integration-3",
+ {{{}, {L"System.Runtime", L"", L"", L"ReplaceTargetMethod", min_ver_, max_ver_, {}, empty_sig_type_}, {}}}};
+ auto all = FlattenIntegrations({i1, i2, i3}, false);
+ auto expected = FlattenIntegrations({i1, i3}, false);
+ auto actual = FilterIntegrationsByTarget(all, assembly_import_);
+ EXPECT_EQ(actual, expected);
+}
+
+TEST_F(CLRHelperTest, FiltersFlattenedIntegrationMethodsByTargetAssembly) {
+ MethodReplacement included_method = {{},
+ {L"Samples.Included",
+ L"SomeType",
+ L"SomeMethod",
+ L"ReplaceTargetMethod",
+ min_ver_,
+ max_ver_,
+ {},
+ empty_sig_type_},
+ {}};
+
+ MethodReplacement excluded_method = {{},
+ {L"Samples.Excluded",
+ L"SomeType",
+ L"SomeMethod",
+ L"ReplaceTargetMethod",
+ min_ver_,
+ max_ver_,
+ {},
+ empty_sig_type_},
+ {}};
+
+ Integration mixed_integration = {L"integration-1", {included_method, excluded_method}};
+ Integration included_integration = {L"integration-2", {included_method}};
+ Integration excluded_integration = {L"integration-3", {excluded_method}};
+ auto all = FlattenIntegrations({mixed_integration, included_integration, excluded_integration}, false);
+ auto expected = FlattenIntegrations({{L"integration-1", {included_method}}, included_integration}, false);
+ auto actual = FilterIntegrationsByTargetAssemblyName(all, {L"Samples.Excluded"});
+ EXPECT_EQ(actual, expected);
+}
+
+TEST_F(CLRHelperTest, FiltersFlattenedIntegrationMethodsByTarget) {
+ MethodReference included = {L"Samples.ExampleLibrary",
+ L"SomeType",
+ L"SomeMethod",
+ L"ReplaceTargetMethod",
+ min_ver_,
+ max_ver_,
+ {},
+ empty_sig_type_};
+
+ MethodReference excluded = {L"Samples.ExampleLibrary",
+ L"SomeType",
+ L"SomeOtherMethod",
+ L"ReplaceTargetMethod",
+ Version(0, 0, 0, 0),
+ Version(0, 1, 0, 0),
+ {},
+ empty_sig_type_};
+
+ Integration i1 = {L"integration-1", {{{}, included, {}}, {{}, excluded, {}}}};
+ auto all = FlattenIntegrations({i1}, false);
+ auto filtered = FilterIntegrationsByTarget(all, assembly_import_);
+ bool foundExclusion = false;
+ for (auto& item : filtered) {
+ if (item.replacement.target_method == excluded) {
+ foundExclusion = true;
+ }
+ }
+ EXPECT_FALSE(foundExclusion)
+ << "Expected method within integration to be filtered by version.";
+}
+
+TEST_F(CLRHelperTest, GetsTypeInfoFromTypeDefs) {
+ std::set expected = {
+ L"Microsoft.CodeAnalysis.EmbeddedAttribute",
+ L"System.Runtime.CompilerServices.IsReadOnlyAttribute",
+ L"<>c",
+ L"d__4`2",
+ L"Cookie",
+ L"Raisin",
+ L"Samples.ExampleLibrary.Class1",
+ L"Samples.ExampleLibrary.FakeClient.Biscuit",
+ L"Samples.ExampleLibrary.FakeClient.Biscuit`1",
+ L"Samples.ExampleLibrary.FakeClient.DogClient`2",
+ L"Samples.ExampleLibrary.FakeClient.DogTrick",
+ L"Samples.ExampleLibrary.FakeClient.DogTrick`1",
+ L"Samples.ExampleLibrary.GenericTests.ComprehensiveCaller`2",
+ L"Samples.ExampleLibrary.GenericTests.GenericTarget`2",
+ L"Samples.ExampleLibrary.GenericTests.PointStruct",
+ L"Samples.ExampleLibrary.GenericTests.StructContainer`1"};
+ std::set actual;
+ for (auto& type_def : EnumTypeDefs(metadata_import_)) {
+ auto type_info = GetTypeInfo(metadata_import_, type_def);
+ if (type_info.IsValid()) {
+ actual.insert(type_info.name);
+ }
+ }
+ EXPECT_EQ(actual, expected);
+}
+
+TEST_F(CLRHelperTest, GetsTypeInfoFromTypeRefs) {
+ std::set expected = {
+ L"DebuggingModes",
+ L"Enumerator",
+ L"System.Array",
+ L"System.Attribute",
+ L"System.Collections.DictionaryEntry",
+ L"System.Collections.Generic.Dictionary`2",
+ L"System.Collections.Generic.IList`1",
+ L"System.Collections.Generic.List`1",
+ L"System.Diagnostics.DebuggableAttribute",
+#ifdef _DEBUG
+ L"System.Diagnostics.DebuggerBrowsableAttribute",
+ L"System.Diagnostics.DebuggerBrowsableState",
+#endif
+ L"System.Diagnostics.DebuggerHiddenAttribute",
+#ifdef _DEBUG
+ L"System.Diagnostics.DebuggerStepThroughAttribute",
+#endif
+ L"System.Exception",
+ L"System.Func`3",
+ L"System.Guid",
+ L"System.Int32",
+ L"System.Object",
+ L"System.Reflection.AssemblyCompanyAttribute",
+ L"System.Reflection.AssemblyConfigurationAttribute",
+ L"System.Reflection.AssemblyFileVersionAttribute",
+ L"System.Reflection.AssemblyInformationalVersionAttribute",
+ L"System.Reflection.AssemblyProductAttribute",
+ L"System.Reflection.AssemblyTitleAttribute",
+ L"System.Runtime.CompilerServices.AsyncStateMachineAttribute",
+ L"System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1",
+ L"System.Runtime.CompilerServices.CompilationRelaxationsAttribute",
+ L"System.Runtime.CompilerServices.CompilerGeneratedAttribute",
+ L"System.Runtime.CompilerServices.IAsyncStateMachine",
+ L"System.Runtime.CompilerServices.RuntimeCompatibilityAttribute",
+ L"System.Runtime.CompilerServices.TaskAwaiter",
+ L"System.Runtime.CompilerServices.TaskAwaiter`1",
+ L"System.Runtime.Versioning.TargetFrameworkAttribute",
+ L"System.RuntimeTypeHandle",
+ L"System.String",
+ L"System.Threading.Tasks.Task",
+ L"System.Threading.Tasks.Task`1",
+ L"System.Tuple`2",
+ L"System.Tuple`7",
+ L"System.Type",
+ L"System.ValueType"};
+ std::set actual;
+ for (auto& type_ref : EnumTypeRefs(metadata_import_)) {
+ auto type_info = GetTypeInfo(metadata_import_, type_ref);
+ if (type_info.IsValid()) {
+ actual.insert(type_info.name);
+ }
+ }
+ EXPECT_EQ(expected, actual);
+}
+
+TEST_F(CLRHelperTest, GetsTypeInfoFromModuleRefs) {
+ // TODO(cbd): figure out how to create a module ref, for now its empty
+ std::set expected = {};
+ std::set actual;
+ for (auto& module_ref : EnumModuleRefs(metadata_import_)) {
+ auto type_info = GetTypeInfo(metadata_import_, module_ref);
+ actual.insert(type_info.name);
+ }
+ EXPECT_EQ(actual, expected);
+}
+
+TEST_F(CLRHelperTest, GetsTypeInfoFromMethods) {
+ std::set expected = {
+ L"Microsoft.CodeAnalysis.EmbeddedAttribute",
+ L"System.Runtime.CompilerServices.IsReadOnlyAttribute",
+ L"<>c",
+ L"d__4`2",
+ L"Cookie",
+ L"Raisin",
+ L"Samples.ExampleLibrary.Class1",
+ L"Samples.ExampleLibrary.FakeClient.Biscuit",
+ L"Samples.ExampleLibrary.FakeClient.Biscuit`1",
+ L"Samples.ExampleLibrary.FakeClient.DogClient`2",
+ L"Samples.ExampleLibrary.FakeClient.DogTrick",
+ L"Samples.ExampleLibrary.FakeClient.DogTrick`1",
+ L"Samples.ExampleLibrary.GenericTests.ComprehensiveCaller`2",
+ L"Samples.ExampleLibrary.GenericTests.GenericTarget`2",
+ L"Samples.ExampleLibrary.GenericTests.PointStruct",
+ L"Samples.ExampleLibrary.GenericTests.StructContainer`1"};
+ std::set actual;
+ for (auto& type_def : EnumTypeDefs(metadata_import_)) {
+ for (auto& method_def : EnumMethods(metadata_import_, type_def)) {
+ auto type_info = GetTypeInfo(metadata_import_, method_def);
+ if (type_info.IsValid()) {
+ actual.insert(type_info.name);
+ }
+ }
+ }
+ EXPECT_EQ(actual, expected);
+}
+
+TEST_F(CLRHelperTest,
+ ReturnTypeIsValueTypeOrGenericReturnsCorrectlyForMethodDefs) {
+ std::set> expected = {
+ {L".ctor", L""},
+ {L"Add", L"System.Int32"},
+ {L"Multiply", L"System.Int32"},
+ {L"ToCustomString", L""},
+ {L"ToObject", L""},
+ {L"ToArray", L""},
+ {L"ToCustomArray", L""},
+ {L"ToMdArray", L""},
+ {L"ToJaggedArray", L""},
+ {L"ToList", L""},
+ {L"ToEnumerator", L"Enumerator"},
+ {L"ToDictionaryEntry", L"System.Collections.DictionaryEntry"},
+
+ // Primitive tests
+ {L"ToBool", L"System.Boolean"},
+ {L"ToChar", L"System.Char"},
+ {L"ToSByte", L"System.SByte"},
+ {L"ToByte", L"System.Byte"},
+ {L"ToInt16", L"System.Int16"},
+ {L"ToUInt16", L"System.UInt16"},
+ {L"ToInt32", L"System.Int32"},
+ {L"ToUInt32", L"System.UInt32"},
+ {L"ToInt64", L"System.Int64"},
+ {L"ToUInt64", L"System.UInt64"},
+ {L"ToSingle", L"System.Single"},
+ {L"ToDouble", L"System.Double"}};
+ std::set> actual;
+
+ for (auto& type_def : EnumTypeDefs(metadata_import_)) {
+ for (auto& method_def : EnumMethods(metadata_import_, type_def)) {
+ auto type_info = GetTypeInfo(metadata_import_, method_def);
+ if (type_info.IsValid() &&
+ type_info.name == L"Samples.ExampleLibrary.Class1") {
+ mdToken type_token = mdTokenNil;
+ auto target = GetFunctionInfo(metadata_import_, method_def);
+ bool result = ReturnTypeIsValueTypeOrGeneric(metadata_import_,
+ metadata_emit_,
+ assembly_emit_,
+ target.id,
+ target.signature,
+ &type_token) &&
+ type_token != mdTokenNil;
+ if (result) {
+ auto new_type_ref_info = GetTypeInfo(metadata_import_, type_token);
+ actual.insert({target.name, new_type_ref_info.name});
+ } else {
+ actual.insert({target.name, L""});
+ }
+ }
+ }
+ }
+ EXPECT_EQ(actual, expected);
+}
+
+TEST_F(CLRHelperTest,
+ ReturnTypeIsValueTypeOrGenericReturnsCorrectlyForMemberRefs) {
+ std::vector> expected = {
+ {L"ReturnT1", true},
+ {L"ReturnT1", true},
+ {L"ReturnT1", false},
+ {L"ReturnT1", true},
+ {L"ReturnT1", false},
+ {L"ReturnT1", true},
+
+ {L"ReturnT2", true},
+ {L"ReturnT2", true},
+ {L"ReturnT2", false},
+ {L"ReturnT2", true},
+ {L"ReturnT2", false},
+ {L"ReturnT2", true}};
+ std::vector> actual;
+
+ for (mdMemberRef current = mdtMemberRef + 1; metadata_import_->IsValidToken(current); current++) {
+ auto target = GetFunctionInfo(metadata_import_, current);
+ if (target.name == L"ReturnT1" || target.name == L"ReturnT2") {
+ mdToken type_token = mdTokenNil;
+ bool result = ReturnTypeIsValueTypeOrGeneric(metadata_import_,
+ metadata_emit_,
+ assembly_emit_,
+ target.id,
+ target.signature,
+ &type_token) &&
+ type_token != mdTokenNil;
+ actual.push_back({target.name, result});
+ }
+ }
+ EXPECT_EQ(actual, expected);
+}
+
+TEST_F(CLRHelperTest,
+ ReturnTypeIsValueTypeOrGenericReturnsCorrectlyForMethodSpecs) {
+ std::vector> expected = {
+ {L"ReturnM1", true},
+ {L"ReturnM1", true},
+ {L"ReturnM1", false},
+ {L"ReturnM1", true},
+ {L"ReturnM1", false},
+ {L"ReturnM1", true},
+
+ {L"ReturnM2", true},
+ {L"ReturnM2", true},
+ {L"ReturnM2", false},
+ {L"ReturnM2", true},
+ {L"ReturnM2", false},
+ {L"ReturnM2", true}};
+ std::vector> actual;
+
+ for (mdMethodSpec current = mdtMethodSpec + 1; metadata_import_->IsValidToken(current); current++) {
+ mdToken type_token = mdTokenNil;
+ auto target = GetFunctionInfo(metadata_import_, current);
+ if (target.name.find(L"ReturnM") != std::string::npos) {
+ bool result = ReturnTypeIsValueTypeOrGeneric(metadata_import_,
+ metadata_emit_,
+ assembly_emit_,
+ target.id,
+ target.signature,
+ &type_token) &&
+ type_token != mdTokenNil;
+ actual.push_back({target.name, result});
+ }
+ }
+ EXPECT_EQ(actual, expected);
+}
diff --git a/test/Datadog.Trace.ClrProfiler.Native.Tests/clr_helper_type_check_test.cpp b/test/Datadog.Trace.ClrProfiler.Native.Tests/clr_helper_type_check_test.cpp
new file mode 100644
index 0000000000..eca1cd944d
--- /dev/null
+++ b/test/Datadog.Trace.ClrProfiler.Native.Tests/clr_helper_type_check_test.cpp
@@ -0,0 +1,123 @@
+#include "pch.h"
+
+#include "../../src/Datadog.Trace.ClrProfiler.Native/clr_helpers.h"
+#include "test_helpers.h"
+
+using namespace trace;
+
+class CLRHelperTypeCheckTest : public ::CLRHelperTestBase {};
+
+
+TEST_F(CLRHelperTypeCheckTest, SimpleNoSignatureMethodHasOnlyVoid) {
+ std::vector expected = {
+ L"System.Void"};
+ std::vector actual;
+
+ const auto target = FunctionToTest(
+ WStr("Samples.ExampleLibrary.FakeClient.DogClient`2"), WStr("Silence"));
+
+ EXPECT_TRUE(target.name.size() > 1) << "Test target method not found.";
+
+ TryParseSignatureTypes(metadata_import_, target, actual);
+
+ EXPECT_EQ(expected, actual);
+}
+
+TEST_F(CLRHelperTypeCheckTest, GetsVeryComplexNestedGenericTypeStrings) {
+ std::vector expected = {
+ L"System.Void",
+ L"System.String",
+ L"System.Int32",
+ L"System.Byte[]",
+ L"System.Guid[][]",
+ L"T[][][]",
+ L"System.Collections.Generic.List`1",
+ L"System.Collections.Generic.List`1>",
+ L"System.Tuple`7, System.Int64>, System.Threading.Tasks.Task, System.Guid>",
+ L"System.Collections.Generic.Dictionary`2>>>"};
+ std::vector actual;
+
+ const auto target = FunctionToTest(
+ WStr("Samples.ExampleLibrary.FakeClient.DogClient`2"), WStr("Sit"));
+
+ EXPECT_TRUE(target.name.size() > 1) << "Test target method not found.";
+
+ TryParseSignatureTypes(metadata_import_, target, actual);
+
+ EXPECT_EQ(expected, actual);
+}
+
+
+TEST_F(CLRHelperTypeCheckTest, SimpleStringReturnWithNestedTypeParamsNoGenerics) {
+ std::vector expected = {
+ L"System.String", L"Samples.ExampleLibrary.FakeClient.Biscuit+Cookie",
+ L"Samples.ExampleLibrary.FakeClient.Biscuit+Cookie+Raisin"};
+ std::vector actual;
+
+ const auto target = FunctionToTest(
+ WStr("Samples.ExampleLibrary.FakeClient.DogClient`2"), WStr("TellMeIfTheCookieIsYummy"));
+
+ EXPECT_TRUE(target.name.size() > 1) << "Test target method not found.";
+
+ TryParseSignatureTypes(metadata_import_, target, actual);
+
+ EXPECT_EQ(expected, actual);
+}
+
+
+TEST_F(CLRHelperTypeCheckTest, SimpleClassReturnWithSimpleParamsNoGenerics) {
+ std::vector expected = {L"Samples.ExampleLibrary.FakeClient.Biscuit",
+ L"System.Guid", L"System.Int16",
+ L"Samples.ExampleLibrary.FakeClient.DogTrick"};
+ std::vector actual;
+
+ const auto target = FunctionToTest(
+ WStr("Samples.ExampleLibrary.FakeClient.DogClient`2"), WStr("Rollover"));
+
+ EXPECT_TRUE(target.name.size() > 1) << "Test target method not found.";
+
+ TryParseSignatureTypes(metadata_import_, target, actual);
+
+ EXPECT_EQ(expected, actual);
+}
+
+TEST_F(CLRHelperTypeCheckTest, GenericAsyncMethodWithNestedGenericTask) {
+ std::vector expected = {
+ L"System.Threading.Tasks.Task`1>",
+ L"System.Guid",
+ L"System.Int16",
+ L"Samples.ExampleLibrary.FakeClient.DogTrick`1",
+ L"T",
+ L"T",
+ };
+ std::vector actual;
+
+ const auto target = FunctionToTest(
+ WStr("Samples.ExampleLibrary.FakeClient.DogClient`2"), WStr("StayAndLayDown"));
+
+ EXPECT_TRUE(target.name.size() > 1) << "Test target method not found.";
+
+ TryParseSignatureTypes(metadata_import_, target, actual);
+
+ EXPECT_EQ(expected, actual);
+}
+
+TEST_F(CLRHelperTypeCheckTest, SuccessfullyParsesEverySignature) {
+ std::set expected_failures = {
+ L"Samples.ExampleLibrary.Class1.ToMdArray",
+ L"Samples.ExampleLibrary.Class1.ToEnumerator"
+ };
+ std::set actual_failures;
+ for (auto& type_def : EnumTypeDefs(metadata_import_)) {
+ for (auto& method_def : EnumMethods(metadata_import_, type_def)) {
+ auto target = GetFunctionInfo(metadata_import_, method_def);
+ std::vector actual;
+ auto success = TryParseSignatureTypes(metadata_import_, target, actual);
+ if (!success) {
+ actual_failures.insert(target.type.name + L"." + target.name);
+ }
+ }
+ }
+
+ EXPECT_EQ(expected_failures, actual_failures);
+}
\ No newline at end of file
diff --git a/test/Datadog.Trace.ClrProfiler.Native.Tests/integration_loader_test.cpp b/test/Datadog.Trace.ClrProfiler.Native.Tests/integration_loader_test.cpp
new file mode 100644
index 0000000000..07de71c28e
--- /dev/null
+++ b/test/Datadog.Trace.ClrProfiler.Native.Tests/integration_loader_test.cpp
@@ -0,0 +1,225 @@
+#include "pch.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "../../src/Datadog.Trace.ClrProfiler.Native/integration_loader.h"
+#include "../../src/Datadog.Trace.ClrProfiler.Native/environment_variables.h"
+
+using namespace trace;
+
+TEST(IntegrationLoaderTest, HandlesMissingFile) {
+ auto integrations = LoadIntegrationsFromFile(L"missing-file");
+ EXPECT_EQ(0, integrations.size());
+}
+
+TEST(IntegrationLoaderTest, HandlesInvalidIntegrationNoName) {
+ std::stringstream str("[{}]");
+ auto integrations = LoadIntegrationsFromStream(str);
+ // 0 because name is required
+ EXPECT_EQ(0, integrations.size());
+}
+
+TEST(IntegrationLoaderTest, HandlesInvalidIntegrationBadJson) {
+ std::stringstream str("[");
+ auto integrations = LoadIntegrationsFromStream(str);
+ EXPECT_EQ(0, integrations.size());
+}
+
+TEST(IntegrationLoaderTest, HandlesInvalidIntegrationNotAnObject) {
+ std::stringstream str("[1,2,3]");
+ auto integrations = LoadIntegrationsFromStream(str);
+ EXPECT_EQ(0, integrations.size());
+}
+
+TEST(IntegrationLoaderTest, HandlesInvalidIntegrationNotAnArray) {
+ std::stringstream str(R"TEXT(
+ {"name": "test-integration"}
+ )TEXT");
+ auto integrations = LoadIntegrationsFromStream(str);
+ EXPECT_EQ(0, integrations.size());
+}
+
+TEST(IntegrationLoaderTest, HandlesSingleIntegrationWithNoMethods) {
+ std::stringstream str(R"TEXT(
+ [{ "name": "test-integration" }]
+ )TEXT");
+
+ auto integrations = LoadIntegrationsFromStream(str);
+ EXPECT_EQ(1, integrations.size());
+ EXPECT_STREQ(L"test-integration", integrations[0].integration_name.c_str());
+ EXPECT_EQ(0, integrations[0].method_replacements.size());
+}
+
+TEST(IntegrationLoaderTest,
+ HandlesSingleIntegrationWithInvalidMethodReplacementType) {
+ std::stringstream str(R"TEXT(
+ [{ "name": "test-integration", "method_replacements": 1234 }]
+ )TEXT");
+
+ auto integrations = LoadIntegrationsFromStream(str);
+ EXPECT_EQ(1, integrations.size());
+ EXPECT_STREQ(L"test-integration", integrations[0].integration_name.c_str());
+ EXPECT_EQ(0, integrations[0].method_replacements.size());
+}
+
+TEST(IntegrationLoaderTest, HandlesSingleIntegrationWithMethodReplacements) {
+ std::stringstream str(R"TEXT(
+ [{
+ "name": "test-integration",
+ "method_replacements": [{
+ "caller": { },
+ "target": { "assembly": "Assembly.One", "type": "Type.One", "method": "Method.One", "minimum_major": 0, "minimum_minor": 1, "maximum_major": 10, "maximum_minor": 0 },
+ "wrapper": { "assembly": "Assembly.Two", "type": "Type.Two", "method": "Method.Two", "signature": [0, 1, 1, 28] }
+ }]
+ }]
+ )TEXT");
+
+ auto integrations = LoadIntegrationsFromStream(str);
+ EXPECT_EQ(1, integrations.size());
+ EXPECT_STREQ(L"test-integration", integrations[0].integration_name.c_str());
+}
+
+TEST(IntegrationLoaderTest, DoesNotCrashWithOutOfRangeVersion) {
+ std::stringstream str(R"TEXT(
+ [{
+ "name": "test-integration",
+ "method_replacements": [{
+ "caller": { },
+ "target": { "assembly": "Assembly.One", "type": "Type.One", "method": "Method.One", "minimum_major": 0, "minimum_minor": 1, "maximum_major": 75555, "maximum_minor": 0 },
+ "wrapper": { "assembly": "Assembly.Two", "type": "Type.Two", "method": "Method.Two", "signature": [0, 1, 1, 28] }
+ }]
+ }]
+ )TEXT");
+
+ auto integrations = LoadIntegrationsFromStream(str);
+ EXPECT_EQ(1, integrations.size());
+ EXPECT_STREQ(L"test-integration", integrations[0].integration_name.c_str());
+
+ EXPECT_EQ(1, integrations[0].method_replacements.size());
+ auto mr = integrations[0].method_replacements[0];
+ EXPECT_STREQ(L"", mr.caller_method.assembly.name.c_str());
+ EXPECT_STREQ(L"", mr.caller_method.type_name.c_str());
+ EXPECT_STREQ(L"", mr.caller_method.method_name.c_str());
+ EXPECT_STREQ(L"Assembly.One", mr.target_method.assembly.name.c_str());
+ EXPECT_STREQ(L"Type.One", mr.target_method.type_name.c_str());
+ EXPECT_STREQ(L"Method.One", mr.target_method.method_name.c_str());
+ EXPECT_STREQ(L"Assembly.Two", mr.wrapper_method.assembly.name.c_str());
+ EXPECT_STREQ(L"Type.Two", mr.wrapper_method.type_name.c_str());
+ EXPECT_STREQ(L"Method.Two", mr.wrapper_method.method_name.c_str());
+ EXPECT_EQ(std::vector({0, 1, 1, 28}),
+ mr.wrapper_method.method_signature.data);
+}
+
+TEST(IntegrationLoaderTest, HandlesSingleIntegrationWithMissingCaller) {
+ std::stringstream str(R"TEXT(
+ [{
+ "name": "test-integration",
+ "method_replacements": [{
+ "target": { "assembly": "Assembly.One", "type": "Type.One", "method": "Method.One", "minimum_major": 1, "minimum_minor": 2, "maximum_major": 10, "maximum_minor": 99 },
+ "wrapper": { "assembly": "Assembly.Two", "type": "Type.Two", "method": "Method.Two", "signature": [0, 1, 1, 28] }
+ }]
+ }]
+ )TEXT");
+
+ auto integrations = LoadIntegrationsFromStream(str);
+ EXPECT_EQ(1, integrations.size());
+ EXPECT_STREQ(L"test-integration", integrations[0].integration_name.c_str());
+
+ EXPECT_EQ(1, integrations[0].method_replacements.size());
+ auto mr = integrations[0].method_replacements[0];
+ EXPECT_STREQ(L"", mr.caller_method.assembly.name.c_str());
+ EXPECT_STREQ(L"", mr.caller_method.type_name.c_str());
+ EXPECT_STREQ(L"", mr.caller_method.method_name.c_str());
+ EXPECT_STREQ(L"Assembly.One", mr.target_method.assembly.name.c_str());
+ EXPECT_STREQ(L"Type.One", mr.target_method.type_name.c_str());
+ EXPECT_STREQ(L"Method.One", mr.target_method.method_name.c_str());
+ EXPECT_STREQ(L"Assembly.Two", mr.wrapper_method.assembly.name.c_str());
+ EXPECT_STREQ(L"Type.Two", mr.wrapper_method.type_name.c_str());
+ EXPECT_STREQ(L"Method.Two", mr.wrapper_method.method_name.c_str());
+ EXPECT_STREQ(L"Method.Two", mr.wrapper_method.method_name.c_str());
+ EXPECT_EQ(1, mr.target_method.min_version.major);
+ EXPECT_EQ(2, mr.target_method.min_version.minor);
+ EXPECT_EQ(0, mr.target_method.min_version.build);
+ EXPECT_EQ(10, mr.target_method.max_version.major);
+ EXPECT_EQ(99, mr.target_method.max_version.minor);
+ EXPECT_EQ(USHRT_MAX, mr.target_method.max_version.build);
+ EXPECT_EQ(std::vector({0, 1, 1, 28}),
+ mr.wrapper_method.method_signature.data);
+}
+
+TEST(IntegrationLoaderTest, HandlesSingleIntegrationWithInvalidTarget) {
+ std::stringstream str(R"TEXT(
+ [{
+ "name": "test-integration",
+ "method_replacements": [{
+ "target": 1234,
+ "wrapper": { "assembly": "Assembly.Two", "type": "Type.Two", "method": "Method.Two" }
+ }]
+ }]
+ )TEXT");
+
+ auto integrations = LoadIntegrationsFromStream(str);
+ EXPECT_EQ(1, integrations.size());
+ EXPECT_STREQ(L"test-integration", integrations[0].integration_name.c_str());
+
+ EXPECT_EQ(1, integrations[0].method_replacements.size());
+ auto mr = integrations[0].method_replacements[0];
+ EXPECT_STREQ(L"", mr.target_method.assembly.name.c_str());
+ EXPECT_STREQ(L"", mr.target_method.type_name.c_str());
+ EXPECT_STREQ(L"", mr.target_method.method_name.c_str());
+}
+
+TEST(IntegrationLoaderTest, LoadsFromEnvironment) {
+ auto tmpname1 = std::filesystem::temp_directory_path() / "test-1.json";
+ auto tmpname2 = std::filesystem::temp_directory_path() / "test-2.json";
+ std::ofstream f;
+ f.open(tmpname1);
+ f << R"TEXT(
+ [{ "name": "test-integration-1" }]
+ )TEXT";
+ f.close();
+ f.open(tmpname2);
+ f << R"TEXT(
+ [{ "name": "test-integration-2" }]
+ )TEXT";
+ f.close();
+
+ auto name = tmpname1.wstring() + L"," + tmpname2.wstring();
+
+ SetEnvironmentVariableW(trace::environment::integrations_path.data(), name.data());
+
+ std::vector expected_names = {L"test-integration-1",
+ L"test-integration-2"};
+ std::vector actual_names;
+ for (auto& integration : LoadIntegrationsFromEnvironment()) {
+ actual_names.push_back(integration.integration_name);
+ }
+ EXPECT_EQ(expected_names, actual_names);
+
+ std::filesystem::remove(tmpname1);
+ std::filesystem::remove(tmpname2);
+}
+
+TEST(IntegrationLoaderTest, DeserializesSignatureTypeArray) {
+ std::stringstream str(R"TEXT(
+ [{
+ "name": "test-integration",
+ "method_replacements": [{
+ "caller": { },
+ "target": { "assembly": "Assembly.One", "type": "Type.One", "method": "Method.One", "signature_types": ["System.Void", "_", "FakeClient.Pipeline'1"] },
+ "wrapper": { "assembly": "Assembly.Two", "type": "Type.Two", "method": "Method.One", "signature": [0, 1, 1, 28] }
+ }]
+ }]
+ )TEXT");
+
+ auto integrations = LoadIntegrationsFromStream(str);
+ const auto target = integrations[0].method_replacements[0].target_method;
+ EXPECT_STREQ(L"System.Void", target.signature_types[0].c_str());
+ EXPECT_STREQ(L"_", target.signature_types[1].c_str());
+ EXPECT_STREQ(L"FakeClient.Pipeline'1", target.signature_types[2].c_str());
+}
diff --git a/test/Datadog.Trace.ClrProfiler.Native.Tests/integration_test.cpp b/test/Datadog.Trace.ClrProfiler.Native.Tests/integration_test.cpp
new file mode 100644
index 0000000000..3bcd499253
--- /dev/null
+++ b/test/Datadog.Trace.ClrProfiler.Native.Tests/integration_test.cpp
@@ -0,0 +1,46 @@
+#include "pch.h"
+
+#include "../../src/Datadog.Trace.ClrProfiler.Native/integration.h"
+
+using namespace trace;
+
+TEST(IntegrationTest, AssemblyReference) {
+ AssemblyReference ref(
+ L"Some.Assembly, Version=1.2.3.4, Culture=notneutral, "
+ L"PublicKeyToken=0123456789abcdef");
+
+ EXPECT_EQ(ref.name, L"Some.Assembly");
+ EXPECT_EQ(ref.version, Version(1, 2, 3, 4));
+ EXPECT_EQ(ref.locale, L"notneutral");
+ EXPECT_EQ(ref.public_key,
+ PublicKey({0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}));
+}
+
+TEST(IntegrationTest, AssemblyReferenceNameOnly) {
+ AssemblyReference ref(L"Some.Assembly");
+
+ EXPECT_EQ(ref.name, L"Some.Assembly");
+ EXPECT_EQ(ref.version, Version(0, 0, 0, 0));
+ EXPECT_EQ(ref.locale, L"neutral");
+ EXPECT_EQ(ref.public_key, PublicKey({0, 0, 0, 0, 0, 0, 0, 0}));
+}
+
+TEST(IntegrationTest, AssemblyReferenceInvalidPublicKey) {
+ AssemblyReference ref(L"Some.Assembly, PublicKeyToken=xyz");
+ EXPECT_EQ(ref.public_key, PublicKey({0, 0, 0, 0, 0, 0, 0, 0}));
+}
+
+TEST(IntegrationTest, AssemblyReferenceNullPublicKey) {
+ AssemblyReference ref(L"Some.Assembly, PublicKeyToken=null");
+ EXPECT_EQ(ref.public_key, PublicKey({0, 0, 0, 0, 0, 0, 0, 0}));
+}
+
+TEST(IntegrationTest, AssemblyReferencePartialVersion) {
+ AssemblyReference ref(L"Some.Assembly, Version=1.2.3");
+ EXPECT_EQ(ref.version, Version(0, 0, 0, 0));
+}
+
+TEST(IntegrationTest, AssemblyReferenceInvalidVersion) {
+ AssemblyReference ref(L"Some.Assembly, Version=xyz");
+ EXPECT_EQ(ref.version, Version(0, 0, 0, 0));
+}
\ No newline at end of file
diff --git a/test/Datadog.Trace.ClrProfiler.Native.Tests/metadata_builder_test.cpp b/test/Datadog.Trace.ClrProfiler.Native.Tests/metadata_builder_test.cpp
new file mode 100644
index 0000000000..10554e1751
--- /dev/null
+++ b/test/Datadog.Trace.ClrProfiler.Native.Tests/metadata_builder_test.cpp
@@ -0,0 +1,157 @@
+#include "pch.h"
+
+#include "../../src/Datadog.Trace.ClrProfiler.Native/clr_helpers.h"
+#include "../../src/Datadog.Trace.ClrProfiler.Native/metadata_builder.h"
+
+using namespace trace;
+
+class MetadataBuilderTest : public ::testing::Test {
+ protected:
+ ModuleMetadata* module_metadata_ = nullptr;
+ MetadataBuilder* metadata_builder_ = nullptr;
+ ICLRStrongName* strong_name_ = nullptr;
+ IMetaDataDispenser* metadata_dispenser_ = nullptr;
+ std::vector empty_sig_type_;
+
+ void SetUp() override {
+ ICLRMetaHost* metahost = nullptr;
+ HRESULT hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost,
+ (void**)&metahost);
+ ASSERT_TRUE(SUCCEEDED(hr));
+
+ IEnumUnknown* runtimes = nullptr;
+ hr = metahost->EnumerateInstalledRuntimes(&runtimes);
+ ASSERT_TRUE(SUCCEEDED(hr));
+
+ ICLRRuntimeInfo* latest = nullptr;
+ ICLRRuntimeInfo* runtime = nullptr;
+ ULONG fetched = 0;
+ while ((hr = runtimes->Next(1, (IUnknown**)&runtime, &fetched)) == S_OK &&
+ fetched > 0) {
+ latest = runtime;
+ }
+
+ hr =
+ latest->GetInterface(CLSID_CorMetaDataDispenser, IID_IMetaDataDispenser,
+ (void**)&metadata_dispenser_);
+ ASSERT_TRUE(SUCCEEDED(hr));
+
+ hr = latest->GetInterface(CLSID_CLRStrongName, IID_ICLRStrongName,
+ (void**)&strong_name_);
+ ASSERT_TRUE(SUCCEEDED(hr));
+
+ ComPtr metadataInterfaces;
+ hr = metadata_dispenser_->OpenScope(L"Samples.ExampleLibrary.dll",
+ ofReadWriteMask, IID_IMetaDataImport2,
+ metadataInterfaces.GetAddressOf());
+ ASSERT_TRUE(SUCCEEDED(hr)) << "File not found: Samples.ExampleLibrary.dll";
+
+ const auto metadataImport =
+ metadataInterfaces.As(IID_IMetaDataImport2);
+ const auto metadataEmit =
+ metadataInterfaces.As(IID_IMetaDataEmit);
+ const auto assemblyImport = metadataInterfaces.As(
+ IID_IMetaDataAssemblyImport);
+ const auto assemblyEmit =
+ metadataInterfaces.As(IID_IMetaDataAssemblyEmit);
+
+ const std::wstring assemblyName = L"Samples.ExampleLibrary";
+
+ const AppDomainID app_domain_id{};
+
+ GUID module_version_id;
+ metadataImport->GetScopeProps(NULL, 1024, nullptr, &module_version_id);
+
+ const std::vector integrations;
+ module_metadata_ =
+ new ModuleMetadata(metadataImport, metadataEmit, assemblyImport, assemblyEmit,
+ assemblyName, app_domain_id, module_version_id, integrations, NULL);
+
+ mdModule module;
+ hr = metadataImport->GetModuleFromScope(&module);
+ ASSERT_TRUE(SUCCEEDED(hr));
+
+ metadata_builder_ =
+ new MetadataBuilder(*module_metadata_, module, metadataImport,
+ metadataEmit, assemblyImport, assemblyEmit);
+
+ hr = metadata_builder_->EmitAssemblyRef(trace::AssemblyReference(
+ L"Samples.ExampleLibraryTracer, Version=1.0.0.0"));
+ ASSERT_TRUE(SUCCEEDED(hr));
+ }
+
+ void TearDown() override {
+ delete this->module_metadata_;
+ delete this->metadata_builder_;
+ }
+};
+
+TEST_F(MetadataBuilderTest, StoresWrapperMemberRef) {
+
+ const auto min_ver = Version(0, 0, 0, 0);
+ const auto max_ver = Version(USHRT_MAX, USHRT_MAX, USHRT_MAX, USHRT_MAX);
+ const MethodReference ref1(L"", L"", L"", L"", min_ver, max_ver, {}, empty_sig_type_);
+ const MethodReference ref2(L"Samples.ExampleLibrary", L"Class1", L"Add", L"", min_ver, max_ver, {}, empty_sig_type_);
+ const MethodReference ref3(L"Samples.ExampleLibrary", L"Class1", L"Add", L"ReplaceTargetMethod", min_ver, max_ver, {}, empty_sig_type_);
+ const MethodReplacement mr1(ref1, ref2, ref3);
+ auto hr = metadata_builder_->StoreWrapperMethodRef(mr1);
+ ASSERT_EQ(S_OK, hr);
+
+ mdMemberRef tmp;
+ auto key_failed = module_metadata_->IsFailedWrapperMemberKey(
+ L"[Samples.ExampleLibrary]Class1.Add_vMin_0.0.0.0_vMax_65535.65535.65535.65535");
+ auto ok = module_metadata_->TryGetWrapperMemberRef(
+ L"[Samples.ExampleLibrary]Class1.Add_vMin_0.0.0.0_vMax_65535.65535.65535.65535", tmp);
+ EXPECT_TRUE(ok);
+ EXPECT_FALSE(key_failed);
+ EXPECT_NE(tmp, 0);
+
+ tmp = 0;
+ key_failed = module_metadata_->IsFailedWrapperMemberKey(
+ L"[Samples.ExampleLibrary]Class2.Add_vMin_0.0.0.0_vMax_65535.65535.65535.65535");
+ ok = module_metadata_->TryGetWrapperMemberRef(
+ L"[Samples.ExampleLibrary]Class2.Add_vMin_0.0.0.0_vMax_65535.65535.65535.65535", tmp);
+ EXPECT_FALSE(ok);
+ EXPECT_FALSE(key_failed);
+ EXPECT_EQ(tmp, 0);
+}
+
+TEST_F(MetadataBuilderTest, StoresWrapperMemberRefForSeparateAssembly) {
+ const auto min_ver = Version(0, 0, 0, 0);
+ const auto max_ver = Version(USHRT_MAX, USHRT_MAX, USHRT_MAX, USHRT_MAX);
+ const MethodReference ref1(L"", L"", L"", L"", min_ver, max_ver, {},
+ empty_sig_type_);
+ const MethodReference ref2(L"Samples.ExampleLibrary", L"Class1", L"Add", L"", min_ver, max_ver, {}, empty_sig_type_);
+ const MethodReference ref3(L"Samples.ExampleLibraryTracer", L"Class1", L"Add", L"ReplaceTargetMethod",
+ min_ver, max_ver, {}, empty_sig_type_);
+ const MethodReplacement mr1(ref1, ref2, ref3);
+ auto hr = metadata_builder_->StoreWrapperMethodRef(mr1);
+ ASSERT_EQ(S_OK, hr);
+
+ mdMemberRef tmp;
+ auto key_failed = module_metadata_->IsFailedWrapperMemberKey(L"[Samples.ExampleLibraryTracer]Class1.Add_vMin_0.0.0.0_vMax_65535.65535.65535.65535");
+ auto ok = module_metadata_->TryGetWrapperMemberRef(
+ L"[Samples.ExampleLibraryTracer]Class1.Add_vMin_0.0.0.0_vMax_65535.65535.65535.65535", tmp);
+ EXPECT_TRUE(ok);
+ EXPECT_FALSE(key_failed);
+ EXPECT_NE(tmp, 0);
+}
+
+TEST_F(MetadataBuilderTest, StoresWrapperMemberRefRecordsFailure) {
+ const auto min_ver = Version(0, 0, 0, 0);
+ const auto max_ver = Version(USHRT_MAX, USHRT_MAX, USHRT_MAX, USHRT_MAX);
+ const MethodReference ref1(L"", L"", L"", L"", min_ver, max_ver, {},
+ empty_sig_type_);
+ const MethodReference ref2(L"Samples.ExampleLibrary", L"Class1", L"Add", L"", min_ver, max_ver, {}, empty_sig_type_);
+ const MethodReference ref3(L"Samples.ExampleLibraryTracer.AssemblyDoesNotExist", L"Class1", L"Add", L"ReplaceTargetMethod",
+ min_ver, max_ver, {}, empty_sig_type_);
+ const MethodReplacement mr1(ref1, ref2, ref3);
+ auto hr = metadata_builder_->StoreWrapperMethodRef(mr1);
+ ASSERT_NE(S_OK, hr);
+
+ auto key_failed = module_metadata_->IsFailedWrapperMemberKey(L"[Samples.ExampleLibraryTracer.AssemblyDoesNotExist]Class1.Add_vMin_0.0.0.0_vMax_65535.65535.65535.65535");
+ EXPECT_TRUE(key_failed);
+
+ key_failed = module_metadata_->IsFailedWrapperMemberKey(L"[Samples.ExampleLibraryTracer]Class1.Add_vMin_0.0.0.0_vMax_65535.65535.65535.65535");
+ EXPECT_FALSE(key_failed);
+}
\ No newline at end of file
diff --git a/test/Datadog.Trace.ClrProfiler.Native.Tests/packages.config b/test/Datadog.Trace.ClrProfiler.Native.Tests/packages.config
new file mode 100644
index 0000000000..d974a133db
--- /dev/null
+++ b/test/Datadog.Trace.ClrProfiler.Native.Tests/packages.config
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/test/Datadog.Trace.ClrProfiler.Native.Tests/pch.cpp b/test/Datadog.Trace.ClrProfiler.Native.Tests/pch.cpp
new file mode 100644
index 0000000000..97b544ec11
--- /dev/null
+++ b/test/Datadog.Trace.ClrProfiler.Native.Tests/pch.cpp
@@ -0,0 +1,6 @@
+//
+// pch.cpp
+// Include the standard header and generate the precompiled header.
+//
+
+#include "pch.h"
diff --git a/test/Datadog.Trace.ClrProfiler.Native.Tests/pch.h b/test/Datadog.Trace.ClrProfiler.Native.Tests/pch.h
new file mode 100644
index 0000000000..b4b50837d6
--- /dev/null
+++ b/test/Datadog.Trace.ClrProfiler.Native.Tests/pch.h
@@ -0,0 +1,18 @@
+//
+// pch.h
+// Header for standard system include files.
+//
+
+#ifndef DD_CLR_PROFILER_TESTS_PCH_H_
+#define DD_CLR_PROFILER_TESTS_PCH_H_
+
+#define GTEST_LANG_CXX11 1
+
+#include "gtest/gtest.h"
+
+#include
+#include
+#include
+#pragma comment(lib, "mscoree.lib")
+
+#endif
\ No newline at end of file
diff --git a/test/Datadog.Trace.ClrProfiler.Native.Tests/test_helpers.h b/test/Datadog.Trace.ClrProfiler.Native.Tests/test_helpers.h
new file mode 100644
index 0000000000..1365ffdc98
--- /dev/null
+++ b/test/Datadog.Trace.ClrProfiler.Native.Tests/test_helpers.h
@@ -0,0 +1,77 @@
+
+#pragma once
+#include "../../src/Datadog.Trace.ClrProfiler.Native/com_ptr.h"
+#include "../../src/Datadog.Trace.ClrProfiler.Native/integration.h"
+
+namespace trace {
+
+class CLRHelperTestBase : public ::testing::Test {
+ protected:
+ IMetaDataDispenser* metadata_dispenser_;
+ ComPtr metadata_import_;
+ ComPtr metadata_emit_;
+ ComPtr assembly_import_;
+ ComPtr assembly_emit_;
+ Version min_ver_ = Version(0, 0, 0, 0);
+ Version max_ver_ = Version(USHRT_MAX, USHRT_MAX, USHRT_MAX, USHRT_MAX);
+ std::vector empty_sig_type_;
+
+ void LoadMetadataDependencies() {
+ ICLRMetaHost* metahost = nullptr;
+ HRESULT hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost,
+ (void**)&metahost);
+ ASSERT_TRUE(SUCCEEDED(hr));
+
+ IEnumUnknown* runtimes = nullptr;
+ hr = metahost->EnumerateInstalledRuntimes(&runtimes);
+ ASSERT_TRUE(SUCCEEDED(hr));
+
+ ICLRRuntimeInfo* latest = nullptr;
+ ICLRRuntimeInfo* runtime = nullptr;
+ ULONG fetched = 0;
+ while ((hr = runtimes->Next(1, (IUnknown**)&runtime, &fetched)) == S_OK &&
+ fetched > 0) {
+ latest = runtime;
+ }
+
+ hr =
+ latest->GetInterface(CLSID_CorMetaDataDispenser, IID_IMetaDataDispenser,
+ (void**)&metadata_dispenser_);
+ ASSERT_TRUE(SUCCEEDED(hr));
+
+ ComPtr metadataInterfaces;
+ hr = metadata_dispenser_->OpenScope(L"Samples.ExampleLibrary.dll",
+ ofReadWriteMask, IID_IMetaDataImport2,
+ metadataInterfaces.GetAddressOf());
+ ASSERT_TRUE(SUCCEEDED(hr)) << "Samples.ExampleLibrary.dll was not found.";
+
+ metadata_import_ =
+ metadataInterfaces.As(IID_IMetaDataImport2);
+ metadata_emit_ =
+ metadataInterfaces.As(IID_IMetaDataEmit);
+ assembly_import_ =
+ metadataInterfaces.As(IID_IMetaDataAssemblyImport);
+ assembly_emit_ =
+ metadataInterfaces.As(IID_IMetaDataAssemblyEmit);
+ }
+
+ void SetUp() override { LoadMetadataDependencies(); }
+
+ FunctionInfo FunctionToTest(const WSTRING& type_name, const WSTRING& method_name) const {
+ for (auto& type_def : EnumTypeDefs(metadata_import_)) {
+ for (auto& method_def : EnumMethods(metadata_import_, type_def)) {
+ auto target = GetFunctionInfo(metadata_import_, method_def);
+ if (target.type.name != type_name) {
+ continue;
+ }
+ if (target.name != method_name) {
+ continue;
+ }
+ return target;
+ }
+ }
+
+ return {};
+ }
+};
+} // namespace trace
diff --git a/test/Datadog.Trace.ClrProfiler.Native.Tests/version_struct_test.cpp b/test/Datadog.Trace.ClrProfiler.Native.Tests/version_struct_test.cpp
new file mode 100644
index 0000000000..9d3bc587e0
--- /dev/null
+++ b/test/Datadog.Trace.ClrProfiler.Native.Tests/version_struct_test.cpp
@@ -0,0 +1,50 @@
+#include "pch.h"
+
+#include "../../src/Datadog.Trace.ClrProfiler.Native/integration.h"
+
+using namespace trace;
+
+TEST(VersionStructTest, Major2GreaterThanMajor1) {
+ const auto v1 = Version(1, 0, 0, 0);
+ const auto v2 = Version(2, 0, 0, 0);
+ ASSERT_TRUE(v2 > v1) << "Expected v2 to be greater than v1.";
+}
+
+TEST(VersionStructTest, Minor2GreaterThanMinor1) {
+ const auto v0_1 = Version(0, 1, 0, 0);
+ const auto v0_2 = Version(0, 2, 0, 0);
+ ASSERT_TRUE(v0_2 > v0_1) << "Expected v0_2 to be greater than v0_1.";
+}
+
+TEST(VersionStructTest, Build1GreaterThanBuild0) {
+ const auto v0_0_0 = Version(0, 0, 0, 0);
+ const auto v0_0_1 = Version(0, 0, 1, 0);
+ ASSERT_TRUE(v0_0_1 > v0_0_0) << "Expected v0_0_1 to be greater than v0_0_0.";
+}
+
+TEST(VersionStructTest, Major1LessThanMajor2) {
+ const auto v1 = Version(1, 0, 0, 0);
+ const auto v2 = Version(2, 0, 0, 0);
+ ASSERT_TRUE(v1 < v2) << "Expected v1 to be less than v2.";
+}
+
+TEST(VersionStructTest, Minor1LessThanMinor2) {
+ const auto v0_1 = Version(0, 1, 0, 0);
+ const auto v0_2 = Version(0, 2, 0, 0);
+ ASSERT_TRUE(v0_1 < v0_2) << "Expected v0_1 to be less than v0_2.";
+}
+
+TEST(VersionStructTest, Build0LessThanBuild1) {
+ const auto v0_0_0 = Version(0, 0, 0, 0);
+ const auto v0_0_1 = Version(0, 0, 1, 0);
+ ASSERT_TRUE(v0_0_0 < v0_0_1) << "Expected v0_0_0 to be less than v0_0_1.";
+}
+
+TEST(VersionStructTest, RevisionDoesNotAffectComparison) {
+ const auto v1_2_3_4 = Version(1, 2, 3, 4);
+ const auto v1_2_3_5 = Version(1, 2, 3, 5);
+ ASSERT_FALSE(v1_2_3_5 > v1_2_3_4)
+ << "Expected v1_2_3_5 to not be greater than v1_2_3_4.";
+ ASSERT_FALSE(v1_2_3_4 < v1_2_3_5)
+ << "Expected v1_2_3_4 to not be less than v1_2_3_5.";
+}
diff --git a/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/Class1.cs b/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/Class1.cs
new file mode 100644
index 0000000000..c8a7e6a600
--- /dev/null
+++ b/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/Class1.cs
@@ -0,0 +1,132 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Samples.ExampleLibrary
+{
+ public class Class1
+ {
+ public int Add(int x, int y)
+ {
+ return x + y;
+ }
+
+ public virtual int Multiply(int x, int y)
+ {
+ return x * y;
+ }
+
+ public Func Divide = (int x, int y) => x / y;
+
+ public string ToCustomString()
+ {
+ return "Custom";
+ }
+
+ public object ToObject()
+ {
+ return this;
+ }
+
+ public Class1[] ToArray()
+ {
+ return new Class1[] { this };
+ }
+
+ public Array ToCustomArray()
+ {
+ var lengthsArray = new int[2] { 5, 10 };
+ var lowerBoundsArray = new int[2] { 20, 15 };
+ return Array.CreateInstance(typeof(Class1), lengthsArray, lowerBoundsArray);
+ }
+
+ public Class1[, ,] ToMdArray()
+ {
+ return new Class1[4, 2, 3];
+ }
+
+ public Class1[][] ToJaggedArray()
+ {
+ return new Class1[][]
+ {
+ new Class1[] { this },
+ new Class1[] { null, null }
+ };
+ }
+
+ public List ToList()
+ {
+ return new List() { this };
+ }
+
+ public List.Enumerator ToEnumerator()
+ {
+ return ToList().GetEnumerator();
+ }
+
+ public DictionaryEntry ToDictionaryEntry()
+ {
+ return new DictionaryEntry("Class1", this);
+ }
+
+ public bool ToBool()
+ {
+ return false;
+ }
+
+ public char ToChar()
+ {
+ return 'b';
+ }
+
+ public sbyte ToSByte()
+ {
+ return 0x1;
+ }
+
+ public byte ToByte()
+ {
+ return 0x1;
+ }
+
+ public Int16 ToInt16()
+ {
+ return 16;
+ }
+
+ public UInt16 ToUInt16()
+ {
+ return 16;
+ }
+
+ public Int32 ToInt32()
+ {
+ return 32;
+ }
+
+ public UInt32 ToUInt32()
+ {
+ return 32;
+ }
+
+ public Int64 ToInt64()
+ {
+ return 64;
+ }
+
+ public UInt64 ToUInt64()
+ {
+ return 64;
+ }
+
+ public float ToSingle()
+ {
+ return 0.1f;
+ }
+
+ public double ToDouble()
+ {
+ return 0.1;
+ }
+ }
+}
diff --git a/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/FakeClient/Biscuit.cs b/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/FakeClient/Biscuit.cs
new file mode 100644
index 0000000000..30d3011ad9
--- /dev/null
+++ b/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/FakeClient/Biscuit.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+
+namespace Samples.ExampleLibrary.FakeClient
+{
+ public class Biscuit : Biscuit
+ {
+ public T Reward { get; set; }
+ }
+
+ public class Biscuit
+ {
+ public Guid Id { get; set; }
+
+ public string Message { get; set; }
+
+ public List Treats { get; set; } = new List();
+
+ public class Cookie
+ {
+ public bool IsYummy { get; set; }
+
+ public class Raisin
+ {
+ public bool IsPurple { get; set; }
+ }
+ }
+ }
+}
diff --git a/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/FakeClient/DogClient.cs b/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/FakeClient/DogClient.cs
new file mode 100644
index 0000000000..87838dac2a
--- /dev/null
+++ b/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/FakeClient/DogClient.cs
@@ -0,0 +1,76 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace Samples.ExampleLibrary.FakeClient
+{
+ public class DogClient
+ {
+ public void Silence()
+ {
+ Task.Delay(1).Wait();
+ }
+
+ public string TellMeIfTheCookieIsYummy(Biscuit.Cookie cookie, Biscuit.Cookie.Raisin raisin)
+ {
+ if (cookie.IsYummy)
+ {
+ if (raisin.IsPurple)
+ {
+ return "Yes, it is yummy, with purple raisins.";
+ }
+
+ return "Yes, it is yummy, with white raisins.";
+ }
+
+ return "No, it is not yummy";
+ }
+
+ public void Sit(
+ string message,
+ int howManyTimes,
+ byte[] whatEvenIs = null,
+ Guid[][] whatEvenIsThis = null,
+ T1[][][] whatEvenIsThisT = null,
+ List evenMoreWhatIsThis = null,
+ List> previousTricks = null,
+ Tuple, long>, Task, Guid> tuple = null,
+ Dictionary>>> whatAmIDoing = null)
+ {
+ for (var i = 0; i < howManyTimes; i++)
+ {
+ message +=
+ message
+ + whatEvenIs?.ToString()
+ + whatEvenIsThis?.ToString()
+ + whatEvenIsThisT?.ToString()
+ + evenMoreWhatIsThis?.GetType()
+ + previousTricks?.GetType()
+ + tuple?.GetType()
+ + whatAmIDoing?.GetType();
+ }
+ }
+
+ public Biscuit Rollover(Guid clientId, short timesToRun, DogTrick trick)
+ {
+ var biscuit = new Biscuit
+ {
+ Id = clientId,
+ Message = trick.Message
+ };
+
+ Sit("Sit!", timesToRun);
+
+ return biscuit;
+ }
+
+ public async Task> StayAndLayDown(Guid clientId, short timesToRun, DogTrick trick, TM1 extraTreat, TM2 extraExtraTreat)
+ {
+ await Task.Delay(5);
+ var biscuit = new Biscuit();
+ biscuit.Treats.Add(extraTreat);
+ biscuit.Treats.Add(extraExtraTreat);
+ return await Task.FromResult(biscuit);
+ }
+ }
+}
diff --git a/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/FakeClient/DogTrick.cs b/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/FakeClient/DogTrick.cs
new file mode 100644
index 0000000000..8470ad7263
--- /dev/null
+++ b/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/FakeClient/DogTrick.cs
@@ -0,0 +1,14 @@
+namespace Samples.ExampleLibrary.FakeClient
+{
+ public class DogTrick
+ {
+ public string Message { get; set; }
+
+ public T Reward { get; set; }
+ }
+
+ public class DogTrick
+ {
+ public string Message { get; set; }
+ }
+}
diff --git a/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/GenericTests/ComprehensiveCaller.cs b/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/GenericTests/ComprehensiveCaller.cs
new file mode 100644
index 0000000000..974fa8b6e7
--- /dev/null
+++ b/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/GenericTests/ComprehensiveCaller.cs
@@ -0,0 +1,136 @@
+using System;
+using System.Threading.Tasks;
+
+namespace Samples.ExampleLibrary.GenericTests
+{
+ public class ComprehensiveCaller
+ {
+ #region CallReturnM1
+ public void CallReturnM1WithCallerTypeArgs(GenericTarget target, CallerT1 input1, CallerT2 input2)
+ {
+ target.ReturnM1(input1, input2);
+ }
+
+ public void CallReturnM1WithCallerTypeArgsReversed(GenericTarget target, CallerT1 input1, CallerT2 input2)
+ {
+ target.ReturnM1(input2, input1);
+ }
+
+ public void CallReturnM1WithClass(GenericTarget target, Exception input1, CallerT2 input2)
+ {
+ target.ReturnM1(input1, input2);
+ }
+
+ public void CallReturnM1WithStruct(GenericTarget target, PointStruct input1, CallerT2 input2)
+ {
+ target.ReturnM1(input1, input2);
+ }
+
+ public void CallReturnM1WithReferenceTypeGenericInstantiation(GenericTarget target, Task input1, CallerT2 input2)
+ {
+ target.ReturnM1, CallerT2>(input1, input2);
+ }
+
+ public void CallReturnM1WithValueTypeGenericInstantiation(GenericTarget target, StructContainer input1, CallerT2 input2)
+ {
+ target.ReturnM1, CallerT2>(input1, input2);
+ }
+ #endregion
+
+ #region CallReturnM2
+ public void CallReturnM2WithCallerTypeArgs(GenericTarget target, CallerT1 input1, CallerT2 input2)
+ {
+ target.ReturnM2(input1, input2);
+ }
+
+ public void CallReturnM2WithCallerTypeArgsReversed(GenericTarget target, CallerT1 input1, CallerT2 input2)
+ {
+ target.ReturnM2(input2, input1);
+ }
+
+ public void CallReturnM2WithClass(GenericTarget target, CallerT1 input1, Exception input2)
+ {
+ target.ReturnM2(input1, input2);
+ }
+
+ public void CallReturnM2WithStruct(GenericTarget target, CallerT1 input1, PointStruct input2)
+ {
+ target.ReturnM2(input1, input2);
+ }
+
+ public void CallReturnM2WithReferenceTypeGenericInstantiation(GenericTarget target, CallerT1 input1, Task input2)
+ {
+ target.ReturnM2>(input1, input2);
+ }
+
+ public void CallReturnM2WithValueTypeGenericInstantiation(GenericTarget target, CallerT1 input1, StructContainer input2)
+ {
+ target.ReturnM2>(input1, input2);
+ }
+ #endregion
+
+ #region CallReturnT1
+ public void CallReturnT1WithCallerTypeArgs(GenericTarget target, object input)
+ {
+ target.ReturnT1(input);
+ }
+
+ public void CallReturnT1WithCallerTypeArgsReversed(GenericTarget target, object input)
+ {
+ target.ReturnT1(input);
+ }
+
+ public void CallReturnT1WithClass(GenericTarget target, object input)
+ {
+ target.ReturnT1(input);
+ }
+
+ public void CallReturnT1WithStruct(GenericTarget target, object input)
+ {
+ target.ReturnT1(input);
+ }
+
+ public void CallReturnT1WithReferenceTypeGenericInstantiation(GenericTarget, CallerT2> target, object input)
+ {
+ target.ReturnT1(input);
+ }
+
+ public void CallReturnT1WithValueTypeGenericInstantiation(GenericTarget, CallerT2> target, object input)
+ {
+ target.ReturnT1(input);
+ }
+ #endregion
+
+ #region CallReturnT2
+ public void CallReturnT2WithCallerTypeArgs(GenericTarget target, object input)
+ {
+ target.ReturnT2(input);
+ }
+
+ public void CallReturnT2WithCallerTypeArgsReversed(GenericTarget target, object input)
+ {
+ target.ReturnT2(input);
+ }
+
+ public void CallReturnT2WithClass(GenericTarget target, object input)
+ {
+ target.ReturnT2(input);
+ }
+
+ public void CallReturnT2WithStruct(GenericTarget, PointStruct> target, object input)
+ {
+ target.ReturnT2(input);
+ }
+
+ public void CallReturnT2WithReferenceTypeGenericInstantiation(GenericTarget>>, Task> target, object input)
+ {
+ target.ReturnT2(input);
+ }
+
+ public void CallReturnT2WithValueTypeGenericInstantiation(GenericTarget> target, object input)
+ {
+ target.ReturnT2(input);
+ }
+ #endregion
+ }
+}
diff --git a/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/GenericTests/GenericTarget.cs b/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/GenericTests/GenericTarget.cs
new file mode 100644
index 0000000000..9807e5c9df
--- /dev/null
+++ b/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/GenericTests/GenericTarget.cs
@@ -0,0 +1,25 @@
+namespace Samples.ExampleLibrary.GenericTests
+{
+ public class GenericTarget
+ {
+ public M1 ReturnM1(M1 input1, M2 input2)
+ {
+ return input1;
+ }
+
+ public M2 ReturnM2(M1 input1, M2 input2)
+ {
+ return input2;
+ }
+
+ public T1 ReturnT1(object input)
+ {
+ return (T1)input;
+ }
+
+ public T2 ReturnT2(object input)
+ {
+ return (T2)input;
+ }
+ }
+}
diff --git a/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/GenericTests/PointStruct.cs b/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/GenericTests/PointStruct.cs
new file mode 100644
index 0000000000..62e327d71d
--- /dev/null
+++ b/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/GenericTests/PointStruct.cs
@@ -0,0 +1,14 @@
+namespace Samples.ExampleLibrary.GenericTests
+{
+ public struct PointStruct
+ {
+ public int X;
+ public int Y;
+
+ public PointStruct(int x, int y)
+ {
+ X = x;
+ Y = y;
+ }
+ }
+}
diff --git a/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/GenericTests/StructContainer.cs b/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/GenericTests/StructContainer.cs
new file mode 100644
index 0000000000..fd1e62f23b
--- /dev/null
+++ b/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/GenericTests/StructContainer.cs
@@ -0,0 +1,17 @@
+using System.Collections.Generic;
+
+namespace Samples.ExampleLibrary.GenericTests
+{
+ public struct StructContainer
+ {
+ public List Items { get; }
+
+ public long Id { get; }
+
+ public StructContainer(long id, List items)
+ {
+ Id = id;
+ Items = items;
+ }
+ }
+}
diff --git a/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/GlobalSuppressions.cs b/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/GlobalSuppressions.cs
new file mode 100644
index 0000000000..fa229a1f4d
--- /dev/null
+++ b/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/GlobalSuppressions.cs
@@ -0,0 +1,15 @@
+// This file is used by Code Analysis to maintain SuppressMessage
+// attributes that are applied to this project.
+// Project-level suppressions either have no target or are given
+// a specific target and scoped to a namespace, type, member, etc.
+
+using System.Diagnostics.CodeAnalysis;
+
+[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Reviewed.")]
+[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:Elements should appear in the correct order", Justification = "Reviewed.")]
+[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "Reviewed.")]
+[assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1001:Commas should be spaced correctly", Justification = "Reviewed.")]
+[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1121:Use built-in type alias", Justification = "Reviewed.>")]
+[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1314:Type parameter names should begin with T", Justification = "Reviewed.")]
+[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1124:Do not use regions", Justification = "Reviewed.")]
+[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Reviewed.")]
diff --git a/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/Samples.ExampleLibrary.csproj b/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/Samples.ExampleLibrary.csproj
new file mode 100644
index 0000000000..6a3e57d200
--- /dev/null
+++ b/test/test-applications/integrations/dependency-libs/Samples.ExampleLibrary/Samples.ExampleLibrary.csproj
@@ -0,0 +1,7 @@
+
+
+
+ netstandard1.0
+
+
+
diff --git a/test/test-applications/integrations/dependency-libs/Samples.ExampleLibraryTracer/Class1.cs b/test/test-applications/integrations/dependency-libs/Samples.ExampleLibraryTracer/Class1.cs
new file mode 100644
index 0000000000..b9df0afdd9
--- /dev/null
+++ b/test/test-applications/integrations/dependency-libs/Samples.ExampleLibraryTracer/Class1.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace Samples.ExampleLibraryTracer
+{
+ public class Class1
+ {
+ public int Add(int x, int y)
+ {
+ return 2 * (x + y);
+ }
+
+ public virtual int Multiply(int x, int y)
+ {
+ return 2 * (x * y);
+ }
+ }
+}
diff --git a/test/test-applications/integrations/dependency-libs/Samples.ExampleLibraryTracer/Samples.ExampleLibraryTracer.csproj b/test/test-applications/integrations/dependency-libs/Samples.ExampleLibraryTracer/Samples.ExampleLibraryTracer.csproj
new file mode 100644
index 0000000000..6a3e57d200
--- /dev/null
+++ b/test/test-applications/integrations/dependency-libs/Samples.ExampleLibraryTracer/Samples.ExampleLibraryTracer.csproj
@@ -0,0 +1,7 @@
+
+
+
+ netstandard1.0
+
+
+