Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support filtering ObjectAllocated callback for pinned object heap allocation only #55448

Merged
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
3 changes: 3 additions & 0 deletions src/coreclr/inc/corprof.idl
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,9 @@ typedef enum

COR_PRF_HIGH_MONITOR_EVENT_PIPE = 0x00000080,

// Enables the pinned object allocation monitoring.
COR_PRF_HIGH_MONITOR_PINNEDOBJECT_ALLOCATED = 0x00000100,

COR_PRF_HIGH_ALLOWABLE_AFTER_ATTACH = COR_PRF_HIGH_IN_MEMORY_SYMBOLS_UPDATED |
COR_PRF_HIGH_MONITOR_DYNAMIC_FUNCTION_UNLOADS |
COR_PRF_HIGH_BASIC_GC |
Expand Down
15 changes: 15 additions & 0 deletions src/coreclr/inc/profilepriv.inl
Original file line number Diff line number Diff line change
Expand Up @@ -1860,6 +1860,21 @@ inline BOOL CORProfilerTrackLargeAllocations()
(&g_profControlBlock)->globalEventMask.IsEventMaskHighSet(COR_PRF_HIGH_MONITOR_LARGEOBJECT_ALLOCATED));
}

inline BOOL CORProfilerTrackPinnedAllocations()
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
CANNOT_TAKE_LOCK;
}
CONTRACTL_END;

return
(CORProfilerPresent() &&
(&g_profControlBlock)->globalEventMask.IsEventMaskHighSet(COR_PRF_HIGH_MONITOR_PINNEDOBJECT_ALLOCATED));
}

inline BOOL CORProfilerEnableRejit()
{
CONTRACTL
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/pal/prebuilt/inc/corprof.h
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,7 @@ enum __MIDL___MIDL_itf_corprof_0000_0000_0006
COR_PRF_HIGH_REQUIRE_PROFILE_IMAGE = 0,
COR_PRF_HIGH_MONITOR_LARGEOBJECT_ALLOCATED = 0x40,
COR_PRF_HIGH_MONITOR_EVENT_PIPE = 0x80,
COR_PRF_HIGH_MONITOR_PINNEDOBJECT_ALLOCATED = 0x100,
COR_PRF_HIGH_ALLOWABLE_AFTER_ATTACH = ( ( ( ( ( COR_PRF_HIGH_IN_MEMORY_SYMBOLS_UPDATED | COR_PRF_HIGH_MONITOR_DYNAMIC_FUNCTION_UNLOADS ) | COR_PRF_HIGH_BASIC_GC ) | COR_PRF_HIGH_MONITOR_GC_MOVED_OBJECTS ) | COR_PRF_HIGH_MONITOR_LARGEOBJECT_ALLOCATED ) | COR_PRF_HIGH_MONITOR_EVENT_PIPE ) ,
COR_PRF_HIGH_ALLOWABLE_NOTIFICATION_PROFILER = ( ( ( ( ( ( COR_PRF_HIGH_IN_MEMORY_SYMBOLS_UPDATED | COR_PRF_HIGH_MONITOR_DYNAMIC_FUNCTION_UNLOADS ) | COR_PRF_HIGH_DISABLE_TIERED_COMPILATION ) | COR_PRF_HIGH_BASIC_GC ) | COR_PRF_HIGH_MONITOR_GC_MOVED_OBJECTS ) | COR_PRF_HIGH_MONITOR_LARGEOBJECT_ALLOCATED ) | COR_PRF_HIGH_MONITOR_EVENT_PIPE ) ,
COR_PRF_HIGH_MONITOR_IMMUTABLE = COR_PRF_HIGH_DISABLE_TIERED_COMPILATION
Expand Down
8 changes: 8 additions & 0 deletions src/coreclr/vm/eeprofinterfaces.inl
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,13 @@ FORCEINLINE BOOL TrackLargeAllocations()
#endif // PROFILING_SUPPORTED
}

