diff --git a/src/Agent/CHANGELOG.md b/src/Agent/CHANGELOG.md index b89ad2f1da..3b38f29b07 100644 --- a/src/Agent/CHANGELOG.md +++ b/src/Agent/CHANGELOG.md @@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +* Fixes [#1353](https://github.com/newrelic/newrelic-dotnet-agent/issues/1353) so that out-of-process .Net Core web applications are instrumented according to the setting in newrelic.config. [1392](https://github.com/newrelic/newrelic-dotnet-agent/pull/1392) ## [10.7.0] diff --git a/src/Agent/NewRelic/Profiler/Common/Strings.h b/src/Agent/NewRelic/Profiler/Common/Strings.h index ad752f13e6..6459a08743 100644 --- a/src/Agent/NewRelic/Profiler/Common/Strings.h +++ b/src/Agent/NewRelic/Profiler/Common/Strings.h @@ -62,5 +62,12 @@ namespace NewRelic { namespace Profiler return true; } + + static xstring_t ToUpper(xstring_t source) + { + std::transform(source.begin(), source.end(), source.begin(), ::towupper); + return source; + } + }; }} diff --git a/src/Agent/NewRelic/Profiler/Configuration/Configuration.h b/src/Agent/NewRelic/Profiler/Configuration/Configuration.h index d5735bbc33..c79b60689b 100644 --- a/src/Agent/NewRelic/Profiler/Configuration/Configuration.h +++ b/src/Agent/NewRelic/Profiler/Configuration/Configuration.h @@ -171,11 +171,11 @@ namespace NewRelic { namespace Profiler { namespace Configuration { }); } - bool ShouldInstrument(xstring_t const& processPath, xstring_t const& appPoolId, xstring_t const& commandLine, bool isCoreClr) + bool ShouldInstrument(xstring_t const& processPath, xstring_t const& parentProcessPath, xstring_t const& appPoolId, xstring_t const& commandLine, bool isCoreClr) { if (isCoreClr) { - return ShouldInstrumentNetCore(processPath, appPoolId, commandLine); + return ShouldInstrumentNetCore(processPath, parentProcessPath, appPoolId, commandLine); } else { @@ -423,9 +423,13 @@ namespace NewRelic { namespace Profiler { namespace Configuration { return false; } - bool IsW3wpProcess(const xstring_t& processName) + bool IsW3wpProcess(const xstring_t& processName, xstring_t const& parentProcessName) { - return Strings::EndsWith(processName, _X("W3WP.EXE")); + auto isIis = Strings::EndsWith(processName, _X("W3WP.EXE")) || Strings::EndsWith(parentProcessName, _X("W3WP.EXE")); + + LogInfo(_X("Process ") + processName + _X(" with parent process ") + parentProcessName + (isIis ? _X(" is") : _X(" is not")) + _X(" IIS.")); + + return isIis; } bool ShouldInstrumentApplicationPool(const xstring_t& appPoolId) @@ -487,7 +491,7 @@ namespace NewRelic { namespace Profiler { namespace Configuration { } // Test to see if we should instrument this .NET Core application at all - bool ShouldInstrumentNetCore(xstring_t const& processPath, xstring_t const& appPoolId, xstring_t const& commandLine) + bool ShouldInstrumentNetCore(xstring_t const& processPath, xstring_t const& parentProcessPath, xstring_t const& appPoolId, xstring_t const& commandLine) { //If it contains MsBuild, it is a build command and should not be profiled. bool isMsBuildInvocation = NewRelic::Profiler::Strings::ContainsCaseInsensitive(commandLine, _X("MSBuild.dll")); @@ -533,7 +537,7 @@ namespace NewRelic { namespace Profiler { namespace Configuration { return false; } - if (IsW3wpProcess(processPath)) { + if (IsW3wpProcess(processPath, parentProcessPath)) { return ShouldInstrumentApplicationPool(appPoolId); } @@ -543,10 +547,10 @@ namespace NewRelic { namespace Profiler { namespace Configuration { // Test to see if we should instrument this .NET Framework application at all bool ShouldInstrumentNetFramework(xstring_t const& processPath, xstring_t const& appPoolId) { - return ShouldInstrumentProcess(processPath, appPoolId); + return ShouldInstrumentProcess(processPath, _X(""), appPoolId); } - bool ShouldInstrumentProcess(const xstring_t& processName, const xstring_t& appPoolId) + bool ShouldInstrumentProcess(const xstring_t& processName, xstring_t const& parentProcessName, const xstring_t& appPoolId) { if (!_agentEnabled) { LogInfo("New Relic has been disabled via newrelic.config file."); @@ -574,7 +578,7 @@ namespace NewRelic { namespace Profiler { namespace Configuration { return true; } - if (IsW3wpProcess(processName)) { + if (IsW3wpProcess(processName, parentProcessName)) { return ShouldInstrumentApplicationPool(appPoolId); } diff --git a/src/Agent/NewRelic/Profiler/ConfigurationTest/ConfigurationTest.cpp b/src/Agent/NewRelic/Profiler/ConfigurationTest/ConfigurationTest.cpp index 044d861779..81d141f40d 100644 --- a/src/Agent/NewRelic/Profiler/ConfigurationTest/ConfigurationTest.cpp +++ b/src/Agent/NewRelic/Profiler/ConfigurationTest/ConfigurationTest.cpp @@ -46,31 +46,31 @@ namespace NewRelic { namespace Profiler { namespace Configuration { namespace Te TEST_METHOD(should_instrument_w3wp) { Configuration configuration(true); - Assert::IsTrue(configuration.ShouldInstrument(L"w3wp.exe", L"foo", L"", false)); + Assert::IsTrue(configuration.ShouldInstrument(L"w3wp.exe", L"", L"foo", L"", false)); } TEST_METHOD(should_instrument_WcfSvcHost) { Configuration configuration(true); - Assert::IsTrue(configuration.ShouldInstrument(L"WcfSvcHost.exe", L"foo", L"", false)); + Assert::IsTrue(configuration.ShouldInstrument(L"WcfSvcHost.exe", L"", L"foo", L"", false)); } TEST_METHOD(should_not_instrument_SMSvcHost) { Configuration configuration(true); - Assert::IsFalse(configuration.ShouldInstrument(L"SMSvcHost.exe", L"foo", L"", false)); + Assert::IsFalse(configuration.ShouldInstrument(L"SMSvcHost.exe", L"", L"foo", L"", false)); } TEST_METHOD(should_not_instrument_if_disabled) { Configuration configuration(false); - Assert::IsFalse(configuration.ShouldInstrument(L"w3wp.exe", L"foo", L"", false)); + Assert::IsFalse(configuration.ShouldInstrument(L"w3wp.exe", L"", L"foo", L"", false)); } TEST_METHOD(should_not_instrument_process) { Configuration configuration(true); - Assert::IsFalse(configuration.ShouldInstrument(L"foo.exe", L"", L"", false)); + Assert::IsFalse(configuration.ShouldInstrument(L"foo.exe", L"", L"", L"", false)); } TEST_METHOD(instrument_process) @@ -78,7 +78,7 @@ namespace NewRelic { namespace Profiler { namespace Configuration { namespace Te ProcessesPtr processes(new Processes()); processes->emplace(L"foo.exe"); Configuration configuration(true, Logger::Level::LEVEL_INFO, processes); - Assert::IsTrue(configuration.ShouldInstrument(L"foo.exe", L"", L"", false)); + Assert::IsTrue(configuration.ShouldInstrument(L"foo.exe", L"", L"", L"", false)); } TEST_METHOD(global_xml_agent_enabled_missing_local_xml_agent_enabled_missing) @@ -204,7 +204,7 @@ namespace NewRelic { namespace Profiler { namespace Configuration { namespace Te "); Configuration configuration(configurationXml, _missingAgentEnabledConfigPair); - Assert::IsTrue(configuration.ShouldInstrument(L"Foo.exe", L"", L"", false)); + Assert::IsTrue(configuration.ShouldInstrument(L"Foo.exe", L"", L"", L"", false)); } TEST_METHOD(instrument_multiple_processes_from_xml) @@ -222,8 +222,8 @@ namespace NewRelic { namespace Profiler { namespace Configuration { namespace Te "); Configuration configuration(configurationXml, _missingAgentEnabledConfigPair); - Assert::IsTrue(configuration.ShouldInstrument(L"Foo.exe", L"", L"", false)); - Assert::IsTrue(configuration.ShouldInstrument(L"Bar.exe", L"", L"", false)); + Assert::IsTrue(configuration.ShouldInstrument(L"Foo.exe", L"", L"", L"", false)); + Assert::IsTrue(configuration.ShouldInstrument(L"Bar.exe", L"", L"", L"", false)); } TEST_METHOD(do_not_instrument_process_not_in_xml) @@ -241,7 +241,7 @@ namespace NewRelic { namespace Profiler { namespace Configuration { namespace Te "); Configuration configuration(configurationXml,_missingAgentEnabledConfigPair); - Assert::IsFalse(configuration.ShouldInstrument(L"Baz.exe", L"", L"", false)); + Assert::IsFalse(configuration.ShouldInstrument(L"Baz.exe", L"", L"", L"", false)); } TEST_METHOD(exception_on_missing_configuration_node) @@ -263,14 +263,14 @@ namespace NewRelic { namespace Profiler { namespace Configuration { namespace Te { std::wstring configurationXml(L""); Configuration configuration(configurationXml, _missingAgentEnabledConfigPair); - Assert::IsFalse(configuration.ShouldInstrument(L"w3wp.exe", L"~Foo", L"", false)); + Assert::IsFalse(configuration.ShouldInstrument(L"w3wp.exe", L"", L"~Foo", L"", false)); } TEST_METHOD(tilde_in_string_but_not_at_start_is_not_ignored) { std::wstring configurationXml(L""); Configuration configuration(configurationXml, _missingAgentEnabledConfigPair); - Assert::IsTrue(configuration.ShouldInstrument(L"w3wp.exe", L"F~oo", L"", false)); + Assert::IsTrue(configuration.ShouldInstrument(L"w3wp.exe", L"", L"F~oo", L"", false)); } @@ -286,7 +286,7 @@ namespace NewRelic { namespace Profiler { namespace Configuration { namespace Te "); Configuration configuration(configurationXml, _missingAgentEnabledConfigPair); - Assert::IsTrue(configuration.ShouldInstrument(L"w3wp.exe", L"bar", L"", false)); + Assert::IsTrue(configuration.ShouldInstrument(L"w3wp.exe", L"", L"bar", L"", false)); } TEST_METHOD(application_pool_blacklist_without_default) @@ -301,7 +301,7 @@ namespace NewRelic { namespace Profiler { namespace Configuration { namespace Te "); Configuration configuration(configurationXml, _missingAgentEnabledConfigPair); - Assert::IsFalse(configuration.ShouldInstrument(L"w3wp.exe", L"foo", L"", false)); + Assert::IsFalse(configuration.ShouldInstrument(L"w3wp.exe", L"", L"foo", L"", false)); } TEST_METHOD(application_pool_whitelist_without_default) @@ -316,7 +316,7 @@ namespace NewRelic { namespace Profiler { namespace Configuration { namespace Te "); Configuration configuration(configurationXml, _missingAgentEnabledConfigPair); - Assert::IsTrue(configuration.ShouldInstrument(L"w3wp.exe", L"foo", L"", false)); + Assert::IsTrue(configuration.ShouldInstrument(L"w3wp.exe", L"", L"foo", L"", false)); } TEST_METHOD(application_pool_blacklist) @@ -331,7 +331,7 @@ namespace NewRelic { namespace Profiler { namespace Configuration { namespace Te "); Configuration configuration(configurationXml, _missingAgentEnabledConfigPair); - Assert::IsFalse(configuration.ShouldInstrument(L"w3wp.exe", L"foo", L"", false)); + Assert::IsFalse(configuration.ShouldInstrument(L"w3wp.exe", L"", L"foo", L"", false)); } TEST_METHOD(application_pool_whitelist) @@ -346,7 +346,7 @@ namespace NewRelic { namespace Profiler { namespace Configuration { namespace Te "); Configuration configuration(configurationXml, _missingAgentEnabledConfigPair); - Assert::IsTrue(configuration.ShouldInstrument(L"w3wp.exe", L"foo", L"", false)); + Assert::IsTrue(configuration.ShouldInstrument(L"w3wp.exe", L"", L"foo", L"", false)); } TEST_METHOD(application_pools_some_white_some_black_some_default_black) @@ -365,12 +365,12 @@ namespace NewRelic { namespace Profiler { namespace Configuration { namespace Te "); Configuration configuration(configurationXml, _missingAgentEnabledConfigPair); - Assert::IsTrue(configuration.ShouldInstrument(L"w3wp.exe", L"whiteFoo", L"", false)); - Assert::IsTrue(configuration.ShouldInstrument(L"w3wp.exe", L"whiteBar", L"", false)); - Assert::IsFalse(configuration.ShouldInstrument(L"w3wp.exe", L"blackFoo", L"", false)); - Assert::IsFalse(configuration.ShouldInstrument(L"w3wp.exe", L"blackBar", L"", false)); - Assert::IsFalse(configuration.ShouldInstrument(L"w3wp.exe", L"defaultFoo", L"", false)); - Assert::IsFalse(configuration.ShouldInstrument(L"w3wp.exe", L"defaultBar", L"", false)); + Assert::IsTrue(configuration.ShouldInstrument(L"w3wp.exe", L"", L"whiteFoo", L"", false)); + Assert::IsTrue(configuration.ShouldInstrument(L"w3wp.exe", L"", L"whiteBar", L"", false)); + Assert::IsFalse(configuration.ShouldInstrument(L"w3wp.exe", L"", L"blackFoo", L"", false)); + Assert::IsFalse(configuration.ShouldInstrument(L"w3wp.exe", L"", L"blackBar", L"", false)); + Assert::IsFalse(configuration.ShouldInstrument(L"w3wp.exe", L"", L"defaultFoo", L"", false)); + Assert::IsFalse(configuration.ShouldInstrument(L"w3wp.exe", L"", L"defaultBar", L"", false)); } TEST_METHOD(application_pools_some_white_some_black_some_default_white) @@ -389,12 +389,135 @@ namespace NewRelic { namespace Profiler { namespace Configuration { namespace Te "); Configuration configuration(configurationXml, _missingAgentEnabledConfigPair); - Assert::IsTrue(configuration.ShouldInstrument(L"w3wp.exe", L"whiteFoo", L"", false)); - Assert::IsTrue(configuration.ShouldInstrument(L"w3wp.exe", L"whiteBar", L"", false)); - Assert::IsFalse(configuration.ShouldInstrument(L"w3wp.exe", L"blackFoo", L"", false)); - Assert::IsFalse(configuration.ShouldInstrument(L"w3wp.exe", L"blackBar", L"", false)); - Assert::IsTrue(configuration.ShouldInstrument(L"w3wp.exe", L"defaultFoo", L"", false)); - Assert::IsTrue(configuration.ShouldInstrument(L"w3wp.exe", L"defaultBar", L"", false)); + Assert::IsTrue(configuration.ShouldInstrument(L"w3wp.exe", L"", L"whiteFoo", L"", false)); + Assert::IsTrue(configuration.ShouldInstrument(L"w3wp.exe", L"", L"whiteBar", L"", false)); + Assert::IsFalse(configuration.ShouldInstrument(L"w3wp.exe", L"", L"blackFoo", L"", false)); + Assert::IsFalse(configuration.ShouldInstrument(L"w3wp.exe", L"", L"blackBar", L"", false)); + Assert::IsTrue(configuration.ShouldInstrument(L"w3wp.exe", L"", L"defaultFoo", L"", false)); + Assert::IsTrue(configuration.ShouldInstrument(L"w3wp.exe", L"", L"defaultBar", L"", false)); + } + + TEST_METHOD(application_pools_oop_instrument_by_default) + { + std::wstring configurationXml(L"\ + \ + \ + \ + \ + \ + \ + "); + + Configuration configuration(configurationXml, _missingAgentEnabledConfigPair); + Assert::IsTrue(configuration.ShouldInstrument(L"Foo.exe", L"w3wp.exe", L"bar", L"", false)); + } + + TEST_METHOD(application_pool_oop_blacklist_without_default) + { + std::wstring configurationXml(L"\ + \ + \ + \ + \ + \ + \ + "); + + Configuration configuration(configurationXml, _missingAgentEnabledConfigPair); + Assert::IsFalse(configuration.ShouldInstrument(L"Foo.exe", L"w3wp.exe", L"foo", L"", false)); + } + + TEST_METHOD(application_pool_oop_whitelist_without_default) + { + std::wstring configurationXml(L"\ + \ + \ + \ + \ + \ + \ + "); + + Configuration configuration(configurationXml, _missingAgentEnabledConfigPair); + Assert::IsTrue(configuration.ShouldInstrument(L"Foo.exe", L"w3wp.exe", L"foo", L"", false)); + } + + TEST_METHOD(application_pool_oop_blacklist) + { + std::wstring configurationXml(L"\ + \ + \ + \ + \ + \ + \ + "); + + Configuration configuration(configurationXml, _missingAgentEnabledConfigPair); + Assert::IsFalse(configuration.ShouldInstrument(L"Foo.exe", L"w3wp.exe", L"foo", L"", false)); + } + + TEST_METHOD(application_pool_oop_whitelist) + { + std::wstring configurationXml(L"\ + \ + \ + \ + \ + \ + \ + "); + + Configuration configuration(configurationXml, _missingAgentEnabledConfigPair); + Assert::IsTrue(configuration.ShouldInstrument(L"Foo.exe", L"w3wp.exe", L"foo", L"", false)); + } + + TEST_METHOD(application_pools_oop_some_white_some_black_some_default_black) + { + std::wstring configurationXml(L"\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + "); + + Configuration configuration(configurationXml, _missingAgentEnabledConfigPair); + Assert::IsTrue(configuration.ShouldInstrument(L"Foo.exe", L"w3wp.exe", L"whiteFoo", L"", false)); + Assert::IsTrue(configuration.ShouldInstrument(L"Foo.exe", L"w3wp.exe", L"whiteBar", L"", false)); + Assert::IsFalse(configuration.ShouldInstrument(L"Foo.exe", L"w3wp.exe", L"blackFoo", L"", false)); + Assert::IsFalse(configuration.ShouldInstrument(L"Foo.exe", L"w3wp.exe", L"blackBar", L"", false)); + Assert::IsFalse(configuration.ShouldInstrument(L"Foo.exe", L"w3wp.exe", L"defaultFoo", L"", false)); + Assert::IsFalse(configuration.ShouldInstrument(L"Foo.exe", L"w3wp.exe", L"defaultBar", L"", false)); + } + + TEST_METHOD(application_pools_oop_some_white_some_black_some_default_white) + { + std::wstring configurationXml(L"\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + "); + + Configuration configuration(configurationXml, _missingAgentEnabledConfigPair); + Assert::IsTrue(configuration.ShouldInstrument(L"Foo.exe", L"w3wp.exe", L"whiteFoo", L"", false)); + Assert::IsTrue(configuration.ShouldInstrument(L"Foo.exe", L"w3wp.exe", L"whiteBar", L"", false)); + Assert::IsFalse(configuration.ShouldInstrument(L"Foo.exe", L"w3wp.exe", L"blackFoo", L"", false)); + Assert::IsFalse(configuration.ShouldInstrument(L"Foo.exe", L"w3wp.exe", L"blackBar", L"", false)); + Assert::IsTrue(configuration.ShouldInstrument(L"Foo.exe", L"w3wp.exe", L"defaultFoo", L"", false)); + Assert::IsTrue(configuration.ShouldInstrument(L"Foo.exe", L"w3wp.exe", L"defaultBar", L"", false)); } TEST_METHOD(agent_enabled_via_application_configuration) @@ -414,7 +537,7 @@ namespace NewRelic { namespace Profiler { namespace Configuration { namespace Te "); Configuration configuration(newRelicConfigXml,_missingAgentEnabledConfigPair, appConfigXml); - Assert::IsTrue(configuration.ShouldInstrument(L"foo.exe", L"", L"", false)); + Assert::IsTrue(configuration.ShouldInstrument(L"Foo.exe", L"", L"", L"", false)); } TEST_METHOD(agent_disabled_via_application_configuration) @@ -434,7 +557,7 @@ namespace NewRelic { namespace Profiler { namespace Configuration { namespace Te "); Configuration configuration(newRelicConfigXml, _missingAgentEnabledConfigPair, appConfigXml); - Assert::IsFalse(configuration.ShouldInstrument(L"foo.exe", L"", L"", false)); + Assert::IsFalse(configuration.ShouldInstrument(L"Foo.exe", L"", L"", L"", false)); } TEST_METHOD(agent_disabled_when_missing_from_application_configuration) @@ -453,7 +576,7 @@ namespace NewRelic { namespace Profiler { namespace Configuration { namespace Te "); Configuration configuration(newRelicConfigXml, _missingAgentEnabledConfigPair, appConfigXml); - Assert::IsFalse(configuration.ShouldInstrument(L"foo.exe", L"", L"", false)); + Assert::IsFalse(configuration.ShouldInstrument(L"Foo.exe", L"", L"", L"", false)); } TEST_METHOD(agent_disabled_when_app_config_does_not_exist) @@ -466,7 +589,7 @@ namespace NewRelic { namespace Profiler { namespace Configuration { namespace Te std::wstring appConfigXml(L""); Configuration configuration(newRelicConfigXml, _missingAgentEnabledConfigPair, appConfigXml); - Assert::IsFalse(configuration.ShouldInstrument(L"foo.exe", L"", L"", false)); + Assert::IsFalse(configuration.ShouldInstrument(L"Foo.exe", L"", L"", L"", false)); } TEST_METHOD(agent_enabled_in_application_config_is_case_insensitive) @@ -486,7 +609,7 @@ namespace NewRelic { namespace Profiler { namespace Configuration { namespace Te "); Configuration configuration(newRelicConfigXml, _missingAgentEnabledConfigPair, appConfigXml); - Assert::IsTrue(configuration.ShouldInstrument(L"foo.exe", L"", L"", false)); + Assert::IsTrue(configuration.ShouldInstrument(L"Foo.exe", L"", L"", L"", false)); } TEST_METHOD(agent_disabled_when_disabled_in_application_config_but_listed_in_newrelic_config) @@ -513,7 +636,7 @@ namespace NewRelic { namespace Profiler { namespace Configuration { namespace Te "); Configuration configuration(configurationXml, _missingAgentEnabledConfigPair, appConfigXml); - Assert::IsFalse(configuration.ShouldInstrument(L"foo.exe", L"", L"", false)); + Assert::IsFalse(configuration.ShouldInstrument(L"Foo.exe", L"", L"", L"", false)); } TEST_METHOD(agent_disabled_when_junk_in_application_config) @@ -533,7 +656,7 @@ namespace NewRelic { namespace Profiler { namespace Configuration { namespace Te "); Configuration configuration(configurationXml, _missingAgentEnabledConfigPair, appConfigXml); - Assert::IsFalse(configuration.ShouldInstrument(L"foo.exe", L"", L"", false)); + Assert::IsFalse(configuration.ShouldInstrument(L"Foo.exe", L"", L"", L"", false)); } TEST_METHOD(agent_enabled_when_in_process_list_and_no_flag_in_application_config) @@ -558,7 +681,7 @@ namespace NewRelic { namespace Profiler { namespace Configuration { namespace Te "); Configuration configuration(configurationXml, _missingAgentEnabledConfigPair, appConfigXml); - Assert::IsTrue(configuration.ShouldInstrument(L"foo.exe", L"", L"", false)); + Assert::IsTrue(configuration.ShouldInstrument(L"Foo.exe", L"", L"", L"", false)); } TEST_METHOD(win32helper_throw_on_error_throws_on_failure) diff --git a/src/Agent/NewRelic/Profiler/ConfigurationTest/ShouldInstrumentTest.cpp b/src/Agent/NewRelic/Profiler/ConfigurationTest/ShouldInstrumentTest.cpp index 00b8dfdc68..d3eef86831 100644 --- a/src/Agent/NewRelic/Profiler/ConfigurationTest/ShouldInstrumentTest.cpp +++ b/src/Agent/NewRelic/Profiler/ConfigurationTest/ShouldInstrumentTest.cpp @@ -17,50 +17,50 @@ namespace NewRelic { namespace Profiler { namespace Configuration { namespace Te auto isCoreClr = true; - Assert::IsFalse(configuration.ShouldInstrument(processPath, appPoolId, _X("dotnet run"), isCoreClr)); - Assert::IsFalse(configuration.ShouldInstrument(processPath, appPoolId, _X("DotNet Run"), isCoreClr)); - Assert::IsFalse(configuration.ShouldInstrument(processPath, appPoolId, _X("dotnet.exe run"), isCoreClr)); - Assert::IsFalse(configuration.ShouldInstrument(processPath, appPoolId, _X("\"dotnet.exe\" run"), isCoreClr)); - Assert::IsFalse(configuration.ShouldInstrument(processPath, appPoolId, _X("\"c:\\program files\\dotnet.exe\" run"), isCoreClr)); - Assert::IsFalse(configuration.ShouldInstrument(processPath, appPoolId, _X("\"c:\\program files\\dotnet.exe\" run"), isCoreClr)); - Assert::IsFalse(configuration.ShouldInstrument(processPath, appPoolId, _X("dotnet publish"), isCoreClr)); - Assert::IsFalse(configuration.ShouldInstrument(processPath, appPoolId, _X("dotnet restore"), isCoreClr)); - Assert::IsFalse(configuration.ShouldInstrument(processPath, appPoolId, _X("dotnet run -p c:\\test\\test.csproj"), isCoreClr)); - Assert::IsFalse(configuration.ShouldInstrument(processPath, appPoolId, _X("dotnet run -p \"c:\\program files\\test.csproj\""), isCoreClr)); - Assert::IsFalse(configuration.ShouldInstrument(processPath, appPoolId, _X("dotnet run -p ~/test.csproj"), isCoreClr)); - Assert::IsFalse(configuration.ShouldInstrument(processPath, appPoolId, _X("dotnet.exe exec \"c:\\program files\\MSBuild.dll\" -maxcpucount"), isCoreClr)); - Assert::IsFalse(configuration.ShouldInstrument(processPath, appPoolId, _X("dotnet.exe exec c:\\test\\msbuild.dll"), isCoreClr)); - Assert::IsFalse(configuration.ShouldInstrument(processPath, appPoolId, _X("dotnet new console"), isCoreClr)); - Assert::IsFalse(configuration.ShouldInstrument(processPath, appPoolId, _X("dotnet new mvc"), isCoreClr)); - Assert::IsFalse(configuration.ShouldInstrument(processPath, appPoolId, _X("dotnet\" run"), isCoreClr)); - Assert::IsFalse(configuration.ShouldInstrument(processPath, appPoolId, _X("\"dotnet\" run"), isCoreClr)); - Assert::IsFalse(configuration.ShouldInstrument(processPath, appPoolId, _X("app1.exe | dotnet run"), isCoreClr)); - Assert::IsFalse(configuration.ShouldInstrument(processPath, appPoolId, _X("dotnet Kudu.Services.Web.dll"), isCoreClr)); - Assert::IsFalse(configuration.ShouldInstrument(processPath, appPoolId, _X("/opt/Kudu/Kudu.Services.Web"), isCoreClr)); + Assert::IsFalse(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("dotnet run"), isCoreClr)); + Assert::IsFalse(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("DotNet Run"), isCoreClr)); + Assert::IsFalse(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("dotnet.exe run"), isCoreClr)); + Assert::IsFalse(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("\"dotnet.exe\" run"), isCoreClr)); + Assert::IsFalse(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("\"c:\\program files\\dotnet.exe\" run"), isCoreClr)); + Assert::IsFalse(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("\"c:\\program files\\dotnet.exe\" run"), isCoreClr)); + Assert::IsFalse(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("dotnet publish"), isCoreClr)); + Assert::IsFalse(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("dotnet restore"), isCoreClr)); + Assert::IsFalse(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("dotnet run -p c:\\test\\test.csproj"), isCoreClr)); + Assert::IsFalse(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("dotnet run -p \"c:\\program files\\test.csproj\""), isCoreClr)); + Assert::IsFalse(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("dotnet run -p ~/test.csproj"), isCoreClr)); + Assert::IsFalse(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("dotnet.exe exec \"c:\\program files\\MSBuild.dll\" -maxcpucount"), isCoreClr)); + Assert::IsFalse(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("dotnet.exe exec c:\\test\\msbuild.dll"), isCoreClr)); + Assert::IsFalse(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("dotnet new console"), isCoreClr)); + Assert::IsFalse(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("dotnet new mvc"), isCoreClr)); + Assert::IsFalse(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("dotnet\" run"), isCoreClr)); + Assert::IsFalse(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("\"dotnet\" run"), isCoreClr)); + Assert::IsFalse(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("app1.exe | dotnet run"), isCoreClr)); + Assert::IsFalse(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("dotnet Kudu.Services.Web.dll"), isCoreClr)); + Assert::IsFalse(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("/opt/Kudu/Kudu.Services.Web"), isCoreClr)); - Assert::IsTrue(configuration.ShouldInstrument(processPath, appPoolId, _X("dotnetXexe restore"), isCoreClr)); - Assert::IsTrue(configuration.ShouldInstrument(processPath, appPoolId, _X("\"c:\\program files\\dotnet.exe\"run"), isCoreClr)); - Assert::IsTrue(configuration.ShouldInstrument(processPath, appPoolId, _X("dotnet exec test.dll"), isCoreClr)); - Assert::IsTrue(configuration.ShouldInstrument(processPath, appPoolId, _X("dotnet exec publish.dll"), isCoreClr)); - Assert::IsTrue(configuration.ShouldInstrument(processPath, appPoolId, _X("dotnet publish.dll"), isCoreClr)); - Assert::IsTrue(configuration.ShouldInstrument(processPath, appPoolId, _X("dotnet run.dll"), isCoreClr)); - Assert::IsTrue(configuration.ShouldInstrument(processPath, appPoolId, _X("dotnet restore.dll"), isCoreClr)); - Assert::IsTrue(configuration.ShouldInstrument(processPath, appPoolId, _X("dotnet.exerun publish.dll"), isCoreClr)); - Assert::IsTrue(configuration.ShouldInstrument(processPath, appPoolId, _X("IpublishedThis.exe"), isCoreClr)); - Assert::IsTrue(configuration.ShouldInstrument(processPath, appPoolId, _X("dotnet new.dll"), isCoreClr)); - Assert::IsTrue(configuration.ShouldInstrument(processPath, appPoolId, _X("dotnet exec new.dll"), isCoreClr)); - Assert::IsTrue(configuration.ShouldInstrument(processPath, appPoolId, _X("dotnet myapp.dll run thisapp"), isCoreClr)); - Assert::IsTrue(configuration.ShouldInstrument(processPath, appPoolId, _X("dotnet/run/IpublishedThis.exe"), isCoreClr)); - Assert::IsTrue(configuration.ShouldInstrument(processPath, appPoolId, _X("run dotnet"), isCoreClr)); - Assert::IsTrue(configuration.ShouldInstrument(processPath, appPoolId, _X("IpublishedThis.exe \"dotnet run\""), isCoreClr)); - Assert::IsTrue(configuration.ShouldInstrument(processPath, appPoolId, _X("IpublishedThis.exe 'dotnet run'"), isCoreClr)); - Assert::IsTrue(configuration.ShouldInstrument(processPath, appPoolId, _X("dotnet exec IpublishedThis.dll dotnet run"), isCoreClr)); - Assert::IsTrue(configuration.ShouldInstrument(processPath, appPoolId, _X("dotnet exec IpublishedThis.dll \"dotnet run \""), isCoreClr)); + Assert::IsTrue(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("dotnetXexe restore"), isCoreClr)); + Assert::IsTrue(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("\"c:\\program files\\dotnet.exe\"run"), isCoreClr)); + Assert::IsTrue(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("dotnet exec test.dll"), isCoreClr)); + Assert::IsTrue(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("dotnet exec publish.dll"), isCoreClr)); + Assert::IsTrue(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("dotnet publish.dll"), isCoreClr)); + Assert::IsTrue(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("dotnet run.dll"), isCoreClr)); + Assert::IsTrue(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("dotnet restore.dll"), isCoreClr)); + Assert::IsTrue(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("dotnet.exerun publish.dll"), isCoreClr)); + Assert::IsTrue(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("IpublishedThis.exe"), isCoreClr)); + Assert::IsTrue(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("dotnet new.dll"), isCoreClr)); + Assert::IsTrue(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("dotnet exec new.dll"), isCoreClr)); + Assert::IsTrue(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("dotnet myapp.dll run thisapp"), isCoreClr)); + Assert::IsTrue(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("dotnet/run/IpublishedThis.exe"), isCoreClr)); + Assert::IsTrue(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("run dotnet"), isCoreClr)); + Assert::IsTrue(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("IpublishedThis.exe \"dotnet run\""), isCoreClr)); + Assert::IsTrue(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("IpublishedThis.exe 'dotnet run'"), isCoreClr)); + Assert::IsTrue(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("dotnet exec IpublishedThis.dll dotnet run"), isCoreClr)); + Assert::IsTrue(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("dotnet exec IpublishedThis.dll \"dotnet run \""), isCoreClr)); //These will incorrectly not be instrumented, but they are edge cases. We are documenting them here. - Assert::IsFalse(configuration.ShouldInstrument(processPath, appPoolId, _X("IpublishedThis.exe \"dotnet run \""), isCoreClr)); - Assert::IsFalse(configuration.ShouldInstrument(processPath, appPoolId, _X("'IpublishedThis.exe' \"dotnet run \""), isCoreClr)); - Assert::IsFalse(configuration.ShouldInstrument(processPath, appPoolId, _X("IpublishedThis.exe dotnet run"), isCoreClr)); + Assert::IsFalse(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("IpublishedThis.exe \"dotnet run \""), isCoreClr)); + Assert::IsFalse(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("'IpublishedThis.exe' \"dotnet run \""), isCoreClr)); + Assert::IsFalse(configuration.ShouldInstrument(processPath, L"", appPoolId, _X("IpublishedThis.exe dotnet run"), isCoreClr)); } }; }}}} diff --git a/src/Agent/NewRelic/Profiler/NewRelic.Profiler.sln.DotSettings b/src/Agent/NewRelic/Profiler/NewRelic.Profiler.sln.DotSettings index af19264f5c..f1861933d2 100644 --- a/src/Agent/NewRelic/Profiler/NewRelic.Profiler.sln.DotSettings +++ b/src/Agent/NewRelic/Profiler/NewRelic.Profiler.sln.DotSettings @@ -15,4 +15,4 @@ <NamingElement Priority="5"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="parameter" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></NamingElement> <NamingElement Priority="17"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="type alias" /><type Name="typedef" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /></NamingElement> <NamingElement Priority="12"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="union member" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></NamingElement> - <NamingElement Priority="3"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="union" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></NamingElement> \ No newline at end of file + <NamingElement Priority="3"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="union" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></NamingElement> diff --git a/src/Agent/NewRelic/Profiler/Profiler/CorProfilerCallbackImpl.h b/src/Agent/NewRelic/Profiler/Profiler/CorProfilerCallbackImpl.h index d3c254c319..c6e679c3e1 100644 --- a/src/Agent/NewRelic/Profiler/Profiler/CorProfilerCallbackImpl.h +++ b/src/Agent/NewRelic/Profiler/Profiler/CorProfilerCallbackImpl.h @@ -235,11 +235,12 @@ namespace NewRelic { namespace Profiler { LogTrace("Checking to see if we should instrument this process."); auto forceProfiling = _systemCalls->GetForceProfiling(); - auto processPath = GetAndTransformProcessPath(); + auto processPath = Strings::ToUpper(_systemCalls->GetProcessPath()); auto commandLine = _systemCalls->GetProgramCommandLine(); + auto parentProcessPath = Strings::ToUpper(_systemCalls->GetParentProcessPath()); auto appPoolId = GetAppPoolId(_systemCalls); LogInfo(L"Command line: ", commandLine); - if (!forceProfiling && !configuration->ShouldInstrument(processPath, appPoolId, commandLine, _isCoreClr)) { + if (!forceProfiling && !configuration->ShouldInstrument(processPath, parentProcessPath, appPoolId, commandLine, _isCoreClr)) { LogInfo("This process should not be instrumented, unloading profiler."); return CORPROF_E_PROFILER_CANCEL_ACTIVATION; } @@ -1265,13 +1266,6 @@ namespace NewRelic { namespace Profiler { } } - xstring_t GetAndTransformProcessPath() - { - auto processPath = _systemCalls->GetProcessPath(); - std::transform(processPath.begin(), processPath.end(), processPath.begin(), ::towupper); - return processPath; - } - void DelayProfilerAttach() { auto profilerDelay = _systemCalls->GetProfilerDelay(); diff --git a/src/Agent/NewRelic/Profiler/Profiler/SystemCalls.h b/src/Agent/NewRelic/Profiler/Profiler/SystemCalls.h index 1ebcf761da..35a7a59b9b 100644 --- a/src/Agent/NewRelic/Profiler/Profiler/SystemCalls.h +++ b/src/Agent/NewRelic/Profiler/Profiler/SystemCalls.h @@ -9,6 +9,7 @@ #include #include #include "Exceptions.h" +#include #include "Win32Helpers.h" #include "../Logging/Logger.h" @@ -85,6 +86,82 @@ namespace NewRelic { namespace Profiler return moduleName; } + virtual xstring_t GetParentProcessPath() + { + const int MAX_PROCESS_PATH = 1024; + wchar_t moduleName[MAX_PROCESS_PATH]; + + const auto ppid = GetParentProcessId(); + if (!ppid) + { + LogTrace("Process does not have a parent process id."); + return moduleName; + } + + LogTrace("Parent process id is: ", ppid); + + const auto handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, ppid); + if (handle) + { + DWORD buffSize = MAX_PROCESS_PATH; + const auto result = ::QueryFullProcessImageName(handle, 0, moduleName, &buffSize); + CloseHandle(handle); + + if (!result) + { + auto error = ::GetLastError(); + LogError(L"Unable to get the parent process path (1). Error number: ", std::hex, error, std::resetiosflags(std::ios_base::basefield)); + throw ProfilerException(); + } + } + else + { + auto error = ::GetLastError(); + LogError(L"Unable to get the parent process path (2). Error number: ", std::hex, error, std::resetiosflags(std::ios_base::basefield)); + throw ProfilerException(); + } + + LogTrace("Parent process name is ", moduleName); + + return moduleName; + } + + virtual uint32_t GetParentProcessId() + { + uint32_t ppid = 0; + const uint32_t pid = GetCurrentProcessId(); + const HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + + if (hSnapshot == INVALID_HANDLE_VALUE) + { + return ppid; + } + + PROCESSENTRY32 pe32; + ZeroMemory(&pe32, sizeof pe32); + pe32.dwSize = sizeof pe32; + + if (!::Process32First(hSnapshot, &pe32)) + { + CloseHandle(hSnapshot); + return ppid; + } + + do + { + if (pe32.th32ProcessID == pid) + { + ppid = pe32.th32ParentProcessID; + break; + } + } while (::Process32Next(hSnapshot, &pe32)); + + CloseHandle(hSnapshot); + + return ppid; + } + + virtual xstring_t GetProcessDirectoryPath() { const int MAX_PROCESS_PATH = 1024; diff --git a/src/Agent/NewRelic/Profiler/Profiler/UnixSystemCalls.h b/src/Agent/NewRelic/Profiler/Profiler/UnixSystemCalls.h index 7f51042c5f..1d58111266 100644 --- a/src/Agent/NewRelic/Profiler/Profiler/UnixSystemCalls.h +++ b/src/Agent/NewRelic/Profiler/Profiler/UnixSystemCalls.h @@ -124,6 +124,11 @@ namespace NewRelic { namespace Profiler return _X("."); } + virtual xstring_t GetParentProcessPath() + { + return _X("."); + } + virtual xstring_t GetProcessDirectoryPath() { return _X("."); diff --git a/src/Agent/_profilerBuild/linux-arm64-release/libNewRelicProfiler.so b/src/Agent/_profilerBuild/linux-arm64-release/libNewRelicProfiler.so index bd9b58259c..83118f25be 100644 Binary files a/src/Agent/_profilerBuild/linux-arm64-release/libNewRelicProfiler.so and b/src/Agent/_profilerBuild/linux-arm64-release/libNewRelicProfiler.so differ diff --git a/src/Agent/_profilerBuild/linux-x64-release/libNewRelicProfiler.so b/src/Agent/_profilerBuild/linux-x64-release/libNewRelicProfiler.so index b85af54feb..971bfb6adb 100644 Binary files a/src/Agent/_profilerBuild/linux-x64-release/libNewRelicProfiler.so and b/src/Agent/_profilerBuild/linux-x64-release/libNewRelicProfiler.so differ diff --git a/src/Agent/_profilerBuild/x64-Release/NewRelic.Profiler.dll b/src/Agent/_profilerBuild/x64-Release/NewRelic.Profiler.dll index d7c33a2acb..0126bbda29 100644 Binary files a/src/Agent/_profilerBuild/x64-Release/NewRelic.Profiler.dll and b/src/Agent/_profilerBuild/x64-Release/NewRelic.Profiler.dll differ diff --git a/src/Agent/_profilerBuild/x86-Release/NewRelic.Profiler.dll b/src/Agent/_profilerBuild/x86-Release/NewRelic.Profiler.dll index ea1b20583c..e6c22451d3 100644 Binary files a/src/Agent/_profilerBuild/x86-Release/NewRelic.Profiler.dll and b/src/Agent/_profilerBuild/x86-Release/NewRelic.Profiler.dll differ