diff --git a/src/Agent/NewRelic/Profiler/ConfigurationTest/InstrumentationConfigurationTest.cpp b/src/Agent/NewRelic/Profiler/ConfigurationTest/InstrumentationConfigurationTest.cpp index 7af24e0f7a..405745a3b8 100644 --- a/src/Agent/NewRelic/Profiler/ConfigurationTest/InstrumentationConfigurationTest.cpp +++ b/src/Agent/NewRelic/Profiler/ConfigurationTest/InstrumentationConfigurationTest.cpp @@ -15,7 +15,7 @@ namespace NewRelic { namespace Profiler { namespace Configuration { namespace Te class MockTokenResolver : public SignatureParser::ITokenResolver { public: - MockTokenResolver(const std::wstring& typeString = L"MyNamespace.MyClass") : _typeString(typeString) {} + MockTokenResolver(const std::wstring& typeString = L"MyNamespace.MyClass") : _typeString(typeString), _typeGenericArgumentCount(0) {} virtual std::wstring GetTypeStringsFromTypeDefOrRefOrSpecToken(uint32_t /*typeDefOrRefOrSPecToken*/) override { @@ -926,5 +926,49 @@ namespace NewRelic { namespace Profiler { namespace Configuration { namespace Te Assert::IsFalse(instrumentationPoint == nullptr); } + TEST_METHOD(does_not_match_when_assembly_is_mscorlib) + { + InstrumentationXmlSetPtr xmlSet(new InstrumentationXmlSet()); + xmlSet->emplace(L"filename", L"\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + "); + InstrumentationConfiguration instrumentation(xmlSet); + auto function = std::make_shared(); + function->_assemblyName = _X("mscorlib"); + auto instrumentationPoint = instrumentation.TryGetInstrumentationPoint(function); + Assert::IsTrue(instrumentationPoint == nullptr); + } + + TEST_METHOD(does_not_match_when_assembly_is_SystemPrivateCoreLib) + { + InstrumentationXmlSetPtr xmlSet(new InstrumentationXmlSet()); + xmlSet->emplace(L"filename", L"\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + "); + InstrumentationConfiguration instrumentation(xmlSet); + auto function = std::make_shared(); + function->_assemblyName = _X("System.Private.CoreLib"); + auto instrumentationPoint = instrumentation.TryGetInstrumentationPoint(function); + Assert::IsTrue(instrumentationPoint == nullptr); + } + }; }}}} diff --git a/src/Agent/NewRelic/Profiler/MethodRewriterTest/AgentCallStyleTests.cpp b/src/Agent/NewRelic/Profiler/MethodRewriterTest/AgentCallStyleTests.cpp index 2f4acdd603..6383d25c7d 100644 --- a/src/Agent/NewRelic/Profiler/MethodRewriterTest/AgentCallStyleTests.cpp +++ b/src/Agent/NewRelic/Profiler/MethodRewriterTest/AgentCallStyleTests.cpp @@ -37,6 +37,26 @@ namespace NewRelic { namespace Profiler { namespace MethodRewriter{ namespace Te RunTest(_X("true"), _X("true"), AgentCallStyle::Strategy::Reflection); } + TEST_METHOD(AgentCallStrategy_EnvironmentVariable_0) + { + RunTest(_X("0"), _X("false"), AgentCallStyle::Strategy::InAgentCache); + } + + TEST_METHOD(AgentCallStrategy_EnvironmentVariable_1) + { + RunTest(_X("1"), _X("false"), AgentCallStyle::Strategy::AppDomainCache); + } + + TEST_METHOD(AgentCallStrategy_EnvironmentVariable_Empty) + { + RunTest(_X(""), _X("false"), AgentCallStyle::Strategy::InAgentCache); + } + + TEST_METHOD(AgentCallStrategy_EnvironmentVariable_NotSet) + { + RunTestNoEnvironmentVariablesSet(AgentCallStyle::Strategy::InAgentCache); + } + TEST_METHOD(AgentCallStrategy_ToString_InAgentCache) { const xstring_t expectedValue = _X("In Agent Cache"); @@ -59,22 +79,19 @@ namespace NewRelic { namespace Profiler { namespace MethodRewriter{ namespace Te void RunTest(const xstring_t& legacyCachingEnabled, const xstring_t& disableAppDomainCache, const AgentCallStyle::Strategy expectedStrategy) { auto systemCalls = std::make_shared(); - systemCalls->EnvironmentVariableResult = [&legacyCachingEnabled, &disableAppDomainCache](const xstring_t& variableName) - { - if (variableName == _X("NEW_RELIC_ENABLE_LEGACY_CACHING")) - { - return std::make_unique(legacyCachingEnabled); - } - else if (variableName == _X("NEW_RELIC_DISABLE_APPDOMAIN_CACHING")) - { - return std::make_unique(disableAppDomainCache); - } - else - { - return std::make_unique(_X("")); - } - }; + systemCalls->SetEnvironmentVariable(_X("NEW_RELIC_ENABLE_LEGACY_CACHING"), legacyCachingEnabled); + systemCalls->SetEnvironmentVariable(_X("NEW_RELIC_DISABLE_APPDOMAIN_CACHING"), disableAppDomainCache); + + AgentCallStyle agentCallStyle(systemCalls); + + const auto callStrategy = agentCallStyle.GetConfiguredCallingStrategy(); + Assert::AreEqual(static_cast(expectedStrategy), static_cast(callStrategy)); + } + + void RunTestNoEnvironmentVariablesSet(const AgentCallStyle::Strategy expectedStrategy) + { + auto systemCalls = std::make_shared(); AgentCallStyle agentCallStyle(systemCalls); const auto callStrategy = agentCallStyle.GetConfiguredCallingStrategy(); diff --git a/src/Agent/NewRelic/Profiler/MethodRewriterTest/FunctionManipulatorTest.cpp b/src/Agent/NewRelic/Profiler/MethodRewriterTest/FunctionManipulatorTest.cpp index 4e14456d90..75c3a6a57d 100644 --- a/src/Agent/NewRelic/Profiler/MethodRewriterTest/FunctionManipulatorTest.cpp +++ b/src/Agent/NewRelic/Profiler/MethodRewriterTest/FunctionManipulatorTest.cpp @@ -11,6 +11,8 @@ #include "MockSystemCalls.h" #include "../MethodRewriter/FunctionManipulator.h" #include "../MethodRewriter/InstrumentFunctionManipulator.h" +#include "../MethodRewriter/ApiFunctionManipulator.h" +#include "../MethodRewriter/HelperFunctionManipulator.h" using namespace Microsoft::VisualStudio::CppUnitTestFramework; @@ -25,15 +27,201 @@ namespace NewRelic { namespace Profiler { namespace MethodRewriter { namespace T FunctionManipulator manipulator(function, false, AgentCallStyle::Strategy::InAgentCache); } - TEST_METHOD(instrument_minimal_method) + TEST_METHOD(instrument_api_method_netframework_inagentcache) { auto function = std::make_shared(); - InstrumentFunctionManipulator manipulator(function, std::make_shared(nullptr, L""), false, AgentCallStyle::Strategy::InAgentCache); + ApiFunctionManipulator manipulator(function, std::make_shared(nullptr, _X("")), false, AgentCallStyle::Strategy::InAgentCache); + + manipulator.InstrumentApi(); + } + + TEST_METHOD(instrument_api_method_netframework_appdomaincache) + { + auto function = std::make_shared(); + ApiFunctionManipulator manipulator(function, std::make_shared(nullptr, _X("")), false, AgentCallStyle::Strategy::AppDomainCache); + + manipulator.InstrumentApi(); + } + + TEST_METHOD(instrument_api_method_netframework_reflection) + { + auto function = std::make_shared(); + ApiFunctionManipulator manipulator(function, std::make_shared(nullptr, _X("")), false, AgentCallStyle::Strategy::Reflection); + + manipulator.InstrumentApi(); + } + + TEST_METHOD(instrument_api_method_coreclr_inagentcache) + { + auto function = std::make_shared(); + function->_isCoreClr = true; + ApiFunctionManipulator manipulator(function, std::make_shared(nullptr, _X("")), true, AgentCallStyle::Strategy::InAgentCache); + + manipulator.InstrumentApi(); + } + + TEST_METHOD(instrument_api_method_coreclr_appdomaincache) + { + auto function = std::make_shared(); + function->_isCoreClr = true; + ApiFunctionManipulator manipulator(function, std::make_shared(nullptr, _X("")), true, AgentCallStyle::Strategy::AppDomainCache); + + manipulator.InstrumentApi(); + } + + TEST_METHOD(instrument_api_method_coreclr_reflection) + { + auto function = std::make_shared(); + function->_isCoreClr = true; + ApiFunctionManipulator manipulator(function, std::make_shared(nullptr, _X("")), true, AgentCallStyle::Strategy::Reflection); + + manipulator.InstrumentApi(); + } + + TEST_METHOD(instrument_minimal_method_netframework_inagentcache) + { + auto function = std::make_shared(); + InstrumentFunctionManipulator manipulator(function, std::make_shared(nullptr, _X("")), false, AgentCallStyle::Strategy::InAgentCache); + + auto instrumentationPoint = CreateInstrumentationPointThatMatchesFunction(function); + manipulator.InstrumentDefault(instrumentationPoint); + } + + TEST_METHOD(instrument_minimal_method_netframework_appdomaincache) + { + auto function = std::make_shared(); + InstrumentFunctionManipulator manipulator(function, std::make_shared(nullptr, _X("")), false, AgentCallStyle::Strategy::AppDomainCache); + + auto instrumentationPoint = CreateInstrumentationPointThatMatchesFunction(function); + manipulator.InstrumentDefault(instrumentationPoint); + } + + TEST_METHOD(instrument_minimal_method_netframework_reflection) + { + auto function = std::make_shared(); + InstrumentFunctionManipulator manipulator(function, std::make_shared(nullptr, _X("")), false, AgentCallStyle::Strategy::Reflection); + + auto instrumentationPoint = CreateInstrumentationPointThatMatchesFunction(function); + manipulator.InstrumentDefault(instrumentationPoint); + } + + TEST_METHOD(instrument_minimal_method_coreclr_inagentcache) + { + auto function = std::make_shared(); + InstrumentFunctionManipulator manipulator(function, std::make_shared(nullptr, _X("")), true, AgentCallStyle::Strategy::InAgentCache); + + auto instrumentationPoint = CreateInstrumentationPointThatMatchesFunction(function); + manipulator.InstrumentDefault(instrumentationPoint); + } + + TEST_METHOD(instrument_minimal_method_coreclr_appdomaincache) + { + auto function = std::make_shared(); + InstrumentFunctionManipulator manipulator(function, std::make_shared(nullptr, _X("")), true, AgentCallStyle::Strategy::AppDomainCache); auto instrumentationPoint = CreateInstrumentationPointThatMatchesFunction(function); manipulator.InstrumentDefault(instrumentationPoint); } + TEST_METHOD(instrument_minimal_method_coreclr_reflection) + { + auto function = std::make_shared(); + InstrumentFunctionManipulator manipulator(function, std::make_shared(nullptr, _X("")), true, AgentCallStyle::Strategy::Reflection); + + auto instrumentationPoint = CreateInstrumentationPointThatMatchesFunction(function); + manipulator.InstrumentDefault(instrumentationPoint); + } + + TEST_METHOD(helper_method_unsupported_method) + { + auto function = std::make_shared(); + function->_functionName = _X("ThisMethodShouldNotExist"); + HelperFunctionManipulator manipulator(function, true, AgentCallStyle::Strategy::InAgentCache); + + manipulator.InstrumentHelper(); + } + + TEST_METHOD(helper_method_LoadAssemblyOrThrow) + { + auto function = std::make_shared(); + function->_functionName = _X("LoadAssemblyOrThrow"); + HelperFunctionManipulator manipulator(function, true, AgentCallStyle::Strategy::InAgentCache); + + manipulator.InstrumentHelper(); + } + + TEST_METHOD(helper_method_GetTypeViaReflectionOrThrow) + { + auto function = std::make_shared(); + function->_functionName = _X("GetTypeViaReflectionOrThrow"); + HelperFunctionManipulator manipulator(function, true, AgentCallStyle::Strategy::InAgentCache); + + manipulator.InstrumentHelper(); + } + + TEST_METHOD(helper_method_GetMethodViaReflectionOrThrow) + { + auto function = std::make_shared(); + function->_functionName = _X("GetMethodViaReflectionOrThrow"); + HelperFunctionManipulator manipulator(function, true, AgentCallStyle::Strategy::InAgentCache); + + manipulator.InstrumentHelper(); + } + + TEST_METHOD(helper_method_StoreMethodInAppDomainStorageOrThrow) + { + auto function = std::make_shared(); + function->_functionName = _X("StoreMethodInAppDomainStorageOrThrow"); + HelperFunctionManipulator manipulator(function, true, AgentCallStyle::Strategy::InAgentCache); + + manipulator.InstrumentHelper(); + } + + TEST_METHOD(helper_method_GetMethodFromAppDomainStorage) + { + auto function = std::make_shared(); + function->_functionName = _X("GetMethodFromAppDomainStorage"); + HelperFunctionManipulator manipulator(function, true, AgentCallStyle::Strategy::InAgentCache); + + manipulator.InstrumentHelper(); + } + + TEST_METHOD(helper_method_GetMethodFromAppDomainStorageOrReflectionOrThrow) + { + auto function = std::make_shared(); + function->_functionName = _X("GetMethodFromAppDomainStorageOrReflectionOrThrow"); + HelperFunctionManipulator manipulator(function, true, AgentCallStyle::Strategy::InAgentCache); + + manipulator.InstrumentHelper(); + } + + TEST_METHOD(helper_method_EnsureInitialized) + { + auto function = std::make_shared(); + function->_functionName = _X("EnsureInitialized"); + HelperFunctionManipulator manipulator(function, true, AgentCallStyle::Strategy::InAgentCache); + + manipulator.InstrumentHelper(); + } + + TEST_METHOD(helper_method_GetMethodCacheLookupMethod) + { + auto function = std::make_shared(); + function->_functionName = _X("GetMethodCacheLookupMethod"); + HelperFunctionManipulator manipulator(function, true, AgentCallStyle::Strategy::InAgentCache); + + manipulator.InstrumentHelper(); + } + + TEST_METHOD(helper_method_GetMethodInfoFromAgentCache) + { + auto function = std::make_shared(); + function->_functionName = _X("GetMethodInfoFromAgentCache"); + HelperFunctionManipulator manipulator(function, true, AgentCallStyle::Strategy::InAgentCache); + + manipulator.InstrumentHelper(); + } + //TEST_METHOD(test_method_with_no_code) //{ // Assert::Fail(L"Test not implemented."); diff --git a/src/Agent/NewRelic/Profiler/MethodRewriterTest/InstrumentorsTest.cpp b/src/Agent/NewRelic/Profiler/MethodRewriterTest/InstrumentorsTest.cpp new file mode 100644 index 0000000000..c07dc9c42a --- /dev/null +++ b/src/Agent/NewRelic/Profiler/MethodRewriterTest/InstrumentorsTest.cpp @@ -0,0 +1,434 @@ +// Copyright 2020 New Relic, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include "UnreferencedFunctions.h" + +#include "MockFunction.h" +#include "../MethodRewriter/Instrumentors.h" + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +namespace NewRelic { namespace Profiler { namespace MethodRewriter { namespace Test +{ + TEST_CLASS(InstrumentatorsTest) + { + public: + TEST_METHOD(DefaultInstrumentor_ShouldNotTraceWhenFunctionShouldNotBeTraced) + { + auto function = std::make_shared(); + function->_functionName = _X("NotInstrumentedMethod"); + function->_shouldTrace = false; + + DefaultInstrumentor instrumentor; + + auto result = instrumentor.Instrument(function, GetInstrumentationSettings(), false, AgentCallStyle::Strategy::InAgentCache); + + Assert::IsFalse(result); + } + + TEST_METHOD(DefaultInstrumentor_ShouldTraceWhenFunctionShouldBeTraced) + { + auto function = std::make_shared(); + function->_functionName = _X("NotInstrumentedMethod"); + function->_shouldTrace = true; + + DefaultInstrumentor instrumentor; + + auto result = instrumentor.Instrument(function, GetInstrumentationSettings(), false, AgentCallStyle::Strategy::InAgentCache); + + Assert::IsTrue(result); + } + + TEST_METHOD(DefaultInstrumentor_ShouldNotTraceWhenFunctionHasTdSequentialLayout) + { + auto function = std::make_shared(); + function->_classAttributes = CorTypeAttr::tdSequentialLayout; + + DefaultInstrumentor instrumentor; + + auto result = instrumentor.Instrument(function, GetInstrumentationSettings(), false, AgentCallStyle::Strategy::InAgentCache); + + Assert::IsFalse(result); + } + + TEST_METHOD(DefaultInstrumentor_ShouldNotTraceWhenFunctionHasMdSpecialName) + { + auto function = std::make_shared(); + function->_methodAttributes = CorMethodAttr::mdSpecialName; + + DefaultInstrumentor instrumentor; + + auto result = instrumentor.Instrument(function, GetInstrumentationSettings(), false, AgentCallStyle::Strategy::InAgentCache); + + Assert::IsFalse(result); + } + + TEST_METHOD(DefaultInstrumentor_ShouldTraceWhenFunctionHasMdSpecialNameAndIsConstructor) + { + auto function = std::make_shared(); + function->_functionName = _X(".ctor"); + function->_methodAttributes = CorMethodAttr::mdSpecialName | CorMethodAttr::mdRTSpecialName; + function->_shouldTrace = true; + + DefaultInstrumentor instrumentor; + + auto result = instrumentor.Instrument(function, GetInstrumentationSettings(), false, AgentCallStyle::Strategy::InAgentCache); + + Assert::IsTrue(result); + } + + TEST_METHOD(DefaultInstrumentor_ShouldNotTraceWhenFunctionHasMdPInvokeImpl) + { + auto function = std::make_shared(); + function->_methodAttributes = CorMethodAttr::mdPinvokeImpl; + + DefaultInstrumentor instrumentor; + + auto result = instrumentor.Instrument(function, GetInstrumentationSettings(), false, AgentCallStyle::Strategy::InAgentCache); + + Assert::IsFalse(result); + } + + TEST_METHOD(DefaultInstrumentor_ShouldNotTraceWhenFunctionHasMdUnmanagedExport) + { + auto function = std::make_shared(); + function->_methodAttributes = CorMethodAttr::mdUnmanagedExport; + + DefaultInstrumentor instrumentor; + + auto result = instrumentor.Instrument(function, GetInstrumentationSettings(), false, AgentCallStyle::Strategy::InAgentCache); + + Assert::IsFalse(result); + } + + TEST_METHOD(DefaultInstrumentor_ShouldNotTraceWhenFunctionShouldInjectInstrumentation) + { + auto function = std::make_shared(); + // This flag is confusing because it seems to do the opposite of what the name suggests but it is used + // to track the difference between an initial JIT and a ReJIT. We inject the instrumentation on a ReJIT. + function->_shouldInjectMethodInstrumentation = true; + + DefaultInstrumentor instrumentor; + + auto result = instrumentor.Instrument(function, GetInstrumentationSettings(), false, AgentCallStyle::Strategy::InAgentCache); + + Assert::IsFalse(result); + } + + TEST_METHOD(DefaultInstrumentor_ShouldNotTraceWhenFunctionIsNotValid) + { + auto function = std::make_shared(); + function->_isValid = false; + + DefaultInstrumentor instrumentor; + + auto result = instrumentor.Instrument(function, GetInstrumentationSettings(), false, AgentCallStyle::Strategy::InAgentCache); + + Assert::IsFalse(result); + } + + TEST_METHOD(ApiInstrumentor_ShouldNotInstrumentWhenTypeIsNotTheStubApiType) + { + auto function = std::make_shared(); + function->_typeName = _X("NotTheApi"); + + ApiInstrumentor instrumentor; + + auto result = instrumentor.Instrument(function, GetInstrumentationSettings(), false, AgentCallStyle::Strategy::InAgentCache); + + Assert::IsFalse(result); + } + + TEST_METHOD(ApiInstrumentor_ShouldInstrumentWhenTypeIsTheStubApiType) + { + auto function = std::make_shared(); + function->_typeName = _X("NewRelic.Api.Agent.NewRelic"); + + ApiInstrumentor instrumentor; + + auto result = instrumentor.Instrument(function, GetInstrumentationSettings(), false, AgentCallStyle::Strategy::InAgentCache); + + Assert::IsTrue(result); + } + + TEST_METHOD(ApiInstrumentor_ShouldNotInstrumentWhenMethodIsTheStaticConstructor) + { + auto function = std::make_shared(); + function->_typeName = _X("NewRelic.Api.Agent.NewRelic"); + function->_functionName = _X(".cctor"); + + ApiInstrumentor instrumentor; + + auto result = instrumentor.Instrument(function, GetInstrumentationSettings(), false, AgentCallStyle::Strategy::InAgentCache); + + Assert::IsFalse(result); + } + + TEST_METHOD(ApiInstrumentor_ShouldNotInstrumentWhenMethodIsGetAgent) + { + auto function = std::make_shared(); + function->_typeName = _X("NewRelic.Api.Agent.NewRelic"); + function->_functionName = _X("GetAgent"); + + ApiInstrumentor instrumentor; + + auto result = instrumentor.Instrument(function, GetInstrumentationSettings(), false, AgentCallStyle::Strategy::InAgentCache); + + Assert::IsFalse(result); + } + + TEST_METHOD(HelperInstrumentor_ShouldNotInstrumentInCoreClrWhenAssemblyIsMscorlib) + { + auto function = std::make_shared(); + function->_assemblyName = function->_moduleName = _X("mscorlib.dll"); + + HelperInstrumentor instrumentor; + + auto result = instrumentor.Instrument(function, GetInstrumentationSettings(), true, AgentCallStyle::Strategy::InAgentCache); + + Assert::IsFalse(result); + } + + TEST_METHOD(HelperInstrumentor_ShouldNotInstrumentInCoreClrWhenAssemblyIsNotSystemPrivateCoreLib) + { + auto function = std::make_shared(); + + HelperInstrumentor instrumentor; + + auto result = instrumentor.Instrument(function, GetInstrumentationSettings(), true, AgentCallStyle::Strategy::InAgentCache); + + Assert::IsFalse(result); + } + + TEST_METHOD(HelperInstrumentor_ShouldNotInstrumentInNetFrameworkWhenAssemblyIsNotMscorlib) + { + auto function = std::make_shared(); + + HelperInstrumentor instrumentor; + + auto result = instrumentor.Instrument(function, GetInstrumentationSettings(), false, AgentCallStyle::Strategy::InAgentCache); + + Assert::IsFalse(result); + } + + TEST_METHOD(HelperInstrumentor_ShouldNotInstrumentInCoreClrForTheWrongType) + { + auto function = std::make_shared(); + function->_assemblyName = function->_moduleName = _X("System.Private.CoreLib.dll"); + + HelperInstrumentor instrumentor; + + auto result = instrumentor.Instrument(function, GetInstrumentationSettings(), true, AgentCallStyle::Strategy::InAgentCache); + + Assert::IsFalse(result); + } + + TEST_METHOD(HelperInstrumentor_ShouldNotInstrumentInNetFrameworkForTheWrongType) + { + auto function = std::make_shared(); + function->_assemblyName = function->_moduleName = _X("mscorlib.dll"); + + HelperInstrumentor instrumentor; + + auto result = instrumentor.Instrument(function, GetInstrumentationSettings(), false, AgentCallStyle::Strategy::InAgentCache); + + Assert::IsFalse(result); + } + + TEST_METHOD(HelperInstrumentor_ShouldNotInstrumentInCoreClrForNonHelperMethods) + { + auto function = std::make_shared(); + function->_assemblyName = function->_moduleName = _X("System.Private.CoreLib.dll"); + function->_typeName = _X("System.CannotUnloadAppDomainException"); + function->_functionName = _X(".ctor"); + + HelperInstrumentor instrumentor; + + auto result = instrumentor.Instrument(function, GetInstrumentationSettings(), true, AgentCallStyle::Strategy::InAgentCache); + + Assert::IsFalse(result); + } + + TEST_METHOD(HelperInstrumentor_ShouldNotInstrumentInNetFrameworkForNonHelperMethods) + { + auto function = std::make_shared(); + function->_assemblyName = function->_moduleName = _X("mscorlib.dll"); + function->_typeName = _X("System.CannotUnloadAppDomainException"); + function->_functionName = _X(".ctor"); + + HelperInstrumentor instrumentor; + + auto result = instrumentor.Instrument(function, GetInstrumentationSettings(), false, AgentCallStyle::Strategy::InAgentCache); + + Assert::IsFalse(result); + } + + TEST_METHOD(HelperInstrumentor_CoreClr_ShouldInstrument_GetThreadLocalBoolean) + { + AssertHelperMethodIsInstrumented(true, _X("GetThreadLocalBoolean")); + } + + TEST_METHOD(HelperInstrumentor_CoreClr_ShouldInstrument_SetThreadLocalBoolean) + { + AssertHelperMethodIsInstrumented(true, _X("SetThreadLocalBoolean")); + } + + TEST_METHOD(HelperInstrumentor_CoreClr_ShouldInstrument_GetAppDomainBoolean) + { + AssertHelperMethodIsInstrumented(true, _X("GetAppDomainBoolean")); + } + + TEST_METHOD(HelperInstrumentor_CoreClr_ShouldInstrument_SetAppDomainBoolean) + { + AssertHelperMethodIsInstrumented(true, _X("SetAppDomainBoolean")); + } + + TEST_METHOD(HelperInstrumentor_CoreClr_ShouldInstrument_LoadAssemblyOrThrow) + { + AssertHelperMethodIsInstrumented(true, _X("LoadAssemblyOrThrow")); + } + + TEST_METHOD(HelperInstrumentor_CoreClr_ShouldInstrument_GetTypeViaReflectionOrThrow) + { + AssertHelperMethodIsInstrumented(true, _X("GetTypeViaReflectionOrThrow")); + } + + TEST_METHOD(HelperInstrumentor_CoreClr_ShouldInstrument_GetMethodViaReflectionOrThrow) + { + AssertHelperMethodIsInstrumented(true, _X("GetMethodViaReflectionOrThrow")); + } + + TEST_METHOD(HelperInstrumentor_CoreClr_ShouldInstrument_GetMethodFromAppDomainStorage) + { + AssertHelperMethodIsInstrumented(true, _X("GetMethodFromAppDomainStorage")); + } + + TEST_METHOD(HelperInstrumentor_CoreClr_ShouldInstrument_GetMethodFromAppDomainStorageOrReflectionOrThrow) + { + AssertHelperMethodIsInstrumented(true, _X("GetMethodFromAppDomainStorageOrReflectionOrThrow")); + } + + TEST_METHOD(HelperInstrumentor_CoreClr_ShouldInstrument_StoreMethodInAppDomainStorageOrThrow) + { + AssertHelperMethodIsInstrumented(true, _X("StoreMethodInAppDomainStorageOrThrow")); + } + + TEST_METHOD(HelperInstrumentor_CoreClr_ShouldInstrument_GetMethodCacheLookupMethod) + { + AssertHelperMethodIsInstrumented(true, _X("GetMethodCacheLookupMethod")); + } + + TEST_METHOD(HelperInstrumentor_CoreClr_ShouldInstrument_GetMethodInfoFromAgentCache) + { + AssertHelperMethodIsInstrumented(true, _X("GetMethodInfoFromAgentCache")); + } + + TEST_METHOD(HelperInstrumentor_CoreClr_ShouldInstrument_EnsureInitialized) + { + AssertHelperMethodIsInstrumented(true, _X("EnsureInitialized")); + } + + TEST_METHOD(HelperInstrumentor_NetFramework_ShouldInstrument_GetThreadLocalBoolean) + { + AssertHelperMethodIsInstrumented(false, _X("GetThreadLocalBoolean")); + } + + TEST_METHOD(HelperInstrumentor_NetFramework_ShouldInstrument_SetThreadLocalBoolean) + { + AssertHelperMethodIsInstrumented(false, _X("SetThreadLocalBoolean")); + } + + TEST_METHOD(HelperInstrumentor_NetFramework_ShouldInstrument_GetAppDomainBoolean) + { + AssertHelperMethodIsInstrumented(false, _X("GetAppDomainBoolean")); + } + + TEST_METHOD(HelperInstrumentor_NetFramework_ShouldInstrument_SetAppDomainBoolean) + { + AssertHelperMethodIsInstrumented(false, _X("SetAppDomainBoolean")); + } + + TEST_METHOD(HelperInstrumentor_NetFramework_ShouldInstrument_LoadAssemblyOrThrow) + { + AssertHelperMethodIsInstrumented(false, _X("LoadAssemblyOrThrow")); + } + + TEST_METHOD(HelperInstrumentor_NetFramework_ShouldInstrument_GetTypeViaReflectionOrThrow) + { + AssertHelperMethodIsInstrumented(false, _X("GetTypeViaReflectionOrThrow")); + } + + TEST_METHOD(HelperInstrumentor_NetFramework_ShouldInstrument_GetMethodViaReflectionOrThrow) + { + AssertHelperMethodIsInstrumented(false, _X("GetMethodViaReflectionOrThrow")); + } + + TEST_METHOD(HelperInstrumentor_NetFramework_ShouldInstrument_GetMethodFromAppDomainStorage) + { + AssertHelperMethodIsInstrumented(false, _X("GetMethodFromAppDomainStorage")); + } + + TEST_METHOD(HelperInstrumentor_NetFramework_ShouldInstrument_GetMethodFromAppDomainStorageOrReflectionOrThrow) + { + AssertHelperMethodIsInstrumented(false, _X("GetMethodFromAppDomainStorageOrReflectionOrThrow")); + } + + TEST_METHOD(HelperInstrumentor_NetFramework_ShouldInstrument_StoreMethodInAppDomainStorageOrThrow) + { + AssertHelperMethodIsInstrumented(false, _X("StoreMethodInAppDomainStorageOrThrow")); + } + + TEST_METHOD(HelperInstrumentor_NetFramework_ShouldInstrument_GetMethodCacheLookupMethod) + { + AssertHelperMethodIsInstrumented(false, _X("GetMethodCacheLookupMethod")); + } + + TEST_METHOD(HelperInstrumentor_NetFramework_ShouldInstrument_GetMethodInfoFromAgentCache) + { + AssertHelperMethodIsInstrumented(false, _X("GetMethodInfoFromAgentCache")); + } + + TEST_METHOD(HelperInstrumentor_NetFramework_ShouldInstrument_EnsureInitialized) + { + AssertHelperMethodIsInstrumented(false, _X("EnsureInitialized")); + } + + private: + InstrumentationSettingsPtr GetInstrumentationSettings() const + { + Configuration::InstrumentationXmlSetPtr xmlSet(new Configuration::InstrumentationXmlSet()); + xmlSet->emplace(L"filename", L"\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + "); + Configuration::InstrumentationConfigurationPtr instrumentation(new Configuration::InstrumentationConfiguration(xmlSet)); + return std::make_shared(instrumentation, _X("")); + } + + void AssertHelperMethodIsInstrumented(const bool isCoreClr, const xstring_t& functionName) + { + auto function = std::make_shared(); + function->_assemblyName = function->_moduleName = isCoreClr ? _X("System.Private.CoreLib.dll") : _X("mscorlib.dll"); + function->_typeName = _X("System.CannotUnloadAppDomainException"); + function->_functionName = functionName; + + HelperInstrumentor instrumentor; + + auto result = instrumentor.Instrument(function, GetInstrumentationSettings(), isCoreClr, AgentCallStyle::Strategy::InAgentCache); + + // The method returns false even in the success case + Assert::IsFalse(result); + } + }; +}}}} diff --git a/src/Agent/NewRelic/Profiler/MethodRewriterTest/MethodRewriterTest.cpp b/src/Agent/NewRelic/Profiler/MethodRewriterTest/MethodRewriterTest.cpp index d15a7d0684..ad0c99b85d 100644 --- a/src/Agent/NewRelic/Profiler/MethodRewriterTest/MethodRewriterTest.cpp +++ b/src/Agent/NewRelic/Profiler/MethodRewriterTest/MethodRewriterTest.cpp @@ -16,11 +16,11 @@ using namespace Microsoft::VisualStudio::CppUnitTestFramework; -namespace NewRelic { namespace Profiler { namespace MethodRewriter { - namespace Test { - TEST_CLASS(MethodRewriterTest){ - public : - /* +namespace NewRelic { namespace Profiler { namespace MethodRewriter { namespace Test { + TEST_CLASS(MethodRewriterTest) + { + public: + /* TEST_METHOD(request_function_name_callback) { // setup a default method rewriter and function to instrument @@ -38,53 +38,291 @@ namespace NewRelic { namespace Profiler { namespace MethodRewriter { ValidateDefaultMockFunctionCallback(); }*/ - TEST_METHOD(overloads_are_instrumented){ - // ARRANGE - auto overload1 = std::make_shared(); - BYTEVECTOR(signature1Bytes, - 0x00, // default calling convention - 0x01, // 1 parameter - 0x01, // void return - 0x12, // parameter 1 class - 0x49 // class token (compressed 0x01000012) - ); - overload1->_signature = std::make_shared(signature1Bytes); - - auto overload2 = std::make_shared(); - BYTEVECTOR(signature2Bytes, - 0x00, // default calling convention - 0x01, // 1 parameter - 0x01, // void return - 0x0e // parameter 1 string - ); - overload1->_signature = std::make_shared(signature2Bytes); - - auto instrumentationSet = std::make_shared(); - instrumentationSet->insert(overload1->GetInstrumentationPoint()); - instrumentationSet->insert(overload2->GetInstrumentationPoint()); - auto instrumentation = std::make_shared(instrumentationSet); - auto methodRewriter = std::make_shared(instrumentation, _X(""), false); - - uint8_t overload1CallCount = 0; - overload1->_writeMethodHandler = [&overload1CallCount](const ByteVector&) { - ++overload1CallCount; - }; - - uint8_t overload2CallCount = 0; - overload2->_writeMethodHandler = [&overload2CallCount](const ByteVector&) { - ++overload2CallCount; - }; - - // ACT - methodRewriter->Instrument(overload1, AgentCallStyle::Strategy::InAgentCache); - methodRewriter->Instrument(overload2, AgentCallStyle::Strategy::InAgentCache); - - // ASSERT - Assert::AreEqual((uint8_t)1, overload1CallCount, L"Function should have been instrumented 1 time!"); - Assert::AreEqual((uint8_t)1, overload2CallCount, L"Function should have been instrumented 1 time!"); - } - -private: + TEST_METHOD(overloads_are_instrumented) + { + // ARRANGE + auto overload1 = std::make_shared(); + BYTEVECTOR(signature1Bytes, + 0x00, // default calling convention + 0x01, // 1 parameter + 0x01, // void return + 0x12, // parameter 1 class + 0x49 // class token (compressed 0x01000012) + ); + overload1->_signature = std::make_shared(signature1Bytes); + + auto overload2 = std::make_shared(); + BYTEVECTOR(signature2Bytes, + 0x00, // default calling convention + 0x01, // 1 parameter + 0x01, // void return + 0x0e // parameter 1 string + ); + overload1->_signature = std::make_shared(signature2Bytes); + + auto instrumentationSet = std::make_shared(); + instrumentationSet->insert(overload1->GetInstrumentationPoint()); + instrumentationSet->insert(overload2->GetInstrumentationPoint()); + auto instrumentation = std::make_shared(instrumentationSet); + auto methodRewriter = std::make_shared(instrumentation, _X(""), false); + + uint8_t overload1CallCount = 0; + overload1->_writeMethodHandler = [&overload1CallCount](const ByteVector&) { + ++overload1CallCount; + }; + + uint8_t overload2CallCount = 0; + overload2->_writeMethodHandler = [&overload2CallCount](const ByteVector&) { + ++overload2CallCount; + }; + + // ACT + methodRewriter->Instrument(overload1, AgentCallStyle::Strategy::InAgentCache); + methodRewriter->Instrument(overload2, AgentCallStyle::Strategy::InAgentCache); + + // ASSERT + Assert::AreEqual((uint8_t)1, overload1CallCount, L"Function should have been instrumented 1 time!"); + Assert::AreEqual((uint8_t)1, overload2CallCount, L"Function should have been instrumented 1 time!"); + } + + TEST_METHOD(GetConfigurationReturnsPointerToOriginalConfiguration) + { + auto function = std::make_shared(); + + auto instrumentation = GetInstrumentationConfigurationForFunction(function); + + auto methodRewriter = std::make_shared(instrumentation, _X(""), false); + + Assert::IsTrue(instrumentation == methodRewriter->GetInstrumentationConfiguration()); + } + + TEST_METHOD(GetAssemblyInstrumentation_IsEmptyWhenAssemblyIsNotInstrumented) + { + auto function = std::make_shared(); + + auto methodRewriter = GetMethodRewriterWithConfigurationForFunction(function); + + auto instrumentationForAssembly = methodRewriter->GetAssemblyInstrumentation(_X("ADifferentAssembly")); + + Assert::AreEqual(static_cast(0), instrumentationForAssembly.size()); + } + + TEST_METHOD(GetAssemblyInstrumentation_IsNotEmptyWhenAssemblyIsInstrumented) + { + auto function = std::make_shared(); + + auto methodRewriter = GetMethodRewriterWithConfigurationForFunction(function); + + auto instrumentationForAssembly = methodRewriter->GetAssemblyInstrumentation(function->GetAssemblyName()); + + Assert::AreEqual(static_cast(1), instrumentationForAssembly.size()); + } + + TEST_METHOD(ShouldInstrumentAssembly_FalseWhenAssemblyIsNotInstrumented) + { + auto function = std::make_shared(); + + auto methodRewriter = GetMethodRewriterWithConfigurationForFunction(function); + + Assert::IsFalse(methodRewriter->ShouldInstrumentAssembly(_X("ADifferentAssembly"))); + } + + TEST_METHOD(ShouldInstrumentAssembly_TrueWhenAssemblyIsInstrumented) + { + auto function = std::make_shared(); + + auto methodRewriter = GetMethodRewriterWithConfigurationForFunction(function); + + Assert::IsTrue(methodRewriter->ShouldInstrumentAssembly(function->GetAssemblyName())); + } + + TEST_METHOD(ShouldInstrumentType_FalseWhenTypeIsNotInstrumented) + { + auto function = std::make_shared(); + + auto methodRewriter = GetMethodRewriterWithConfigurationForFunction(function); + + Assert::IsFalse(methodRewriter->ShouldInstrumentType(_X("ADifferentType"))); + } + + TEST_METHOD(ShouldInstrumentType_TrueWhenTypeIsInstrumented) + { + auto function = std::make_shared(); + + auto methodRewriter = GetMethodRewriterWithConfigurationForFunction(function); + + Assert::IsTrue(methodRewriter->ShouldInstrumentType(function->GetTypeName())); + } + + TEST_METHOD(ShouldInstrumentFunction_FalseWhenFunctionIsNotInstrumented) + { + auto function = std::make_shared(); + + auto methodRewriter = GetMethodRewriterWithConfigurationForFunction(function); + + Assert::IsFalse(methodRewriter->ShouldInstrumentFunction(_X("ADifferentMethod"))); + } + + TEST_METHOD(ShouldInstrumentFunction_TrueWhenFunctionIsInstrumented) + { + auto function = std::make_shared(); + + auto methodRewriter = GetMethodRewriterWithConfigurationForFunction(function); + + Assert::IsTrue(methodRewriter->ShouldInstrumentFunction(function->GetFunctionName())); + } + + // The following tests ensure that the helper methods are included in the collection of instrumented methods + + TEST_METHOD(ShouldInstrumentAssembly_Mscorlib) + { + auto function = std::make_shared(); + + auto methodRewriter = GetMethodRewriterWithConfigurationForFunction(function); + + Assert::IsTrue(methodRewriter->ShouldInstrumentAssembly(_X("mscorlib"))); + } + + TEST_METHOD(ShouldInstrumentAssembly_SystemPrivateCoreLib) + { + auto function = std::make_shared(); + + auto methodRewriter = GetMethodRewriterWithConfigurationForFunction(function); + + Assert::IsTrue(methodRewriter->ShouldInstrumentAssembly(_X("System.Private.CoreLib"))); + } + + TEST_METHOD(ShouldInstrumentType_SystemCannotUnloadAppDomainException) + { + auto function = std::make_shared(); + + auto methodRewriter = GetMethodRewriterWithConfigurationForFunction(function); + + Assert::IsTrue(methodRewriter->ShouldInstrumentType(_X("System.CannotUnloadAppDomainException"))); + } + + TEST_METHOD(ShouldInstrumentFunction_GetAppDomainBoolean) + { + auto function = std::make_shared(); + + auto methodRewriter = GetMethodRewriterWithConfigurationForFunction(function); + + Assert::IsTrue(methodRewriter->ShouldInstrumentFunction(_X("GetAppDomainBoolean"))); + } + + TEST_METHOD(ShouldInstrumentFunction_GetThreadLocalBoolean) + { + auto function = std::make_shared(); + + auto methodRewriter = GetMethodRewriterWithConfigurationForFunction(function); + + Assert::IsTrue(methodRewriter->ShouldInstrumentFunction(_X("GetThreadLocalBoolean"))); + } + + TEST_METHOD(ShouldInstrumentFunction_SetThreadLocalBoolean) + { + auto function = std::make_shared(); + + auto methodRewriter = GetMethodRewriterWithConfigurationForFunction(function); + + Assert::IsTrue(methodRewriter->ShouldInstrumentFunction(_X("SetThreadLocalBoolean"))); + } + + TEST_METHOD(ShouldInstrumentFunction_GetMethodFromAppDomainStorageOrReflectionOrThrow) + { + auto function = std::make_shared(); + + auto methodRewriter = GetMethodRewriterWithConfigurationForFunction(function); + + Assert::IsTrue(methodRewriter->ShouldInstrumentFunction(_X("GetMethodFromAppDomainStorageOrReflectionOrThrow"))); + } + + TEST_METHOD(ShouldInstrumentFunction_GetMethodFromAppDomainStorage) + { + auto function = std::make_shared(); + + auto methodRewriter = GetMethodRewriterWithConfigurationForFunction(function); + + Assert::IsTrue(methodRewriter->ShouldInstrumentFunction(_X("GetMethodFromAppDomainStorage"))); + } + + TEST_METHOD(ShouldInstrumentFunction_GetMethodViaReflectionOrThrow) + { + auto function = std::make_shared(); + + auto methodRewriter = GetMethodRewriterWithConfigurationForFunction(function); + + Assert::IsTrue(methodRewriter->ShouldInstrumentFunction(_X("GetMethodViaReflectionOrThrow"))); + } + + TEST_METHOD(ShouldInstrumentFunction_GetTypeViaReflectionOrThrow) + { + auto function = std::make_shared(); + + auto methodRewriter = GetMethodRewriterWithConfigurationForFunction(function); + + Assert::IsTrue(methodRewriter->ShouldInstrumentFunction(_X("GetTypeViaReflectionOrThrow"))); + } + + TEST_METHOD(ShouldInstrumentFunction_LoadAssemblyOrThrow) + { + auto function = std::make_shared(); + + auto methodRewriter = GetMethodRewriterWithConfigurationForFunction(function); + + Assert::IsTrue(methodRewriter->ShouldInstrumentFunction(_X("LoadAssemblyOrThrow"))); + } + + TEST_METHOD(ShouldInstrumentFunction_StoreMethodInAppDomainStorageOrThrow) + { + auto function = std::make_shared(); + + auto methodRewriter = GetMethodRewriterWithConfigurationForFunction(function); + + Assert::IsTrue(methodRewriter->ShouldInstrumentFunction(_X("StoreMethodInAppDomainStorageOrThrow"))); + } + + TEST_METHOD(ShouldInstrumentFunction_GetMethodCacheLookupMethod) + { + auto function = std::make_shared(); + + auto methodRewriter = GetMethodRewriterWithConfigurationForFunction(function); + + Assert::IsTrue(methodRewriter->ShouldInstrumentFunction(_X("GetMethodCacheLookupMethod"))); + } + + TEST_METHOD(ShouldInstrumentFunction_EnsureInitialized) + { + auto function = std::make_shared(); + + auto methodRewriter = GetMethodRewriterWithConfigurationForFunction(function); + + Assert::IsTrue(methodRewriter->ShouldInstrumentFunction(_X("EnsureInitialized"))); + } + + TEST_METHOD(ShouldInstrumentFunction_GetMethodInfoFromAgentCache) + { + auto function = std::make_shared(); + + auto methodRewriter = GetMethodRewriterWithConfigurationForFunction(function); + + Assert::IsTrue(methodRewriter->ShouldInstrumentFunction(_X("GetMethodInfoFromAgentCache"))); + } + + private: + Configuration::InstrumentationConfigurationPtr GetInstrumentationConfigurationForFunction(std::shared_ptr function) + { + auto instrumentationSet = std::make_shared(); + instrumentationSet->insert(function->GetInstrumentationPoint()); + + return std::make_shared(instrumentationSet); + } + + std::shared_ptr GetMethodRewriterWithConfigurationForFunction(std::shared_ptr function) + { + return std::make_shared(GetInstrumentationConfigurationForFunction(function), _X(""), false); + } + /* static void ValidateDefaultMockFunctionCallback() { @@ -107,7 +345,6 @@ namespace NewRelic { namespace Profiler { namespace MethodRewriter { catch (...) { } } */ -}; -}} -} -} + }; + +}}}} diff --git a/src/Agent/NewRelic/Profiler/MethodRewriterTest/MethodRewriterTest.vcxproj b/src/Agent/NewRelic/Profiler/MethodRewriterTest/MethodRewriterTest.vcxproj index 4d4ce4eff1..410171fa38 100644 --- a/src/Agent/NewRelic/Profiler/MethodRewriterTest/MethodRewriterTest.vcxproj +++ b/src/Agent/NewRelic/Profiler/MethodRewriterTest/MethodRewriterTest.vcxproj @@ -194,6 +194,7 @@ + Create diff --git a/src/Agent/NewRelic/Profiler/MethodRewriterTest/MockFunction.h b/src/Agent/NewRelic/Profiler/MethodRewriterTest/MockFunction.h index 0bd7651dc0..c637f749fe 100644 --- a/src/Agent/NewRelic/Profiler/MethodRewriterTest/MockFunction.h +++ b/src/Agent/NewRelic/Profiler/MethodRewriterTest/MockFunction.h @@ -47,7 +47,13 @@ namespace NewRelic { namespace Profiler { namespace MethodRewriter { namespace T _signatureToken(0x456), _string(L"[MyAssembly]MyNamespace.MyClass.MyMethod"), _isGenericType(false), - _typeToken(0x045612345) + _typeToken(0x045612345), + _isCoreClr(false), + _shouldInjectMethodInstrumentation(false), + _shouldTrace(false), + _classAttributes(0), + _methodAttributes(0), + _isValid(true) { if (version.empty()) { @@ -138,14 +144,16 @@ namespace NewRelic { namespace Profiler { namespace MethodRewriter { namespace T return std::make_shared((uint16_t)1); } + unsigned long _classAttributes; virtual unsigned long GetClassAttributes() override { - return 0; + return _classAttributes; } + unsigned long _methodAttributes; virtual unsigned long GetMethodAttributes() override { - return 0; + return _methodAttributes; } virtual bool Preprocess() override @@ -153,9 +161,10 @@ namespace NewRelic { namespace Profiler { namespace MethodRewriter { namespace T return true; } + bool _shouldTrace; virtual bool ShouldTrace() override { - return false; + return _shouldTrace; } std::wstring _version; @@ -171,9 +180,10 @@ namespace NewRelic { namespace Profiler { namespace MethodRewriter { namespace T }; } + bool _shouldInjectMethodInstrumentation; virtual bool ShouldInjectMethodInstrumentation() override { - return false; + return _shouldInjectMethodInstrumentation; } virtual uint32_t GetTracerFlags() override @@ -181,14 +191,16 @@ namespace NewRelic { namespace Profiler { namespace MethodRewriter { namespace T return 0; } + bool _isValid; virtual bool IsValid() override { - return true; + return _isValid; } + bool _isCoreClr; virtual bool IsCoreClr() override { - return false; + return _isCoreClr; } uint32_t _typeToken; diff --git a/src/Agent/NewRelic/Profiler/MethodRewriterTest/MockSystemCalls.h b/src/Agent/NewRelic/Profiler/MethodRewriterTest/MockSystemCalls.h index d630a2020d..3ab16c7b1d 100644 --- a/src/Agent/NewRelic/Profiler/MethodRewriterTest/MockSystemCalls.h +++ b/src/Agent/NewRelic/Profiler/MethodRewriterTest/MockSystemCalls.h @@ -5,24 +5,33 @@ #include #include #include +#include #include "../MethodRewriter/ISystemCalls.h" namespace NewRelic { namespace Profiler { namespace MethodRewriter { namespace Test { struct MockSystemCalls : ISystemCalls { - std::function(const std::wstring&)> EnvironmentVariableResult; + MockSystemCalls() { } - MockSystemCalls() + virtual std::unique_ptr TryGetEnvironmentVariable(const xstring_t& variableName) override { - EnvironmentVariableResult = [](const std::wstring&) + auto search = _environmentVariables.find(variableName); + if (search != _environmentVariables.end()) { - return std::unique_ptr(new std::wstring(L"C:\\foo\\bar")); - }; + return std::make_unique(search->second); + } + + return nullptr; } - virtual std::unique_ptr TryGetEnvironmentVariable(const std::wstring& variableName) override + void SetEnvironmentVariable(const xstring_t& name, const xstring_t& value) { - return EnvironmentVariableResult(variableName); + _environmentVariables[name] = value; + } + + void ResetEnvironmentVariables() + { + _environmentVariables.clear(); } virtual bool FileExists(const xstring_t&) override @@ -41,5 +50,8 @@ namespace NewRelic { namespace Profiler { namespace MethodRewriter { namespace T virtual std::unique_ptr GetNewRelicProfilerLogDirectory() override { return nullptr; } virtual std::unique_ptr GetNewRelicLogDirectory() override { return nullptr; } virtual std::unique_ptr GetNewRelicLogLevel() override { return nullptr; } + + private: + std::unordered_map _environmentVariables; }; }}}} diff --git a/src/Agent/NewRelic/Profiler/ModuleInjector/IModule.h b/src/Agent/NewRelic/Profiler/ModuleInjector/IModule.h index 2ff8063793..4990b9678e 100644 --- a/src/Agent/NewRelic/Profiler/ModuleInjector/IModule.h +++ b/src/Agent/NewRelic/Profiler/ModuleInjector/IModule.h @@ -30,8 +30,6 @@ namespace NewRelic { namespace Profiler { namespace ModuleInjector virtual bool NeedsReferenceToCoreLib() = 0; virtual bool GetIsThisTheCoreLibAssembly() = 0; - virtual CComPtr GetMetaDataAssemblyEmit() = 0; - virtual sicily::codegen::ITokenizerPtr GetTokenizer() = 0; }; diff --git a/src/Agent/NewRelic/Profiler/ModuleInjector/ModuleInjector.h b/src/Agent/NewRelic/Profiler/ModuleInjector/ModuleInjector.h index 0305c4bbb8..1f701efb27 100644 --- a/src/Agent/NewRelic/Profiler/ModuleInjector/ModuleInjector.h +++ b/src/Agent/NewRelic/Profiler/ModuleInjector/ModuleInjector.h @@ -125,8 +125,6 @@ namespace NewRelic { namespace Profiler { namespace ModuleInjector } catch (NewRelic::Profiler::Win32Exception&) { - //exception in an error if we are injecting methods, otherwise, just neat to know. - //if is System.Private.CoreLib, allow the loop to proceed. if not, break out of the loop if (is_coreLib) { LogError(L"Failed to tokenize method signature: ", signatum, L". Proceeding to next method."); diff --git a/src/Agent/NewRelic/Profiler/ModuleInjectorTest/MockModule.h b/src/Agent/NewRelic/Profiler/ModuleInjectorTest/MockModule.h new file mode 100644 index 0000000000..309d5b46e8 --- /dev/null +++ b/src/Agent/NewRelic/Profiler/ModuleInjectorTest/MockModule.h @@ -0,0 +1,88 @@ +// Copyright 2020 New Relic, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#include "../ModuleInjector/IModule.h" +#include "../Sicily/SicilyTest/RealisticTokenizer.h" + +namespace NewRelic { namespace Profiler { namespace MethodRewriter { namespace Test +{ + class MockModule : public ModuleInjector::IModule + { + public: + MockModule() : + _moduleName(_X("MyModule")), + _needsReferenceToCoreLib(false), + _injectReferenceToCoreLib(true), + _isThisTheCoreLibAssembly(false), + _tokenizer(std::make_shared()) + { + } + + xstring_t _moduleName; + virtual xstring_t GetModuleName() override + { + return _moduleName; + } + + std::function _injectPlatformInvoke; + virtual void InjectPlatformInvoke(const xstring_t& methodName, const xstring_t& className, const xstring_t& moduleName, const ByteVector& signature) override + { + if (_injectPlatformInvoke) + { + _injectPlatformInvoke(methodName, className, moduleName, signature); + } + } + + std::function _injectStaticSecuritySafeMethod; + virtual void InjectStaticSecuritySafeMethod(const xstring_t& methodName, const xstring_t& className, const ByteVector& signature) override + { + if (_injectStaticSecuritySafeMethod) + { + _injectStaticSecuritySafeMethod(methodName, className, signature); + } + } + + std::function _injectCoreLibSecuritySafeMethodReference; + virtual void InjectCoreLibSecuritySafeMethodReference(const xstring_t& methodName, const xstring_t& className, const ByteVector& signature) override + { + if (_injectCoreLibSecuritySafeMethodReference) + { + _injectCoreLibSecuritySafeMethodReference(methodName, className, signature); + } + } + + std::function _injectNRHelperType; + virtual void InjectNRHelperType() override + { + if (_injectNRHelperType) + { + _injectNRHelperType(); + } + } + + bool _injectReferenceToCoreLib; + virtual bool InjectReferenceToCoreLib() override + { + return _injectReferenceToCoreLib; + } + + bool _needsReferenceToCoreLib; + virtual bool NeedsReferenceToCoreLib() override + { + return _needsReferenceToCoreLib; + } + + bool _isThisTheCoreLibAssembly; + virtual bool GetIsThisTheCoreLibAssembly() override + { + return _isThisTheCoreLibAssembly; + } + + sicily::codegen::ITokenizerPtr _tokenizer; + virtual sicily::codegen::ITokenizerPtr GetTokenizer() override + { + return _tokenizer; + } + }; +}}}} diff --git a/src/Agent/NewRelic/Profiler/ModuleInjectorTest/ModuleInjectorTest.cpp b/src/Agent/NewRelic/Profiler/ModuleInjectorTest/ModuleInjectorTest.cpp new file mode 100644 index 0000000000..3134f0e278 --- /dev/null +++ b/src/Agent/NewRelic/Profiler/ModuleInjectorTest/ModuleInjectorTest.cpp @@ -0,0 +1,211 @@ +// Copyright 2020 New Relic, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include +#define WIN32_LEAN_AND_MEAN +#include +#define LOGGER_DEFINE_STDLOG +#include +#include +#include "CppUnitTest.h" + +#include "MockModule.h" +#include "../ModuleInjector/ModuleInjector.h" + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +namespace NewRelic { namespace Profiler { namespace MethodRewriter { namespace Test +{ + TEST_CLASS(ModuleInjectorTest) + { + public: + TEST_METHOD(ModuleInjector_CoreClr_DoesNotCrash_CannotEnsureReferenceToCoreLib) + { + auto modulePtr = std::make_shared(); + modulePtr->_needsReferenceToCoreLib = true; + modulePtr->_injectReferenceToCoreLib = false; + + ModuleInjector::ModuleInjector::InjectIntoModule(*modulePtr, true); + } + + TEST_METHOD(ModuleInjector_NetFramework_DoesNotCrash_CannotEnsureReferenceToCoreLib) + { + auto modulePtr = std::make_shared(); + modulePtr->_needsReferenceToCoreLib = true; + modulePtr->_injectReferenceToCoreLib = false; + + ModuleInjector::ModuleInjector::InjectIntoModule(*modulePtr, false); + } + + TEST_METHOD(ModuleInjector_CoreClr_InjectsHelperType) + { + auto modulePtr = std::make_shared(); + modulePtr->_isThisTheCoreLibAssembly = true; + bool wasHelperTypeInjected(false); + modulePtr->_injectNRHelperType = [&wasHelperTypeInjected]() { wasHelperTypeInjected = true; }; + + ModuleInjector::ModuleInjector::InjectIntoModule(*modulePtr, true); + + Assert::IsTrue(wasHelperTypeInjected); + } + + TEST_METHOD(ModuleInjector_NetFramework_InjectsHelperType) + { + auto modulePtr = std::make_shared(); + modulePtr->_isThisTheCoreLibAssembly = true; + bool wasHelperTypeInjected(false); + modulePtr->_injectNRHelperType = [&wasHelperTypeInjected]() { wasHelperTypeInjected = true; }; + + ModuleInjector::ModuleInjector::InjectIntoModule(*modulePtr, false); + + Assert::IsTrue(wasHelperTypeInjected); + } + + TEST_METHOD(ModuleInjector_CoreClr_InjectsHelperMethods_DoesNotThrow) + { + auto modulePtr = std::make_shared(); + modulePtr->_isThisTheCoreLibAssembly = true; + modulePtr->_injectNRHelperType = []() { throw NewRelic::Profiler::Win32Exception(E_UNEXPECTED); }; + + ModuleInjector::ModuleInjector::InjectIntoModule(*modulePtr, true); + } + + TEST_METHOD(ModuleInjector_NetFramework_InjectsHelperMethods_DoesNotThrow) + { + auto modulePtr = std::make_shared(); + modulePtr->_isThisTheCoreLibAssembly = true; + modulePtr->_injectNRHelperType = []() { throw NewRelic::Profiler::Win32Exception(E_UNEXPECTED); }; + + ModuleInjector::ModuleInjector::InjectIntoModule(*modulePtr, false); + } + + TEST_METHOD(ModuleInjector_CoreClr_InjectsHelperMethods) + { + AssertHelperMethodsWereInjected(true); + } + + TEST_METHOD(ModuleInjector_NetFramework_InjectsHelperMethods) + { + AssertHelperMethodsWereInjected(false); + } + + TEST_METHOD(ModuleInjector_CoreClr_AlwaysAttemptsToInjectAllMethods) + { + AssertHelperMethodsWereInjected(/*isCoreClr*/ true, /*throwsException*/ true); + } + + TEST_METHOD(ModuleInjector_NetFramework_AlwaysAttemptsToInjectAllMethods) + { + AssertHelperMethodsWereInjected(/*isCoreClr*/ false, /*throwsException*/ true); + } + + TEST_METHOD(ModuleInjector_CoreClr_InjectsHelperMethodReferences) + { + AssertHelperMethodReferencesWereInjected(true); + } + + TEST_METHOD(ModuleInjector_NetFramework_InjectsHelperMethodReferences) + { + AssertHelperMethodReferencesWereInjected(false); + } + + TEST_METHOD(ModuleInjector_CoreClr_AlwaysAttemptsToInjectAllMethodReferences) + { + AssertHelperMethodReferencesWereInjected(/*isCoreClr*/ true, /*throwsException*/ true); + } + + TEST_METHOD(ModuleInjector_NetFramework_AlwaysAttemptsToInjectAllMethodReferences) + { + AssertHelperMethodReferencesWereInjected(/*isCoreClr*/ false, /*throwsException*/ true); + } + + private: + void AssertHelperMethodsWereInjected(const bool isCoreClr) + { + AssertHelperMethodsWereInjected(isCoreClr, false); + } + + void AssertHelperMethodsWereInjected(const bool isCoreClr, const bool throwsException) + { + std::array expectedMethods = { + _X("System.CannotUnloadAppDomainException.LoadAssemblyOrThrow"), + _X("System.CannotUnloadAppDomainException.GetTypeViaReflectionOrThrow"), + _X("System.CannotUnloadAppDomainException.GetMethodViaReflectionOrThrow"), + _X("System.CannotUnloadAppDomainException.GetMethodFromAppDomainStorage"), + _X("System.CannotUnloadAppDomainException.GetMethodFromAppDomainStorageOrReflectionOrThrow"), + _X("System.CannotUnloadAppDomainException.StoreMethodInAppDomainStorageOrThrow"), + _X("System.CannotUnloadAppDomainException.EnsureInitialized"), + _X("System.CannotUnloadAppDomainException.GetMethodInfoFromAgentCache"), + _X("System.CannotUnloadAppDomainException.GetMethodCacheLookupMethod") + }; + + auto modulePtr = std::make_shared(); + modulePtr->_isThisTheCoreLibAssembly = true; + std::set injectedMethodNamesWithType; + modulePtr->_injectStaticSecuritySafeMethod = [&injectedMethodNamesWithType, throwsException](const xstring_t& methodName, const xstring_t& className, const ByteVector& /*signature*/) + { + injectedMethodNamesWithType.emplace(className + _X(".") + methodName); + if (throwsException) + { + throw NewRelic::Profiler::Win32Exception(E_UNEXPECTED); + } + }; + + ModuleInjector::ModuleInjector::InjectIntoModule(*modulePtr, isCoreClr); + + Assert::AreEqual(expectedMethods.size(), injectedMethodNamesWithType.size()); + + for (const auto& expected : expectedMethods) + { + auto search = injectedMethodNamesWithType.find(expected); + Assert::IsTrue(search != injectedMethodNamesWithType.end(), (expected + _X(" was not found")).c_str()); + } + } + + void AssertHelperMethodReferencesWereInjected(const bool isCoreClr) + { + AssertHelperMethodReferencesWereInjected(isCoreClr, false); + } + + void AssertHelperMethodReferencesWereInjected(const bool isCoreClr, const bool throwsException) + { + const xstring_t expectedAssembly = isCoreClr ? _X("[System.Private.CoreLib]") : _X("[mscorlib]"); + std::array expectedMethods = { + expectedAssembly + _X("System.CannotUnloadAppDomainException.LoadAssemblyOrThrow"), + expectedAssembly + _X("System.CannotUnloadAppDomainException.GetTypeViaReflectionOrThrow"), + expectedAssembly + _X("System.CannotUnloadAppDomainException.GetMethodViaReflectionOrThrow"), + expectedAssembly + _X("System.CannotUnloadAppDomainException.GetMethodFromAppDomainStorage"), + expectedAssembly + _X("System.CannotUnloadAppDomainException.GetMethodFromAppDomainStorageOrReflectionOrThrow"), + expectedAssembly + _X("System.CannotUnloadAppDomainException.StoreMethodInAppDomainStorageOrThrow"), + expectedAssembly + _X("System.CannotUnloadAppDomainException.EnsureInitialized"), + expectedAssembly + _X("System.CannotUnloadAppDomainException.GetMethodInfoFromAgentCache"), + expectedAssembly + _X("System.CannotUnloadAppDomainException.GetMethodCacheLookupMethod") + }; + + auto modulePtr = std::make_shared(); + modulePtr->_isThisTheCoreLibAssembly = false; + std::set injectedMethodNamesWithType; + modulePtr->_injectCoreLibSecuritySafeMethodReference = [&injectedMethodNamesWithType, throwsException](const xstring_t& methodName, const xstring_t& className, const ByteVector& /*signature*/) + { + injectedMethodNamesWithType.emplace(className + _X(".") + methodName); + if (throwsException) + { + throw NewRelic::Profiler::Win32Exception(E_UNEXPECTED); + } + }; + + ModuleInjector::ModuleInjector::InjectIntoModule(*modulePtr, isCoreClr); + + Assert::AreEqual(expectedMethods.size(), injectedMethodNamesWithType.size()); + + for (const auto& expected : expectedMethods) + { + auto search = injectedMethodNamesWithType.find(expected); + Assert::IsTrue(search != injectedMethodNamesWithType.end(), (expected + _X(" was not found")).c_str()); + } + } + }; +}}}} diff --git a/src/Agent/NewRelic/Profiler/ModuleInjectorTest/ModuleInjectorTest.vcxproj b/src/Agent/NewRelic/Profiler/ModuleInjectorTest/ModuleInjectorTest.vcxproj new file mode 100644 index 0000000000..67199760eb --- /dev/null +++ b/src/Agent/NewRelic/Profiler/ModuleInjectorTest/ModuleInjectorTest.vcxproj @@ -0,0 +1,207 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {1534117B-A2B3-4403-B391-20DCC8AAEE44} + Win32Proj + ModuleInjectorTest + 10.0.18362.0 + NativeUnitTestProject + + + + DynamicLibrary + true + v142 + Unicode + false + + + DynamicLibrary + false + v142 + true + Unicode + false + + + DynamicLibrary + true + v142 + Unicode + false + + + DynamicLibrary + false + v142 + true + Unicode + false + + + + + + + + + + + + + + + + + + + + + true + $(ProjectDir)bin\$(PlatformTarget)\$(Configuration)\ + $(ProjectDir)obj\$(PlatformTarget)\$(Configuration)\ + + + true + $(ProjectDir)bin\$(PlatformTarget)\$(Configuration)\ + $(ProjectDir)obj\$(PlatformTarget)\$(Configuration)\ + + + false + $(ProjectDir)bin\$(PlatformTarget)\$(Configuration)\ + $(ProjectDir)obj\$(PlatformTarget)\$(Configuration)\ + + + false + $(ProjectDir)bin\$(PlatformTarget)\$(Configuration)\ + $(ProjectDir)obj\$(PlatformTarget)\$(Configuration)\ + + + + Use + Level4 + Disabled + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;%(PreprocessorDefinitions) + true + true + MultiThreadedDebug + stdafx.h + 4091 + + + Windows + true + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + + + Use + Disabled + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;%(PreprocessorDefinitions) + true + true + Level4 + MultiThreadedDebug + stdafx.h + true + 4091 + + + Windows + true + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + true + + + + + Level4 + Use + MaxSpeed + true + true + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;%(PreprocessorDefinitions) + true + true + MultiThreaded + stdafx.h + 4091 + + + Windows + true + true + true + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + + + Level4 + Use + MaxSpeed + true + true + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;%(PreprocessorDefinitions) + true + true + MultiThreaded + stdafx.h + 4091 + + + Windows + true + true + true + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + + + + + + + + Create + Create + Create + Create + + + + + {27654994-8403-4bd4-9d1d-4bccc4e93de6} + + + + + + \ No newline at end of file diff --git a/src/Agent/NewRelic/Profiler/ModuleInjectorTest/UnreferencedFunctions.h b/src/Agent/NewRelic/Profiler/ModuleInjectorTest/UnreferencedFunctions.h new file mode 100644 index 0000000000..65de1ec3c9 --- /dev/null +++ b/src/Agent/NewRelic/Profiler/ModuleInjectorTest/UnreferencedFunctions.h @@ -0,0 +1,98 @@ +// Copyright 2020 New Relic, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#include + +// fix unreferenced local functions warning in CppUnitTest.h +static inline void UseUnreferenced() +{ + std::wstring (*boolFunc)(const bool&) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)boolFunc; + std::wstring (*intFunc)(const int&) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)intFunc; + std::wstring (*longFunc)(const long&) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)longFunc; + std::wstring (*shortFunc)(const short&) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)shortFunc; + std::wstring (*charFunc)(const char&) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)charFunc; + std::wstring (*wchar_tFunc)(const wchar_t&) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)wchar_tFunc; + std::wstring (*signedCharFunc)(const signed char&) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)signedCharFunc; + std::wstring (*unsignedIntFunc)(const unsigned int&) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)unsignedIntFunc; + std::wstring (*unsignedLongFunc)(const unsigned long&) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)unsignedLongFunc; + std::wstring (*unsignedLongLongFunc)(const unsigned long long&) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)unsignedLongLongFunc; + std::wstring (*unsignedCharFunc)(const unsigned char&) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)unsignedCharFunc; + std::wstring (*stringFunc)(const std::string&) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)stringFunc; + std::wstring (*wstringFunc)(const std::wstring&) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)wstringFunc; + std::wstring (*doubleFunc)(const double&) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)doubleFunc; + std::wstring (*floatFunc)(const float&) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)floatFunc; + + std::wstring (*boolFuncConstPtr)(const bool*) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)boolFuncConstPtr; + std::wstring (*intFuncConstPtr)(const int*) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)intFuncConstPtr; + std::wstring (*longFuncConstPtr)(const long*) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)longFuncConstPtr; + std::wstring (*shortFuncConstPtr)(const short*) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)shortFuncConstPtr; + std::wstring (*signedCharFuncConstPtr)(const signed char*) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)signedCharFuncConstPtr; + std::wstring (*unsignedIntFuncConstPtr)(const unsigned int*) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)unsignedIntFuncConstPtr; + std::wstring (*unsignedLongFuncConstPtr)(const unsigned long*) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)unsignedLongFuncConstPtr; + std::wstring (*unsignedLongLongFuncConstPtr)(const unsigned long long*) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)unsignedLongLongFuncConstPtr; + std::wstring (*unsignedCharFuncConstPtr)(const unsigned char*) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)unsignedCharFuncConstPtr; + std::wstring (*charFuncConstPtr)(const char*) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)charFuncConstPtr; + std::wstring (*wchar_tFuncConstPtr)(const wchar_t*) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)wchar_tFuncConstPtr; + std::wstring (*doubleFuncConstPtr)(const double*) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)doubleFuncConstPtr; + std::wstring (*floatFuncConstPtr)(const float*) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)floatFuncConstPtr; + std::wstring (*voidFuncConstPtr)(const void*) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)voidFuncConstPtr; + + std::wstring (*boolFuncPtr)(bool*) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)boolFuncPtr; + std::wstring (*intFuncPtr)(int*) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)intFuncPtr; + std::wstring (*longFuncPtr)(long*) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)longFuncPtr; + std::wstring (*shortFuncPtr)(short*) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)shortFuncPtr; + std::wstring (*signedCharFuncPtr)(signed char*) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)signedCharFuncPtr; + std::wstring (*unsignedIntFuncPtr)(unsigned int*) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)unsignedIntFuncPtr; + std::wstring (*unsignedLongFuncPtr)(unsigned long*) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)unsignedLongFuncPtr; + std::wstring (*unsignedLongLongFuncPtr)(unsigned long long*) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)unsignedLongLongFuncPtr; + std::wstring (*unsignedCharFuncPtr)(unsigned char*) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)unsignedCharFuncPtr; + std::wstring (*charFuncPtr)(char*) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)charFuncPtr; + std::wstring (*wchar_tFuncPtr)(wchar_t*) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)wchar_tFuncPtr; + std::wstring (*doubleFuncPtr)(double*) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)doubleFuncPtr; + std::wstring (*floatFuncPtr)(float*) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)floatFuncPtr; + std::wstring (*voidFuncPtr)(void*) = &Microsoft::VisualStudio::CppUnitTestFramework::ToString; + (void)voidFuncPtr; +} diff --git a/src/Agent/NewRelic/Profiler/ModuleInjectorTest/stdafx.cpp b/src/Agent/NewRelic/Profiler/ModuleInjectorTest/stdafx.cpp new file mode 100644 index 0000000000..d06dc4585c --- /dev/null +++ b/src/Agent/NewRelic/Profiler/ModuleInjectorTest/stdafx.cpp @@ -0,0 +1,4 @@ +// Copyright 2020 New Relic, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +#include "stdafx.h" diff --git a/src/Agent/NewRelic/Profiler/ModuleInjectorTest/stdafx.h b/src/Agent/NewRelic/Profiler/ModuleInjectorTest/stdafx.h new file mode 100644 index 0000000000..cddfcfe7ed --- /dev/null +++ b/src/Agent/NewRelic/Profiler/ModuleInjectorTest/stdafx.h @@ -0,0 +1,10 @@ +// Copyright 2020 New Relic, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +// Headers for CppUnitTest +#define WIN32_LEAN_AND_MEAN +#include +#include +#include "UnreferencedFunctions.h" diff --git a/src/Agent/NewRelic/Profiler/NewRelic.Profiler.sln b/src/Agent/NewRelic/Profiler/NewRelic.Profiler.sln index 9ca3fd2c1f..2d02e3f7c2 100644 --- a/src/Agent/NewRelic/Profiler/NewRelic.Profiler.sln +++ b/src/Agent/NewRelic/Profiler/NewRelic.Profiler.sln @@ -49,6 +49,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProfiledMethods", "Profiled {DD9D2763-2E4F-48AA-BDFD-E23ABB9822AB} = {DD9D2763-2E4F-48AA-BDFD-E23ABB9822AB} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ModuleInjectorTest", "ModuleInjectorTest\ModuleInjectorTest.vcxproj", "{1534117B-A2B3-4403-B391-20DCC8AAEE44}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -209,6 +211,14 @@ Global {BA254DEB-EA81-428A-8BA7-BA55B0395D7A}.Release|Win32.Build.0 = Release|x86 {BA254DEB-EA81-428A-8BA7-BA55B0395D7A}.Release|x64.ActiveCfg = Release|x64 {BA254DEB-EA81-428A-8BA7-BA55B0395D7A}.Release|x64.Build.0 = Release|x64 + {1534117B-A2B3-4403-B391-20DCC8AAEE44}.Debug|Win32.ActiveCfg = Debug|Win32 + {1534117B-A2B3-4403-B391-20DCC8AAEE44}.Debug|Win32.Build.0 = Debug|Win32 + {1534117B-A2B3-4403-B391-20DCC8AAEE44}.Debug|x64.ActiveCfg = Debug|x64 + {1534117B-A2B3-4403-B391-20DCC8AAEE44}.Debug|x64.Build.0 = Debug|x64 + {1534117B-A2B3-4403-B391-20DCC8AAEE44}.Release|Win32.ActiveCfg = Release|Win32 + {1534117B-A2B3-4403-B391-20DCC8AAEE44}.Release|Win32.Build.0 = Release|Win32 + {1534117B-A2B3-4403-B391-20DCC8AAEE44}.Release|x64.ActiveCfg = Release|x64 + {1534117B-A2B3-4403-B391-20DCC8AAEE44}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Agent/NewRelic/Profiler/Profiler/Module.h b/src/Agent/NewRelic/Profiler/Profiler/Module.h index 80226f367b..f9b1b6d432 100644 --- a/src/Agent/NewRelic/Profiler/Profiler/Module.h +++ b/src/Agent/NewRelic/Profiler/Profiler/Module.h @@ -66,8 +66,6 @@ namespace NewRelic { namespace Profiler virtual bool GetIsThisTheCoreLibAssembly() override { return _isCoreLibAssembly; } - virtual CComPtr GetMetaDataAssemblyEmit() override{ return _metaDataAssemblyEmit; } - virtual void InjectPlatformInvoke(const xstring_t& methodName, const xstring_t& className, const xstring_t& moduleName, const ByteVector& signature) override { auto interfaceAttributes = CorMethodAttr::mdStatic | CorMethodAttr::mdPublic | CorMethodAttr::mdPinvokeImpl; diff --git a/src/Agent/NewRelic/Profiler/Sicily/SicilyTest/ParserTest.cpp b/src/Agent/NewRelic/Profiler/Sicily/SicilyTest/ParserTest.cpp index 38fa027984..6d45f4fdcf 100644 --- a/src/Agent/NewRelic/Profiler/Sicily/SicilyTest/ParserTest.cpp +++ b/src/Agent/NewRelic/Profiler/Sicily/SicilyTest/ParserTest.cpp @@ -17,12 +17,17 @@ namespace sicily { private: void TestParser(std::wstring testString) + { + TestParser(testString, testString); + } + + void TestParser(const xstring_t& testString, const xstring_t& expectedParsedString) { Scanner scanner(testString); Parser parser; ast::TypePtr rootType = parser.Parse(scanner); Assert::IsTrue(rootType != nullptr, L"rootType was nullptr"); - Assert::AreEqual(std::wstring(testString), rootType->ToString()); + Assert::AreEqual(expectedParsedString, rootType->ToString()); } public: @@ -195,6 +200,73 @@ namespace sicily { TestParser(L"int32 System.Threading.Interlocked::CompareExchange(int32&, int32, int32)"); } + + TEST_METHOD(EmptyStringToParseThrowsException) + { + Assert::ExpectException([this]() {TestParser(_X("")); }); + } + + TEST_METHOD(ParsingIncompleteInstanceMethodThrowsException) + { + Assert::ExpectException([this]() {TestParser(_X("instance void")); }); + } + + TEST_METHOD(ParsingInvalidMethodTypeThrowsException) + { + Assert::ExpectException([this]() {TestParser(_X("instance void void")); }); + } + + TEST_METHOD(ParseComplexClassName) + { + TestParser(_X("class [assemblyname]Foo1_2Bar.__something")); + } + + TEST_METHOD(ParseClassNameWithSlashes) + { + TestParser(_X("class [assemblyname]Foo/Bar")); + } + + TEST_METHOD(ParseClassNameWithSpacesBeforeDotss) + { + // Foo .Bar -> Foo.Bar + TestParser(_X("class [assemblyname]Foo .Bar"), _X("class [assemblyname]Foo.Bar")); + } + + TEST_METHOD(ParsingGenericClassWithIncorrectGenericCountThrowsException) + { + Assert::ExpectException([this]() { TestParser(_X("class [mscorlib]System.Action`1")); }); + } + + TEST_METHOD(ParsingRawClassNameThrowsException) + { + Assert::ExpectException([this]() { TestParser(_X("instance Foo")); }); + } + + TEST_METHOD(ParsingUnexpectedTokenThrowsException) + { + Assert::ExpectException([this]() { TestParser(_X("instance ,")); }); + } + + TEST_METHOD(ParseMethodWithManyTypes) + { + TestParser(_X("void MyClass::MyMethod(bool, uint32, int32, string&, valuetype Foo)"), _X("void MyClass::MyMethod(bool, unsigned int32, int32, string&, valuetype Foo)")); + } + + TEST_METHOD(ParsingBadAssemblyNameThrowsException) + { + Assert::ExpectException([this]() { TestParser(_X("[BadAssembly")); }); + } + + TEST_METHOD(ParsingBadColonPlacementThrowsException) + { + Assert::ExpectException([this]() { TestParser(_X("Foo:")); }); + Assert::ExpectException([this]() { TestParser(_X("Foo:Bar")); }); + } + + TEST_METHOD(ParsingUnhandledCharacterThrowsException) + { + Assert::ExpectException([this]() { TestParser(_X("^")); }); + } }; } } diff --git a/src/Agent/NewRelic/Profiler/Sicily/SicilyTest/PrimitiveTypeTest.cpp b/src/Agent/NewRelic/Profiler/Sicily/SicilyTest/PrimitiveTypeTest.cpp index b364f897be..f8b496443d 100644 --- a/src/Agent/NewRelic/Profiler/Sicily/SicilyTest/PrimitiveTypeTest.cpp +++ b/src/Agent/NewRelic/Profiler/Sicily/SicilyTest/PrimitiveTypeTest.cpp @@ -119,6 +119,36 @@ namespace sicily PrimitiveTypePtr primitiveType(new PrimitiveType(PrimitiveType::PrimitiveKind::kUINTPTR, false)); Assert::AreEqual(PrimitiveType::PrimitiveKind::kUINTPTR, primitiveType->GetPrimitiveKind()); } + + TEST_METHOD(TestToString) + { + TestToStringForPrimitiveType(PrimitiveType::PrimitiveKind::kBOOL, _X("bool")); + TestToStringForPrimitiveType(PrimitiveType::PrimitiveKind::kCHAR, _X("char")); + TestToStringForPrimitiveType(PrimitiveType::PrimitiveKind::kI1, _X("int8")); + TestToStringForPrimitiveType(PrimitiveType::PrimitiveKind::kI2, _X("int16")); + TestToStringForPrimitiveType(PrimitiveType::PrimitiveKind::kI4, _X("int32")); + TestToStringForPrimitiveType(PrimitiveType::PrimitiveKind::kI8, _X("int64")); + TestToStringForPrimitiveType(PrimitiveType::PrimitiveKind::kINTPTR, _X("unknown")); + TestToStringForPrimitiveType(PrimitiveType::PrimitiveKind::kOBJECT, _X("object")); + TestToStringForPrimitiveType(PrimitiveType::PrimitiveKind::kR4, _X("float32")); + TestToStringForPrimitiveType(PrimitiveType::PrimitiveKind::kR8, _X("float64")); + TestToStringForPrimitiveType(PrimitiveType::PrimitiveKind::kSTRING, _X("string")); + TestToStringForPrimitiveType(PrimitiveType::PrimitiveKind::kU1, _X("unsigned int8")); + TestToStringForPrimitiveType(PrimitiveType::PrimitiveKind::kU2, _X("unsigned int16")); + TestToStringForPrimitiveType(PrimitiveType::PrimitiveKind::kU4, _X("unsigned int32")); + TestToStringForPrimitiveType(PrimitiveType::PrimitiveKind::kU8, _X("unsigned int64")); + TestToStringForPrimitiveType(PrimitiveType::PrimitiveKind::kUINTPTR, _X("unknown")); + TestToStringForPrimitiveType(PrimitiveType::PrimitiveKind::kVOID, _X("void")); + } + + private: + void TestToStringForPrimitiveType(PrimitiveType::PrimitiveKind primitiveKind, const xstring_t& expectedString) + { + PrimitiveTypePtr primitiveType(new PrimitiveType(primitiveKind, false)); + PrimitiveTypePtr primitiveTypeReference(new PrimitiveType(primitiveKind, true)); + Assert::AreEqual(expectedString, primitiveType->ToString()); + Assert::AreEqual(expectedString + _X("&"), primitiveTypeReference->ToString()); + } }; } }