diff --git a/src/tests/profiler/native/CMakeLists.txt b/src/tests/profiler/native/CMakeLists.txt index b7357a678b15a9..b9a1f47e5ab803 100644 --- a/src/tests/profiler/native/CMakeLists.txt +++ b/src/tests/profiler/native/CMakeLists.txt @@ -11,6 +11,7 @@ set(SOURCES gcbasicprofiler/gcbasicprofiler.cpp gcprofiler/gcprofiler.cpp getappdomainstaticaddress/getappdomainstaticaddress.cpp + inlining/inlining.cpp metadatagetdispenser/metadatagetdispenser.cpp multiple/multiple.cpp nullprofiler/nullprofiler.cpp diff --git a/src/tests/profiler/native/classfactory.cpp b/src/tests/profiler/native/classfactory.cpp index d09cb4c4e3bb02..de0a07ce08937d 100644 --- a/src/tests/profiler/native/classfactory.cpp +++ b/src/tests/profiler/native/classfactory.cpp @@ -15,6 +15,7 @@ #include "releaseondetach/releaseondetach.h" #include "transitions/transitions.h" #include "multiple/multiple.h" +#include "inlining/inlining.h" ClassFactory::ClassFactory(REFCLSID clsid) : refCount(0), clsid(clsid) { @@ -114,6 +115,10 @@ HRESULT STDMETHODCALLTYPE ClassFactory::CreateInstance(IUnknown *pUnkOuter, REFI { profiler = new MultiplyLoaded(); } + else if (clsid == InliningProfiler::GetClsid()) + { + profiler = new InliningProfiler(); + } else { printf("No profiler found in ClassFactory::CreateInstance. Did you add your profiler to the list?\n"); diff --git a/src/tests/profiler/native/eltprofiler/slowpatheltprofiler.cpp b/src/tests/profiler/native/eltprofiler/slowpatheltprofiler.cpp index 17b114dd68b4c8..032dd3a82fa9ca 100644 --- a/src/tests/profiler/native/eltprofiler/slowpatheltprofiler.cpp +++ b/src/tests/profiler/native/eltprofiler/slowpatheltprofiler.cpp @@ -21,10 +21,10 @@ shared_ptr SlowPathELTProfiler::s_profiler; #ifndef WIN32 #define UINT_PTR_FORMAT "lx" -#define PROFILER_STUB EXTERN_C __attribute__((visibility("hidden"))) void STDMETHODCALLTYPE +#define PROFILER_STUB __attribute__((visibility("hidden"))) static void STDMETHODCALLTYPE #else // WIN32 #define UINT_PTR_FORMAT "llx" -#define PROFILER_STUB EXTERN_C void STDMETHODCALLTYPE +#define PROFILER_STUB static void STDMETHODCALLTYPE #endif // WIN32 PROFILER_STUB EnterStub(FunctionIDOrClientID functionId, COR_PRF_ELT_INFO eltInfo) diff --git a/src/tests/profiler/native/inlining/inlining.cpp b/src/tests/profiler/native/inlining/inlining.cpp new file mode 100644 index 00000000000000..0cd436833efd05 --- /dev/null +++ b/src/tests/profiler/native/inlining/inlining.cpp @@ -0,0 +1,172 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#define NOMINMAX + +#include "inlining.h" +#include + +using std::shared_ptr; +using std::wcout; +using std::endl; +using std::atomic; + +shared_ptr InliningProfiler::s_profiler; + +#ifndef WIN32 +#define UINT_PTR_FORMAT "lx" +#define PROFILER_STUB __attribute__((visibility("hidden"))) static void STDMETHODCALLTYPE +#else // WIN32 +#define UINT_PTR_FORMAT "llx" +#define PROFILER_STUB static void STDMETHODCALLTYPE +#endif // WIN32 + +PROFILER_STUB EnterStub(FunctionIDOrClientID functionId, COR_PRF_ELT_INFO eltInfo) +{ + SHUTDOWNGUARD_RETVOID(); + + InliningProfiler::s_profiler->EnterCallback(functionId, eltInfo); +} + +PROFILER_STUB LeaveStub(FunctionIDOrClientID functionId, COR_PRF_ELT_INFO eltInfo) +{ + SHUTDOWNGUARD_RETVOID(); + + InliningProfiler::s_profiler->LeaveCallback(functionId, eltInfo); +} + +PROFILER_STUB TailcallStub(FunctionIDOrClientID functionId, COR_PRF_ELT_INFO eltInfo) +{ + SHUTDOWNGUARD_RETVOID(); + + InliningProfiler::s_profiler->TailcallCallback(functionId, eltInfo); +} + +GUID InliningProfiler::GetClsid() +{ + // {DDADC0CB-21C8-4E53-9A6C-7C65EE5800CE} + GUID clsid = { 0xDDADC0CB, 0x21C8, 0x4E53, { 0x9A, 0x6C, 0x7C, 0x65, 0xEE, 0x58, 0x00, 0xCE } }; + return clsid; +} + +HRESULT InliningProfiler::Initialize(IUnknown* pICorProfilerInfoUnk) +{ + Profiler::Initialize(pICorProfilerInfoUnk); + + HRESULT hr = S_OK; + InliningProfiler::s_profiler = shared_ptr(this); + + if (FAILED(hr = pCorProfilerInfo->SetEventMask2(COR_PRF_MONITOR_ENTERLEAVE + | COR_PRF_ENABLE_FUNCTION_ARGS + | COR_PRF_ENABLE_FUNCTION_RETVAL + | COR_PRF_ENABLE_FRAME_INFO + | COR_PRF_MONITOR_JIT_COMPILATION, + 0))) + { + wcout << L"FAIL: IpCorProfilerInfo::SetEventMask2() failed hr=0x" << std::hex << hr << endl; + _failures++; + return hr; + } + + hr = this->pCorProfilerInfo->SetEnterLeaveFunctionHooks3WithInfo(EnterStub, LeaveStub, TailcallStub); + if (hr != S_OK) + { + wcout << L"SetEnterLeaveFunctionHooks3WithInfo failed with hr=0x" << std::hex << hr << endl; + _failures++; + return hr; + } + + return S_OK; +} + +HRESULT InliningProfiler::Shutdown() +{ + Profiler::Shutdown(); + + if (_failures == 0 && _sawInlineeCall) + { + wcout << L"PROFILER TEST PASSES" << endl; + } + else + { + wcout << L"TEST FAILED _failures=" << _failures.load() + << L"_sawInlineeCall=" << _sawInlineeCall << endl; + } + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE InliningProfiler::EnterCallback(FunctionIDOrClientID functionIdOrClientID, COR_PRF_ELT_INFO eltInfo) +{ + String functionName = GetFunctionIDName(functionIdOrClientID.functionID); + if (functionName == WCHAR("Inlining")) + { + _inInlining = true; + } + else if (functionName == WCHAR("BlockInlining")) + { + _inBlockInlining = true; + } + else if (functionName == WCHAR("NoResponse")) + { + _inNoResponse = true; + } + else if (functionName == WCHAR("Inlinee")) + { + if (_inInlining || _inNoResponse) + { + _failures++; + wcout << L"Saw Inlinee as a real method call instead of inlined." << endl; + } + + if (_inBlockInlining) + { + _sawInlineeCall = true; + } + } + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE InliningProfiler::LeaveCallback(FunctionIDOrClientID functionIdOrClientID, COR_PRF_ELT_INFO eltInfo) +{ + String functionName = GetFunctionIDName(functionIdOrClientID.functionID); + if (functionName == WCHAR("Inlining")) + { + _inInlining = false; + } + else if (functionName == WCHAR("BlockInlining")) + { + _inBlockInlining = false; + } + else if (functionName == WCHAR("NoResponse")) + { + _inNoResponse = false; + } + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE InliningProfiler::TailcallCallback(FunctionIDOrClientID functionIdOrClientID, COR_PRF_ELT_INFO eltInfo) +{ + return S_OK; +} + +HRESULT STDMETHODCALLTYPE InliningProfiler::JITInlining(FunctionID callerId, FunctionID calleeId, BOOL* pfShouldInline) +{ + String inlineeName = GetFunctionIDName(calleeId); + if (inlineeName == WCHAR("Inlinee")) + { + String inlinerName = GetFunctionIDName(callerId); + if (inlinerName == WCHAR("Inlining")) + { + *pfShouldInline = TRUE; + } + else if (inlinerName == WCHAR("BlockInlining")) + { + *pfShouldInline = FALSE; + } + } + + return S_OK; +} diff --git a/src/tests/profiler/native/inlining/inlining.h b/src/tests/profiler/native/inlining/inlining.h new file mode 100644 index 00000000000000..4b1b8752e3ec56 --- /dev/null +++ b/src/tests/profiler/native/inlining/inlining.h @@ -0,0 +1,38 @@ +// 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" +#include + +class InliningProfiler : public Profiler +{ +public: + static std::shared_ptr s_profiler; + + InliningProfiler() : + Profiler(), + _failures(0), + _inInlining(false), + _inBlockInlining(false), + _inNoResponse(false) + { + } + + static GUID GetClsid(); + virtual HRESULT STDMETHODCALLTYPE Initialize(IUnknown* pICorProfilerInfoUnk); + virtual HRESULT STDMETHODCALLTYPE Shutdown(); + virtual HRESULT STDMETHODCALLTYPE JITInlining(FunctionID callerId, FunctionID calleeId, BOOL* pfShouldInline); + + HRESULT STDMETHODCALLTYPE EnterCallback(FunctionIDOrClientID functionId, COR_PRF_ELT_INFO eltInfo); + HRESULT STDMETHODCALLTYPE LeaveCallback(FunctionIDOrClientID functionId, COR_PRF_ELT_INFO eltInfo); + HRESULT STDMETHODCALLTYPE TailcallCallback(FunctionIDOrClientID functionId, COR_PRF_ELT_INFO eltInfo); + +private: + std::atomic _failures; + bool _inInlining; + bool _inBlockInlining; + bool _inNoResponse; + bool _sawInlineeCall; +}; diff --git a/src/tests/profiler/unittest/inlining.cs b/src/tests/profiler/unittest/inlining.cs new file mode 100644 index 00000000000000..84b9cdb85f5e45 --- /dev/null +++ b/src/tests/profiler/unittest/inlining.cs @@ -0,0 +1,66 @@ +// 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.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; + +namespace Profiler.Tests +{ + class InliningTest + { + private static readonly Guid InliningGuid = new Guid("DDADC0CB-21C8-4E53-9A6C-7C65EE5800CE"); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Inlinee() + { + Random rand = new Random(); + return rand.Next(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Inlining() + { + int x = Inlinee(); + Console.WriteLine($"Inlining, x={x}"); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void BlockInlining() + { + int x = Inlinee(); + Console.WriteLine($"BlockInlining, x={x}"); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void NoResponse() + { + int x = Inlinee(); + Console.WriteLine($"NoResponse, x={x}"); + } + + public static int RunTest(string[] args) + { + Inlining(); + BlockInlining(); + NoResponse(); + + return 100; + } + + public static int Main(string[] args) + { + if (args.Length > 0 && args[0].Equals("RunTest", StringComparison.OrdinalIgnoreCase)) + { + return RunTest(args); + } + + return ProfilerTestRunner.Run(profileePath: System.Reflection.Assembly.GetExecutingAssembly().Location, + testName: "UnitTestInlining", + profilerClsid: InliningGuid, + profileeOptions: ProfileeOptions.OptimizationSensitive); + } + } +} diff --git a/src/tests/profiler/unittest/inlining.csproj b/src/tests/profiler/unittest/inlining.csproj new file mode 100644 index 00000000000000..b2fbdc913b04a2 --- /dev/null +++ b/src/tests/profiler/unittest/inlining.csproj @@ -0,0 +1,23 @@ + + + .NETCoreApp + exe + BuildAndRun + true + 0 + true + + true + + true + + + + + + + +