Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/tests/profiler/native/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions src/tests/profiler/native/classfactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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");
Expand Down
4 changes: 2 additions & 2 deletions src/tests/profiler/native/eltprofiler/slowpatheltprofiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ shared_ptr<SlowPathELTProfiler> 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)
Expand Down
172 changes: 172 additions & 0 deletions src/tests/profiler/native/inlining/inlining.cpp
Original file line number Diff line number Diff line change
@@ -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 <iostream>

using std::shared_ptr;
using std::wcout;
using std::endl;
using std::atomic;

shared_ptr<InliningProfiler> 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<InliningProfiler>(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;
}
38 changes: 38 additions & 0 deletions src/tests/profiler/native/inlining/inlining.h
Original file line number Diff line number Diff line change
@@ -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 <memory>

class InliningProfiler : public Profiler
{
public:
static std::shared_ptr<InliningProfiler> 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<int> _failures;
bool _inInlining;
bool _inBlockInlining;
bool _inNoResponse;
bool _sawInlineeCall;
};
66 changes: 66 additions & 0 deletions src/tests/profiler/unittest/inlining.cs
Original file line number Diff line number Diff line change
@@ -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)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor Nit: Do we mean to block inlining via attribute on this method? It seems we want to verify that the profiler is making the decision to block inlining for this function.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the test I think the only method where David intended to modify the inlining behavior from the profiler is "Inlinee". For all other methods we are just ensuring they don't unintentionally get inlined so that the test will get enter/exit callbacks for them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, what Noah said 😄. I should have called this method "Inliner" and not "Inlining". I think it's actually worth updating for clarity

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);
}
}
}
23 changes: 23 additions & 0 deletions src/tests/profiler/unittest/inlining.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworkIdentifier>.NETCoreApp</TargetFrameworkIdentifier>
<OutputType>exe</OutputType>
<CLRTestKind>BuildAndRun</CLRTestKind>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<CLRTestPriority>0</CLRTestPriority>
<Optimize>true</Optimize>
<!-- This test provides no interesting scenarios for GCStress -->
<GCStressIncompatible>true</GCStressIncompatible>
<!-- The test launches a secondary process and process launch creates
an infinite event loop in the SocketAsyncEngine on Linux. Since
runincontext loads even framework assemblies into the unloadable
context, locals in this loop prevent unloading -->
<UnloadabilityIncompatible>true</UnloadabilityIncompatible>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildProjectName).cs" />
<ProjectReference Include="$(TestSourceDir)Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" />
<ProjectReference Include="../common/profiler_common.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../native/CMakeLists.txt" />
</ItemGroup>
</Project>