Skip to content

Commit aea5cb9

Browse files
authored
add test for profiler inlining (dotnet#60225)
1 parent 60b51e4 commit aea5cb9

7 files changed

Lines changed: 307 additions & 2 deletions

File tree

src/tests/profiler/native/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ set(SOURCES
1111
gcbasicprofiler/gcbasicprofiler.cpp
1212
gcprofiler/gcprofiler.cpp
1313
getappdomainstaticaddress/getappdomainstaticaddress.cpp
14+
inlining/inlining.cpp
1415
metadatagetdispenser/metadatagetdispenser.cpp
1516
multiple/multiple.cpp
1617
nullprofiler/nullprofiler.cpp

src/tests/profiler/native/classfactory.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "releaseondetach/releaseondetach.h"
1616
#include "transitions/transitions.h"
1717
#include "multiple/multiple.h"
18+
#include "inlining/inlining.h"
1819

1920
ClassFactory::ClassFactory(REFCLSID clsid) : refCount(0), clsid(clsid)
2021
{
@@ -114,6 +115,10 @@ HRESULT STDMETHODCALLTYPE ClassFactory::CreateInstance(IUnknown *pUnkOuter, REFI
114115
{
115116
profiler = new MultiplyLoaded();
116117
}
118+
else if (clsid == InliningProfiler::GetClsid())
119+
{
120+
profiler = new InliningProfiler();
121+
}
117122
else
118123
{
119124
printf("No profiler found in ClassFactory::CreateInstance. Did you add your profiler to the list?\n");

src/tests/profiler/native/eltprofiler/slowpatheltprofiler.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ shared_ptr<SlowPathELTProfiler> SlowPathELTProfiler::s_profiler;
2121

2222
#ifndef WIN32
2323
#define UINT_PTR_FORMAT "lx"
24-
#define PROFILER_STUB EXTERN_C __attribute__((visibility("hidden"))) void STDMETHODCALLTYPE
24+
#define PROFILER_STUB __attribute__((visibility("hidden"))) static void STDMETHODCALLTYPE
2525
#else // WIN32
2626
#define UINT_PTR_FORMAT "llx"
27-
#define PROFILER_STUB EXTERN_C void STDMETHODCALLTYPE
27+
#define PROFILER_STUB static void STDMETHODCALLTYPE
2828
#endif // WIN32
2929

3030
PROFILER_STUB EnterStub(FunctionIDOrClientID functionId, COR_PRF_ELT_INFO eltInfo)
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
#define NOMINMAX
5+
6+
#include "inlining.h"
7+
#include <iostream>
8+
9+
using std::shared_ptr;
10+
using std::wcout;
11+
using std::endl;
12+
using std::atomic;
13+
14+
shared_ptr<InliningProfiler> InliningProfiler::s_profiler;
15+
16+
#ifndef WIN32
17+
#define UINT_PTR_FORMAT "lx"
18+
#define PROFILER_STUB __attribute__((visibility("hidden"))) static void STDMETHODCALLTYPE
19+
#else // WIN32
20+
#define UINT_PTR_FORMAT "llx"
21+
#define PROFILER_STUB static void STDMETHODCALLTYPE
22+
#endif // WIN32
23+
24+
PROFILER_STUB EnterStub(FunctionIDOrClientID functionId, COR_PRF_ELT_INFO eltInfo)
25+
{
26+
SHUTDOWNGUARD_RETVOID();
27+
28+
InliningProfiler::s_profiler->EnterCallback(functionId, eltInfo);
29+
}
30+
31+
PROFILER_STUB LeaveStub(FunctionIDOrClientID functionId, COR_PRF_ELT_INFO eltInfo)
32+
{
33+
SHUTDOWNGUARD_RETVOID();
34+
35+
InliningProfiler::s_profiler->LeaveCallback(functionId, eltInfo);
36+
}
37+
38+
PROFILER_STUB TailcallStub(FunctionIDOrClientID functionId, COR_PRF_ELT_INFO eltInfo)
39+
{
40+
SHUTDOWNGUARD_RETVOID();
41+
42+
InliningProfiler::s_profiler->TailcallCallback(functionId, eltInfo);
43+
}
44+
45+
GUID InliningProfiler::GetClsid()
46+
{
47+
// {DDADC0CB-21C8-4E53-9A6C-7C65EE5800CE}
48+
GUID clsid = { 0xDDADC0CB, 0x21C8, 0x4E53, { 0x9A, 0x6C, 0x7C, 0x65, 0xEE, 0x58, 0x00, 0xCE } };
49+
return clsid;
50+
}
51+
52+
HRESULT InliningProfiler::Initialize(IUnknown* pICorProfilerInfoUnk)
53+
{
54+
Profiler::Initialize(pICorProfilerInfoUnk);
55+
56+
HRESULT hr = S_OK;
57+
InliningProfiler::s_profiler = shared_ptr<InliningProfiler>(this);
58+
59+
if (FAILED(hr = pCorProfilerInfo->SetEventMask2(COR_PRF_MONITOR_ENTERLEAVE
60+
| COR_PRF_ENABLE_FUNCTION_ARGS
61+
| COR_PRF_ENABLE_FUNCTION_RETVAL
62+
| COR_PRF_ENABLE_FRAME_INFO
63+
| COR_PRF_MONITOR_JIT_COMPILATION,
64+
0)))
65+
{
66+
wcout << L"FAIL: IpCorProfilerInfo::SetEventMask2() failed hr=0x" << std::hex << hr << endl;
67+
_failures++;
68+
return hr;
69+
}
70+
71+
hr = this->pCorProfilerInfo->SetEnterLeaveFunctionHooks3WithInfo(EnterStub, LeaveStub, TailcallStub);
72+
if (hr != S_OK)
73+
{
74+
wcout << L"SetEnterLeaveFunctionHooks3WithInfo failed with hr=0x" << std::hex << hr << endl;
75+
_failures++;
76+
return hr;
77+
}
78+
79+
return S_OK;
80+
}
81+
82+
HRESULT InliningProfiler::Shutdown()
83+
{
84+
Profiler::Shutdown();
85+
86+
if (_failures == 0 && _sawInlineeCall)
87+
{
88+
wcout << L"PROFILER TEST PASSES" << endl;
89+
}
90+
else
91+
{
92+
wcout << L"TEST FAILED _failures=" << _failures.load()
93+
<< L"_sawInlineeCall=" << _sawInlineeCall << endl;
94+
}
95+
96+
return S_OK;
97+
}
98+
99+
HRESULT STDMETHODCALLTYPE InliningProfiler::EnterCallback(FunctionIDOrClientID functionIdOrClientID, COR_PRF_ELT_INFO eltInfo)
100+
{
101+
String functionName = GetFunctionIDName(functionIdOrClientID.functionID);
102+
if (functionName == WCHAR("Inlining"))
103+
{
104+
_inInlining = true;
105+
}
106+
else if (functionName == WCHAR("BlockInlining"))
107+
{
108+
_inBlockInlining = true;
109+
}
110+
else if (functionName == WCHAR("NoResponse"))
111+
{
112+
_inNoResponse = true;
113+
}
114+
else if (functionName == WCHAR("Inlinee"))
115+
{
116+
if (_inInlining || _inNoResponse)
117+
{
118+
_failures++;
119+
wcout << L"Saw Inlinee as a real method call instead of inlined." << endl;
120+
}
121+
122+
if (_inBlockInlining)
123+
{
124+
_sawInlineeCall = true;
125+
}
126+
}
127+
128+
return S_OK;
129+
}
130+
131+
HRESULT STDMETHODCALLTYPE InliningProfiler::LeaveCallback(FunctionIDOrClientID functionIdOrClientID, COR_PRF_ELT_INFO eltInfo)
132+
{
133+
String functionName = GetFunctionIDName(functionIdOrClientID.functionID);
134+
if (functionName == WCHAR("Inlining"))
135+
{
136+
_inInlining = false;
137+
}
138+
else if (functionName == WCHAR("BlockInlining"))
139+
{
140+
_inBlockInlining = false;
141+
}
142+
else if (functionName == WCHAR("NoResponse"))
143+
{
144+
_inNoResponse = false;
145+
}
146+
147+
return S_OK;
148+
}
149+
150+
HRESULT STDMETHODCALLTYPE InliningProfiler::TailcallCallback(FunctionIDOrClientID functionIdOrClientID, COR_PRF_ELT_INFO eltInfo)
151+
{
152+
return S_OK;
153+
}
154+
155+
HRESULT STDMETHODCALLTYPE InliningProfiler::JITInlining(FunctionID callerId, FunctionID calleeId, BOOL* pfShouldInline)
156+
{
157+
String inlineeName = GetFunctionIDName(calleeId);
158+
if (inlineeName == WCHAR("Inlinee"))
159+
{
160+
String inlinerName = GetFunctionIDName(callerId);
161+
if (inlinerName == WCHAR("Inlining"))
162+
{
163+
*pfShouldInline = TRUE;
164+
}
165+
else if (inlinerName == WCHAR("BlockInlining"))
166+
{
167+
*pfShouldInline = FALSE;
168+
}
169+
}
170+
171+
return S_OK;
172+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
#pragma once
5+
6+
#include "../profiler.h"
7+
#include <memory>
8+
9+
class InliningProfiler : public Profiler
10+
{
11+
public:
12+
static std::shared_ptr<InliningProfiler> s_profiler;
13+
14+
InliningProfiler() :
15+
Profiler(),
16+
_failures(0),
17+
_inInlining(false),
18+
_inBlockInlining(false),
19+
_inNoResponse(false)
20+
{
21+
}
22+
23+
static GUID GetClsid();
24+
virtual HRESULT STDMETHODCALLTYPE Initialize(IUnknown* pICorProfilerInfoUnk);
25+
virtual HRESULT STDMETHODCALLTYPE Shutdown();
26+
virtual HRESULT STDMETHODCALLTYPE JITInlining(FunctionID callerId, FunctionID calleeId, BOOL* pfShouldInline);
27+
28+
HRESULT STDMETHODCALLTYPE EnterCallback(FunctionIDOrClientID functionId, COR_PRF_ELT_INFO eltInfo);
29+
HRESULT STDMETHODCALLTYPE LeaveCallback(FunctionIDOrClientID functionId, COR_PRF_ELT_INFO eltInfo);
30+
HRESULT STDMETHODCALLTYPE TailcallCallback(FunctionIDOrClientID functionId, COR_PRF_ELT_INFO eltInfo);
31+
32+
private:
33+
std::atomic<int> _failures;
34+
bool _inInlining;
35+
bool _inBlockInlining;
36+
bool _inNoResponse;
37+
bool _sawInlineeCall;
38+
};
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.IO;
6+
using System.Runtime.CompilerServices;
7+
using System.Runtime.InteropServices;
8+
using System.Threading;
9+
10+
namespace Profiler.Tests
11+
{
12+
class InliningTest
13+
{
14+
private static readonly Guid InliningGuid = new Guid("DDADC0CB-21C8-4E53-9A6C-7C65EE5800CE");
15+
16+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
17+
public static int Inlinee()
18+
{
19+
Random rand = new Random();
20+
return rand.Next();
21+
}
22+
23+
[MethodImpl(MethodImplOptions.NoInlining)]
24+
public static void Inlining()
25+
{
26+
int x = Inlinee();
27+
Console.WriteLine($"Inlining, x={x}");
28+
}
29+
30+
[MethodImpl(MethodImplOptions.NoInlining)]
31+
public static void BlockInlining()
32+
{
33+
int x = Inlinee();
34+
Console.WriteLine($"BlockInlining, x={x}");
35+
}
36+
37+
[MethodImpl(MethodImplOptions.NoInlining)]
38+
public static void NoResponse()
39+
{
40+
int x = Inlinee();
41+
Console.WriteLine($"NoResponse, x={x}");
42+
}
43+
44+
public static int RunTest(string[] args)
45+
{
46+
Inlining();
47+
BlockInlining();
48+
NoResponse();
49+
50+
return 100;
51+
}
52+
53+
public static int Main(string[] args)
54+
{
55+
if (args.Length > 0 && args[0].Equals("RunTest", StringComparison.OrdinalIgnoreCase))
56+
{
57+
return RunTest(args);
58+
}
59+
60+
return ProfilerTestRunner.Run(profileePath: System.Reflection.Assembly.GetExecutingAssembly().Location,
61+
testName: "UnitTestInlining",
62+
profilerClsid: InliningGuid,
63+
profileeOptions: ProfileeOptions.OptimizationSensitive);
64+
}
65+
}
66+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFrameworkIdentifier>.NETCoreApp</TargetFrameworkIdentifier>
4+
<OutputType>exe</OutputType>
5+
<CLRTestKind>BuildAndRun</CLRTestKind>
6+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
7+
<CLRTestPriority>0</CLRTestPriority>
8+
<Optimize>true</Optimize>
9+
<!-- This test provides no interesting scenarios for GCStress -->
10+
<GCStressIncompatible>true</GCStressIncompatible>
11+
<!-- The test launches a secondary process and process launch creates
12+
an infinite event loop in the SocketAsyncEngine on Linux. Since
13+
runincontext loads even framework assemblies into the unloadable
14+
context, locals in this loop prevent unloading -->
15+
<UnloadabilityIncompatible>true</UnloadabilityIncompatible>
16+
</PropertyGroup>
17+
<ItemGroup>
18+
<Compile Include="$(MSBuildProjectName).cs" />
19+
<ProjectReference Include="$(TestSourceDir)Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" />
20+
<ProjectReference Include="../common/profiler_common.csproj" />
21+
<ProjectReference Include="$(MSBuildThisFileDirectory)/../native/CMakeLists.txt" />
22+
</ItemGroup>
23+
</Project>

0 commit comments

Comments
 (0)