FORCEINLINE BOOL TrackPinnedAllocations()
{
#ifdef PROFILING_SUPPORTED
return CORProfilerTrackPinnedAllocations();
#else
return FALSE;
#endif // PROFILING_SUPPORTED
}

#endif
3 changes: 2 additions & 1 deletion src/coreclr/vm/gchelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,8 @@ void PublishObjectAndNotify(TObj* &orObject, GC_ALLOC_FLAGS flags)
// Notify the profiler of the allocation
// do this after initializing bounds so callback has size information
if (TrackAllocations() ||
(TrackLargeAllocations() && flags & GC_ALLOC_LARGE_OBJECT_HEAP))
(TrackLargeAllocations() && flags & GC_ALLOC_LARGE_OBJECT_HEAP) ||
(TrackPinnedAllocations() && flags & GC_ALLOC_PINNED_OBJECT_HEAP))
{
OBJECTREF objref = ObjectToOBJECTREF((Object*)orObject);
GCPROTECT_BEGIN(objref);
Expand Down
33 changes: 33 additions & 0 deletions src/tests/profiler/gc/gcallocate.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// 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.Threading;

namespace Profiler.Tests
{
class GCAllocateTests
{
static readonly Guid GcAllocateEventsProfilerGuid = new Guid("55b9554d-6115-45a2-be1e-c80f7fa35369");

public static int RunTest(String[] args)
{
int[] large = new int[100000];
int[] pinned = GC.AllocateArray<int>(32, true);
Console.WriteLine("Test Passed");
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: "GCCallbacksAllocate",
profilerClsid: GcAllocateEventsProfilerGuid);
}
}
}
23 changes: 23 additions & 0 deletions src/tests/profiler/gc/gcallocate.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>
1 change: 1 addition & 0 deletions src/tests/profiler/native/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ set(SOURCES
eventpipeprofiler/eventpipereadingprofiler.cpp
eventpipeprofiler/eventpipewritingprofiler.cpp
eventpipeprofiler/eventpipemetadatareader.cpp
gcallocateprofiler/gcallocateprofiler.cpp
gcbasicprofiler/gcbasicprofiler.cpp
gcprofiler/gcprofiler.cpp
getappdomainstaticaddress/getappdomainstaticaddress.cpp
Expand Down
2 changes: 2 additions & 0 deletions src/tests/profiler/native/classfactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "eventpipeprofiler/eventpipereadingprofiler.h"
#include "eventpipeprofiler/eventpipewritingprofiler.h"
#include "getappdomainstaticaddress/getappdomainstaticaddress.h"
#include "gcallocateprofiler/gcallocateprofiler.h"
#include "gcbasicprofiler/gcbasicprofiler.h"
#include "gcprofiler/gcprofiler.h"
#include "metadatagetdispenser/metadatagetdispenser.h"
Expand Down Expand Up @@ -61,6 +62,7 @@ HRESULT STDMETHODCALLTYPE ClassFactory::CreateInstance(IUnknown *pUnkOuter, REFI

//A little simplistic, we create an instance of every profiler, then return the one whose CLSID matches
Profiler* profilers[] = {
new GCAllocateProfiler(),
new GCBasicProfiler(),
new ReJITProfiler(),
new EventPipeReadingProfiler(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#include "gcallocateprofiler.h"

GUID GCAllocateProfiler::GetClsid()
{
// {55b9554d-6115-45a2-be1e-c80f7fa35369}
GUID clsid = { 0x55b9554d, 0x6115, 0x45a2,{ 0xbe, 0x1e, 0xc8, 0x0f, 0x7f, 0xa3, 0x53, 0x69 } };
return clsid;
}

HRESULT GCAllocateProfiler::Initialize(IUnknown* pICorProfilerInfoUnk)
{
Profiler::Initialize(pICorProfilerInfoUnk);

HRESULT hr = S_OK;
if (FAILED(hr = pCorProfilerInfo->SetEventMask2(COR_PRF_ENABLE_OBJECT_ALLOCATED, COR_PRF_HIGH_BASIC_GC | COR_PRF_HIGH_MONITOR_LARGEOBJECT_ALLOCATED | COR_PRF_HIGH_MONITOR_PINNEDOBJECT_ALLOCATED)))
{
printf("FAIL: ICorProfilerInfo::SetEventMask2() failed hr=0x%x", hr);
return hr;
}

return S_OK;
}

HRESULT STDMETHODCALLTYPE GCAllocateProfiler::ObjectAllocated(ObjectID objectId, ClassID classId)
{
COR_PRF_GC_GENERATION_RANGE gen;
HRESULT hr = pCorProfilerInfo->GetObjectGeneration(objectId, &gen);
if (FAILED(hr))
{
printf("GetObjectGeneration failed hr=0x%x\n", hr);
_failures++;
}
else if (gen.generation == COR_PRF_GC_LARGE_OBJECT_HEAP)
{
_gcLOHAllocations++;
}
else if (gen.generation == COR_PRF_GC_PINNED_OBJECT_HEAP)
{
_gcPOHAllocations++;
}
else
{
printf("Unexpected object allocation captured, gen.generation=0x%x\n", gen.generation);
_failures++;
}

return S_OK;
}

HRESULT GCAllocateProfiler::Shutdown()
{
Profiler::Shutdown();
if (_gcPOHAllocations == 0)
{
printf("There is no POH allocations\n");
}
else if (_gcLOHAllocations == 0)
{
printf("There is no LOH allocations\n");
}
else if (_failures == 0)
{
printf("%d LOH objects allocated\n", (int)_gcLOHAllocations);
printf("%d POH objects allocated\n", (int)_gcPOHAllocations);
printf("PROFILER TEST PASSES\n");
}
fflush(stdout);

return S_OK;
}
26 changes: 26 additions & 0 deletions src/tests/profiler/native/gcallocateprofiler/gcallocateprofiler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// 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"

class GCAllocateProfiler : public Profiler
{
public:
GCAllocateProfiler() : Profiler(),
_gcLOHAllocations(0),
_gcPOHAllocations(0),
_failures(0)
{}

virtual GUID GetClsid();
virtual HRESULT STDMETHODCALLTYPE Initialize(IUnknown* pICorProfilerInfoUnk);
virtual HRESULT STDMETHODCALLTYPE ObjectAllocated(ObjectID objectId, ClassID classId);
virtual HRESULT STDMETHODCALLTYPE Shutdown();

private:
std::atomic<int> _gcLOHAllocations;
std::atomic<int> _gcPOHAllocations;
std::atomic<int> _failures;
};
4 changes: 2 additions & 2 deletions src/tests/profiler/native/gcbasicprofiler/gcbasicprofiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ HRESULT GCBasicProfiler::Initialize(IUnknown* pICorProfilerInfoUnk)
Profiler::Initialize(pICorProfilerInfoUnk);

HRESULT hr = S_OK;
if (FAILED(hr = pCorProfilerInfo->SetEventMask2(0, 0x10)))
if (FAILED(hr = pCorProfilerInfo->SetEventMask2(0, COR_PRF_HIGH_BASIC_GC)))
{
_failures++;
printf("FAIL: ICorProfilerInfo::SetEventMask2() failed hr=0x%x", hr);
Expand All @@ -31,7 +31,7 @@ HRESULT GCBasicProfiler::Shutdown()

if (_gcStarts == 0)
{
printf("GCBasicProfiler::Shutdown: FAIL: Expected GarbaseCollectionStarted to be called\n");
printf("GCBasicProfiler::Shutdown: FAIL: Expected GarbageCollectionStarted to be called\n");
}
else if (_gcFinishes == 0)
{
Expand Down
2 changes: 1 addition & 1 deletion src/tests/profiler/native/gcprofiler/gcprofiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ HRESULT GCProfiler::Shutdown()

if (_gcStarts == 0)
{
printf("GCProfiler::Shutdown: FAIL: Expected GarbaseCollectionStarted to be called\n");
printf("GCProfiler::Shutdown: FAIL: Expected GarbageCollectionStarted to be called\n");
}
else if (_gcFinishes == 0)
{
Expand Down