From d5e09492a3c2519a5b5c9cb92378dc9162c5c55a Mon Sep 17 00:00:00 2001 From: lateralusX Date: Thu, 30 Jul 2026 11:00:50 +0200 Subject: [PATCH 1/6] Add IJW profiler regression test for transition FunctionID (#120151) When a C++/CLI (IJW) method invokes a managed method through a raw native function pointer, the resulting reverse (unmanaged->managed) marshaling stub emits a profiler code-transition callback. Prior to #117901 that stub loaded its secret argument (a UMEntryThunkData*) as the "MethodDesc", so the profiler received a bogus, non-null FunctionID; calling GetFunctionInfo on it crashed with an access violation. The fix routes the stub to EmitLoadNullPtr so the transition reports a NULL FunctionID, but nothing guarded the behavior. Add a self-contained Windows-only profiler test under src/tests/profiler/ijw that reproduces the scenario (managed -> native 'call' -> managed target by pointer) and validates the transition contract: - every non-null transition FunctionID must resolve via GetFunctionInfo; - the by-pointer target is reported CALL on entry and RETURN on exit; - the nested reverse-stub transitions surrounding it must report a NULL FunctionID rather than a bogus pointer. On unfixed runtimes the test fails via the GetFunctionInfo access violation. --- src/tests/profiler/ijw/CMakeLists.txt | 13 ++ src/tests/profiler/ijw/IjwProfileeDll.cpp | 28 ++++ src/tests/profiler/ijw/ijw.cs | 61 ++++++++ src/tests/profiler/ijw/ijw.csproj | 32 ++++ src/tests/profiler/native/CMakeLists.txt | 1 + src/tests/profiler/native/classfactory.cpp | 5 + src/tests/profiler/native/ijw/ijwprofiler.cpp | 139 ++++++++++++++++++ src/tests/profiler/native/ijw/ijwprofiler.h | 55 +++++++ 8 files changed, 334 insertions(+) create mode 100644 src/tests/profiler/ijw/CMakeLists.txt create mode 100644 src/tests/profiler/ijw/IjwProfileeDll.cpp create mode 100644 src/tests/profiler/ijw/ijw.cs create mode 100644 src/tests/profiler/ijw/ijw.csproj create mode 100644 src/tests/profiler/native/ijw/ijwprofiler.cpp create mode 100644 src/tests/profiler/native/ijw/ijwprofiler.h diff --git a/src/tests/profiler/ijw/CMakeLists.txt b/src/tests/profiler/ijw/CMakeLists.txt new file mode 100644 index 00000000000000..129b5398e91790 --- /dev/null +++ b/src/tests/profiler/ijw/CMakeLists.txt @@ -0,0 +1,13 @@ +project (IjwProfileeDll) +include(${CLR_ENG_NATIVE_DIR}/ijw/IJW.cmake) + +include_directories( ${INC_PLATFORM_DIR} ) +set(SOURCES IjwProfileeDll.cpp) + +# add the shared library +add_library (IjwProfileeDll SHARED ${SOURCES}) +target_link_libraries(IjwProfileeDll PRIVATE ${LINK_LIBRARIES_ADDITIONAL}) +add_ijw_msbuild_project_properties(IjwProfileeDll ijwhost) + +# add the install targets +install (TARGETS IjwProfileeDll DESTINATION bin) diff --git a/src/tests/profiler/ijw/IjwProfileeDll.cpp b/src/tests/profiler/ijw/IjwProfileeDll.cpp new file mode 100644 index 00000000000000..37faf92c0b527a --- /dev/null +++ b/src/tests/profiler/ijw/IjwProfileeDll.cpp @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// C++/CLI (IJW) mixed-mode assembly used by the ijw profiler test. +// +// Repro shape for https://github.com/dotnet/runtime/issues/120151: a managed +// method calls a native helper 'call' which invokes the managed +// 'ManagedByPointerTarget' through a raw function pointer. Invoking a managed +// method by native function pointer goes through a reverse (unmanaged->managed) +// marshaling stub, and the profiler code transition callback for that stub used +// to report a bogus, non-null FunctionID. +// +// Compiled with default /clr per-function codegen (no #pragma managed/unmanaged); +// the reporter's sample used a file-static function, but a named function +// reproduces identically and lets the profiler match it by name. +__declspec(noinline) void ManagedByPointerTarget() {} +__declspec(noinline) static void call(void (*f)()) { f(); } + +public ref class TestClass +{ +public: + // Managed -> native 'call' -> managed 'ManagedByPointerTarget' by pointer. + int CallManagedFunctionByPointer() + { + call(ManagedByPointerTarget); + return 100; + } +}; diff --git a/src/tests/profiler/ijw/ijw.cs b/src/tests/profiler/ijw/ijw.cs new file mode 100644 index 00000000000000..4888e4c923ef30 --- /dev/null +++ b/src/tests/profiler/ijw/ijw.cs @@ -0,0 +1,61 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; + +namespace Profiler.Tests +{ + class Ijw + { + static readonly Guid IjwProfilerGuid = new Guid("D6973314-9E66-4EAD-8129-9B1D3AD7CB85"); + + // Managed -> native -> managed-by-pointer scenario. Exercises a reverse + // (unmanaged->managed) marshaling stub whose profiler code transition + // callback must not report a bogus FunctionID. + // Regression test for https://github.com/dotnet/runtime/issues/120151. + private static int CallManagedFunctionByPointer() + { + Assembly ijwDll = Assembly.Load("IjwProfileeDll"); + Type testType = ijwDll.GetType("TestClass"); + object testInstance = Activator.CreateInstance(testType); + MethodInfo method = testType.GetMethod("CallManagedFunctionByPointer"); + return (int)method.Invoke(testInstance, null); + } + + public static int Main(string[] args) + { + if (args.Length > 1 && args[0].Equals("RunTest", StringComparison.OrdinalIgnoreCase)) + { + switch (args[1]) + { + case nameof(CallManagedFunctionByPointer): + return CallManagedFunctionByPointer(); + } + } + + if (!RunProfilerTest(nameof(CallManagedFunctionByPointer))) + { + return 101; + } + + return 100; + } + + private static bool RunProfilerTest(string testName) + { + try + { + return ProfilerTestRunner.Run(profileePath: Assembly.GetExecutingAssembly().Location, + testName: "Ijw", + profilerClsid: IjwProfilerGuid, + profileeArguments: testName) == 100; + } + catch (Exception ex) + { + Console.WriteLine(ex); + } + return false; + } + } +} diff --git a/src/tests/profiler/ijw/ijw.csproj b/src/tests/profiler/ijw/ijw.csproj new file mode 100644 index 00000000000000..856a0cca4f9830 --- /dev/null +++ b/src/tests/profiler/ijw/ijw.csproj @@ -0,0 +1,32 @@ + + + .NETCoreApp + exe + true + + true + false + + true + + true + + true + + true + + true + + + true + + + + + + + + + + diff --git a/src/tests/profiler/native/CMakeLists.txt b/src/tests/profiler/native/CMakeLists.txt index 60c43b3309f48a..dbc2c0d3bc21f9 100644 --- a/src/tests/profiler/native/CMakeLists.txt +++ b/src/tests/profiler/native/CMakeLists.txt @@ -19,6 +19,7 @@ set(SOURCES gcskipobjectsallocatedbyclasscallbackprofiler/gcskipobjectsallocatedbyclasscallbackprofiler.cpp getappdomainstaticaddress/getappdomainstaticaddress.cpp handlesprofiler/handlesprofiler.cpp + ijw/ijwprofiler.cpp inlining/inlining.cpp metadatagetdispenser/metadatagetdispenser.cpp moduleload/moduleload.cpp diff --git a/src/tests/profiler/native/classfactory.cpp b/src/tests/profiler/native/classfactory.cpp index 0ff8a07467c27c..dace6277d6f030 100644 --- a/src/tests/profiler/native/classfactory.cpp +++ b/src/tests/profiler/native/classfactory.cpp @@ -15,6 +15,7 @@ #include "gcheapenumerationprofiler/gcheapenumerationprofiler.h" #include "gcprofiler/gcprofiler.h" #include "handlesprofiler/handlesprofiler.h" +#include "ijw/ijwprofiler.h" #include "metadatagetdispenser/metadatagetdispenser.h" #include "nullprofiler/nullprofiler.h" #include "rejitprofiler/rejitprofiler.h" @@ -174,6 +175,10 @@ HRESULT STDMETHODCALLTYPE ClassFactory::CreateInstance(IUnknown *pUnkOuter, REFI { profiler = new GCSkipObjectsAllocatedByClassCallbackProfiler(); } + else if (clsid == IjwProfiler::GetClsid()) + { + profiler = new IjwProfiler(); + } else { printf("No profiler found in ClassFactory::CreateInstance. Did you add your profiler to the list?\n"); diff --git a/src/tests/profiler/native/ijw/ijwprofiler.cpp b/src/tests/profiler/native/ijw/ijwprofiler.cpp new file mode 100644 index 00000000000000..3fb75c52a313f2 --- /dev/null +++ b/src/tests/profiler/native/ijw/ijwprofiler.cpp @@ -0,0 +1,139 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "ijwprofiler.h" + +GUID IjwProfiler::GetClsid() +{ + // {D6973314-9E66-4EAD-8129-9B1D3AD7CB85} + GUID clsid = { 0xd6973314, 0x9e66, 0x4ead, { 0x81, 0x29, 0x9b, 0x1d, 0x3a, 0xd7, 0xcb, 0x85 } }; + return clsid; +} + +HRESULT IjwProfiler::Initialize(IUnknown* pICorProfilerInfoUnk) +{ + Profiler::Initialize(pICorProfilerInfoUnk); + + HRESULT hr = S_OK; + if (FAILED(hr = pCorProfilerInfo->SetEventMask2(COR_PRF_MONITOR_CODE_TRANSITIONS | COR_PRF_DISABLE_INLINING, 0))) + { + _failures++; + printf("FAIL: ICorProfilerInfo::SetEventMask2() failed hr=0x%x\n", hr); + return hr; + } + + return S_OK; +} + +HRESULT IjwProfiler::Shutdown() +{ + Profiler::Shutdown(); + + // The managed target reached through the native function pointer must have + // been reported CALL on the way in and RETURN on the way out. This also + // ensures the by-pointer scenario actually ran. + bool targetOk = _targetUnmanagedToManaged == COR_PRF_TRANSITION_CALL + && _targetManagedToUnmanaged == COR_PRF_TRANSITION_RETURN; + + if (_failures == 0 && _transitions > 0 && targetOk) + { + printf("PROFILER TEST PASSES\n"); + } + else + { + printf("Test failed _failures=%d _transitions=%d targetU2M=%d targetM2U=%d\n", + _failures.load(), _transitions.load(), + (int)_targetUnmanagedToManaged, (int)_targetManagedToUnmanaged); + } + fflush(stdout); + return S_OK; +} + +void IjwProfiler::HandleTransition(bool unmanagedToManaged, FunctionID functionID, COR_PRF_TRANSITION_REASON reason) +{ + _transitions++; + + const char* which = unmanagedToManaged ? "UnmanagedToManagedTransition" : "ManagedToUnmanagedTransition"; + + // A non-null FunctionID must be a real MethodDesc that GetFunctionInfo can + // resolve. Before the fix for https://github.com/dotnet/runtime/issues/120151 + // a reverse marshaling stub reported a bogus pointer here, and this call + // would fail or crash. + bool isTarget = false; + if (functionID != 0) + { + ClassID classId = 0; + ModuleID moduleId = 0; + mdToken token = 0; + HRESULT hr = pCorProfilerInfo->GetFunctionInfo(functionID, &classId, &moduleId, &token); + if (FAILED(hr)) + { + _failures++; + printf("FAIL: %s reported FunctionID=0x%p (reason=%d) that GetFunctionInfo could not resolve hr=0x%x\n", + which, (void*)functionID, (int)reason, hr); + fflush(stdout); + } + else + { + isTarget = GetFunctionIDName(functionID) == WCHAR("ManagedByPointerTarget"); + } + } + + if (isTarget) + { + // Record the reason for each direction and open/close the window in which + // the nested reverse-stub transitions are checked. The target should be + // seen exactly once per direction. + if (unmanagedToManaged) + { + if (_targetUnmanagedToManaged != NO_TRANSITION) + { + _failures++; + } + _targetUnmanagedToManaged = reason; + if (reason == COR_PRF_TRANSITION_CALL) + { + _insideTarget = true; + } + } + else + { + if (_targetManagedToUnmanaged != NO_TRANSITION) + { + _failures++; + } + _targetManagedToUnmanaged = reason; + if (reason == COR_PRF_TRANSITION_RETURN) + { + _insideTarget = false; + } + } + return; + } + + // Any non-target transition seen while executing the managed target is a + // nested reverse (unmanaged->managed) marshaling stub. Post-fix these report + // a NULL FunctionID; a non-null value here is exactly the 120151 bug (a + // resolvable-but-wrong pointer would slip past the check above). + if (_insideTarget.load() && functionID != 0) + { + _failures++; + printf("FAIL: nested %s inside target reported non-null FunctionID=0x%p (reason=%d)\n", + which, (void*)functionID, (int)reason); + fflush(stdout); + } +} + +HRESULT IjwProfiler::UnmanagedToManagedTransition(FunctionID functionID, COR_PRF_TRANSITION_REASON reason) +{ + SHUTDOWNGUARD(); + HandleTransition(/* unmanagedToManaged */ true, functionID, reason); + return S_OK; +} + +HRESULT IjwProfiler::ManagedToUnmanagedTransition(FunctionID functionID, COR_PRF_TRANSITION_REASON reason) +{ + SHUTDOWNGUARD(); + HandleTransition(/* unmanagedToManaged */ false, functionID, reason); + return S_OK; +} diff --git a/src/tests/profiler/native/ijw/ijwprofiler.h b/src/tests/profiler/native/ijw/ijwprofiler.h new file mode 100644 index 00000000000000..f139ebcfa71f50 --- /dev/null +++ b/src/tests/profiler/native/ijw/ijwprofiler.h @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#pragma once + +#include "../profiler.h" + +#define NO_TRANSITION ((COR_PRF_TRANSITION_REASON)-1) + +// Profiler used by the C++/CLI (IJW) profiler tests. It monitors code +// transitions and validates that every non-null FunctionID reported for a +// transition can be resolved via ICorProfilerInfo::GetFunctionInfo. This is a +// regression guard for https://github.com/dotnet/runtime/issues/120151, where a +// reverse (unmanaged->managed) marshaling stub reported a bogus, non-null +// FunctionID that crashed GetFunctionInfo. +// +// In addition to the "must resolve" check it validates the transition shape of +// the managed method invoked through a native function pointer: the target must +// be reported CALL on the way in and RETURN on the way out, and the nested +// reverse-stub transitions that surround it must report a NULL FunctionID (the +// exact behavior the fix introduced) rather than a bogus pointer. +class IjwProfiler : public Profiler +{ +public: + IjwProfiler() + : Profiler() + , _failures(0) + , _transitions(0) + , _targetUnmanagedToManaged(NO_TRANSITION) + , _targetManagedToUnmanaged(NO_TRANSITION) + , _insideTarget(false) + {} + virtual ~IjwProfiler() = default; + + static GUID GetClsid(); + virtual HRESULT STDMETHODCALLTYPE Initialize(IUnknown* pICorProfilerInfoUnk); + virtual HRESULT STDMETHODCALLTYPE Shutdown(); + virtual HRESULT STDMETHODCALLTYPE UnmanagedToManagedTransition(FunctionID functionID, COR_PRF_TRANSITION_REASON reason); + virtual HRESULT STDMETHODCALLTYPE ManagedToUnmanagedTransition(FunctionID functionID, COR_PRF_TRANSITION_REASON reason); + +private: + std::atomic _failures; + std::atomic _transitions; + + // Transition reasons recorded for the managed method reached through the + // native function pointer (see IjwProfileeDll.cpp). + COR_PRF_TRANSITION_REASON _targetUnmanagedToManaged; + COR_PRF_TRANSITION_REASON _targetManagedToUnmanaged; + + // Set while executing the managed target so nested transitions can be + // checked. The profilee is single-threaded around this call. + std::atomic _insideTarget; + + void HandleTransition(bool unmanagedToManaged, FunctionID functionID, COR_PRF_TRANSITION_REASON reason); +}; From e3a3442cc32e0039445a710675d237df39548fa1 Mon Sep 17 00:00:00 2001 From: lateralusX Date: Thu, 30 Jul 2026 12:41:41 +0200 Subject: [PATCH 2/6] Fix build issue. --- src/tests/profiler/ijw/ijw.csproj | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/tests/profiler/ijw/ijw.csproj b/src/tests/profiler/ijw/ijw.csproj index 856a0cca4f9830..82a41a69437ed0 100644 --- a/src/tests/profiler/ijw/ijw.csproj +++ b/src/tests/profiler/ijw/ijw.csproj @@ -26,7 +26,10 @@ - - + + + From 97c9d8659a18bb199130a719c946c48bb1a622ba Mon Sep 17 00:00:00 2001 From: lateralusX Date: Thu, 30 Jul 2026 12:42:25 +0200 Subject: [PATCH 3/6] Harden regression test. --- src/tests/profiler/native/ijw/ijwprofiler.cpp | 28 +++++++++++++------ src/tests/profiler/native/ijw/ijwprofiler.h | 6 ++++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/tests/profiler/native/ijw/ijwprofiler.cpp b/src/tests/profiler/native/ijw/ijwprofiler.cpp index 3fb75c52a313f2..3e497652fe26e8 100644 --- a/src/tests/profiler/native/ijw/ijwprofiler.cpp +++ b/src/tests/profiler/native/ijw/ijwprofiler.cpp @@ -35,15 +35,20 @@ HRESULT IjwProfiler::Shutdown() bool targetOk = _targetUnmanagedToManaged == COR_PRF_TRANSITION_CALL && _targetManagedToUnmanaged == COR_PRF_TRANSITION_RETURN; - if (_failures == 0 && _transitions > 0 && targetOk) + // The nested reverse-stub transitions surrounding the target must have been + // seen and reported a NULL FunctionID (the exact behavior the fix introduced). + // Requiring at least one makes this a positive assertion rather than merely + // "no bad value was seen". + if (_failures == 0 && _transitions > 0 && targetOk && _nestedNullTransitions > 0) { printf("PROFILER TEST PASSES\n"); } else { - printf("Test failed _failures=%d _transitions=%d targetU2M=%d targetM2U=%d\n", + printf("Test failed _failures=%d _transitions=%d targetU2M=%d targetM2U=%d nestedNull=%d\n", _failures.load(), _transitions.load(), - (int)_targetUnmanagedToManaged, (int)_targetManagedToUnmanaged); + (int)_targetUnmanagedToManaged, (int)_targetManagedToUnmanaged, + _nestedNullTransitions.load()); } fflush(stdout); return S_OK; @@ -115,12 +120,19 @@ void IjwProfiler::HandleTransition(bool unmanagedToManaged, FunctionID functionI // nested reverse (unmanaged->managed) marshaling stub. Post-fix these report // a NULL FunctionID; a non-null value here is exactly the 120151 bug (a // resolvable-but-wrong pointer would slip past the check above). - if (_insideTarget.load() && functionID != 0) + if (_insideTarget.load()) { - _failures++; - printf("FAIL: nested %s inside target reported non-null FunctionID=0x%p (reason=%d)\n", - which, (void*)functionID, (int)reason); - fflush(stdout); + if (functionID != 0) + { + _failures++; + printf("FAIL: nested %s inside target reported non-null FunctionID=0x%p (reason=%d)\n", + which, (void*)functionID, (int)reason); + fflush(stdout); + } + else + { + _nestedNullTransitions++; + } } } diff --git a/src/tests/profiler/native/ijw/ijwprofiler.h b/src/tests/profiler/native/ijw/ijwprofiler.h index f139ebcfa71f50..0d3b4a66a36503 100644 --- a/src/tests/profiler/native/ijw/ijwprofiler.h +++ b/src/tests/profiler/native/ijw/ijwprofiler.h @@ -29,6 +29,7 @@ class IjwProfiler : public Profiler , _targetUnmanagedToManaged(NO_TRANSITION) , _targetManagedToUnmanaged(NO_TRANSITION) , _insideTarget(false) + , _nestedNullTransitions(0) {} virtual ~IjwProfiler() = default; @@ -51,5 +52,10 @@ class IjwProfiler : public Profiler // checked. The profilee is single-threaded around this call. std::atomic _insideTarget; + // Number of nested reverse-stub transitions observed with a NULL FunctionID + // while inside the target. Must be > 0 so the "reverse stub reports NULL" + // contract is positively exercised, not merely never violated. + std::atomic _nestedNullTransitions; + void HandleTransition(bool unmanagedToManaged, FunctionID functionID, COR_PRF_TRANSITION_REASON reason); }; From d160534b128810130a2574595749745bafdacc30 Mon Sep 17 00:00:00 2001 From: lateralusX Date: Thu, 30 Jul 2026 16:44:40 +0200 Subject: [PATCH 4/6] Review feedback. --- src/tests/profiler/ijw/CMakeLists.txt | 26 ++++++++++++++++---------- src/tests/profiler/ijw/ijw.cs | 6 ++++-- src/tests/profiler/ijw/ijw.csproj | 4 ---- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/tests/profiler/ijw/CMakeLists.txt b/src/tests/profiler/ijw/CMakeLists.txt index 129b5398e91790..1922fb04e46a04 100644 --- a/src/tests/profiler/ijw/CMakeLists.txt +++ b/src/tests/profiler/ijw/CMakeLists.txt @@ -1,13 +1,19 @@ -project (IjwProfileeDll) -include(${CLR_ENG_NATIVE_DIR}/ijw/IJW.cmake) +# The IJW (C++/CLI) profilee is Windows-only. This directory is picked up +# unconditionally by the recursive add_subdirectory globber in +# src/tests/CMakeLists.txt, so it must guard itself against non-Windows targets: +# IJW.cmake only defines add_ijw_msbuild_project_properties on Windows hosts. +if (CLR_CMAKE_TARGET_WIN32) + project (IjwProfileeDll) + include(${CLR_ENG_NATIVE_DIR}/ijw/IJW.cmake) -include_directories( ${INC_PLATFORM_DIR} ) -set(SOURCES IjwProfileeDll.cpp) + include_directories( ${INC_PLATFORM_DIR} ) + set(SOURCES IjwProfileeDll.cpp) -# add the shared library -add_library (IjwProfileeDll SHARED ${SOURCES}) -target_link_libraries(IjwProfileeDll PRIVATE ${LINK_LIBRARIES_ADDITIONAL}) -add_ijw_msbuild_project_properties(IjwProfileeDll ijwhost) + # add the shared library + add_library (IjwProfileeDll SHARED ${SOURCES}) + target_link_libraries(IjwProfileeDll PRIVATE ${LINK_LIBRARIES_ADDITIONAL}) + add_ijw_msbuild_project_properties(IjwProfileeDll ijwhost) -# add the install targets -install (TARGETS IjwProfileeDll DESTINATION bin) + # add the install targets + install (TARGETS IjwProfileeDll DESTINATION bin) +endif() diff --git a/src/tests/profiler/ijw/ijw.cs b/src/tests/profiler/ijw/ijw.cs index 4888e4c923ef30..7ce2fb6dd8cdfb 100644 --- a/src/tests/profiler/ijw/ijw.cs +++ b/src/tests/profiler/ijw/ijw.cs @@ -17,9 +17,11 @@ class Ijw private static int CallManagedFunctionByPointer() { Assembly ijwDll = Assembly.Load("IjwProfileeDll"); - Type testType = ijwDll.GetType("TestClass"); + Type testType = ijwDll.GetType("TestClass") + ?? throw new InvalidOperationException("Could not find type 'TestClass' in IjwProfileeDll."); object testInstance = Activator.CreateInstance(testType); - MethodInfo method = testType.GetMethod("CallManagedFunctionByPointer"); + MethodInfo method = testType.GetMethod("CallManagedFunctionByPointer") + ?? throw new InvalidOperationException("Could not find method 'CallManagedFunctionByPointer' on TestClass."); return (int)method.Invoke(testInstance, null); } diff --git a/src/tests/profiler/ijw/ijw.csproj b/src/tests/profiler/ijw/ijw.csproj index 82a41a69437ed0..c9335a4ded73c2 100644 --- a/src/tests/profiler/ijw/ijw.csproj +++ b/src/tests/profiler/ijw/ijw.csproj @@ -3,13 +3,9 @@ .NETCoreApp exe true - - true false true - - true true