From 0ac29ee544efc9c221831425a8346c04f8aad530 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Wed, 19 Apr 2023 00:55:38 +0200 Subject: [PATCH 01/13] Report -1 for GC.GetGeneration(nongc_obj) and same for GetObjectGeneration --- src/coreclr/gc/gc.cpp | 5 +- src/coreclr/gc/gcinterface.h | 2 +- src/coreclr/inc/corerror.xml | 5 ++ src/coreclr/pal/prebuilt/corerror/mscorurt.rc | 5 +- src/coreclr/pal/prebuilt/inc/corerror.h | 1 + src/coreclr/vm/proftoeeinterfaceimpl.cpp | 9 +++ src/tests/profiler/gc/nongcheap.cs | 49 ++++++++++++ src/tests/profiler/gc/nongcheap.csproj | 21 ++++++ src/tests/profiler/native/CMakeLists.txt | 1 + src/tests/profiler/native/classfactory.cpp | 5 ++ .../profiler/native/nongcheap/nongcheap.cpp | 74 +++++++++++++++++++ .../profiler/native/nongcheap/nongcheap.h | 24 ++++++ 12 files changed, 196 insertions(+), 5 deletions(-) create mode 100644 src/tests/profiler/gc/nongcheap.cs create mode 100644 src/tests/profiler/gc/nongcheap.csproj create mode 100644 src/tests/profiler/native/nongcheap/nongcheap.cpp create mode 100644 src/tests/profiler/native/nongcheap/nongcheap.h diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 474e041974cf9..e343abeaa9245 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -45893,9 +45893,10 @@ unsigned int GCHeap::WhichGeneration (Object* object) { uint8_t* o = (uint8_t*)object; #ifdef FEATURE_BASICFREEZE - if (!((o < g_gc_highest_address) && (o >= g_gc_lowest_address))) + if (GCHeap::IsInFrozenSegment (object)) { - return max_generation; + // Report -1 for objects allocated on NonGC heaps (aka frozen segments) + return -1; } #endif //FEATURE_BASICFREEZE gc_heap* hp = gc_heap::heap_of (o); diff --git a/src/coreclr/gc/gcinterface.h b/src/coreclr/gc/gcinterface.h index d04e1f87cce9a..3d47943e15a88 100644 --- a/src/coreclr/gc/gcinterface.h +++ b/src/coreclr/gc/gcinterface.h @@ -984,7 +984,7 @@ void updateGCShadow(Object** ptr, Object* val); #define GC_CALL_INTERIOR 0x1 #define GC_CALL_PINNED 0x2 -// keep in sync with GC_ALLOC_FLAGS in GC.cs +// keep in sync with GC_ALLOC_FLAGS in GC.CoreCLR.cs enum GC_ALLOC_FLAGS { GC_ALLOC_NO_FLAGS = 0, diff --git a/src/coreclr/inc/corerror.xml b/src/coreclr/inc/corerror.xml index f9f604c28ae32..46cd387a09cc2 100644 --- a/src/coreclr/inc/corerror.xml +++ b/src/coreclr/inc/corerror.xml @@ -1182,6 +1182,11 @@ The runtime cannot be suspened since a suspension is already in progress. + + CORPROF_E_NOT_GC_OBJECT + This object belongs to a non-gc heap. + + CORSEC_E_POLICY_EXCEPTION "PolicyException thrown." diff --git a/src/coreclr/pal/prebuilt/corerror/mscorurt.rc b/src/coreclr/pal/prebuilt/corerror/mscorurt.rc index 6f808d6f8f84d..9f5297a657552 100644 --- a/src/coreclr/pal/prebuilt/corerror/mscorurt.rc +++ b/src/coreclr/pal/prebuilt/corerror/mscorurt.rc @@ -18,7 +18,7 @@ BEGIN MSG_FOR_URT_HR(FUSION_E_INVALID_NAME) "The given assembly name was invalid." MSG_FOR_URT_HR(FUSION_E_CACHEFILE_FAILED) "Failed to add file to AppDomain cache." MSG_FOR_URT_HR(FUSION_E_APP_DOMAIN_LOCKED) "The requested assembly version conflicts with what is already bound in the app domain or specified in the manifest." - MSG_FOR_URT_HR(COR_E_LOADING_REFERENCE_ASSEMBLY) "Reference assemblies cannot be loaded for execution." + MSG_FOR_URT_HR(COR_E_LOADING_REFERENCE_ASSEMBLY) "Reference assemblies cannot not be loaded for execution." MSG_FOR_URT_HR(COR_E_AMBIGUOUSIMPLEMENTATION) "Ambiguous implementation found." MSG_FOR_URT_HR(CLDB_E_FILE_BADREAD) "Error occurred during a read." MSG_FOR_URT_HR(CLDB_E_FILE_BADWRITE) "Error occurred during a write." @@ -245,7 +245,7 @@ BEGIN MSG_FOR_URT_HR(CORDBG_E_CANNOT_RESOLVE_ASSEMBLY) "We failed to resolve assembly given an AssemblyRef token. Assembly may be not loaded yet or not a valid token." MSG_FOR_URT_HR(CORDBG_E_MUST_BE_IN_LOAD_MODULE) "Must be in context of LoadModule callback to perform requested operation." MSG_FOR_URT_HR(CORDBG_E_CANNOT_BE_ON_ATTACH) "Requested operation cannot be performed during an attach operation." - MSG_FOR_URT_HR(CORDBG_E_NGEN_NOT_SUPPORTED) "NGEN is not supported." + MSG_FOR_URT_HR(CORDBG_E_NGEN_NOT_SUPPORTED) "NGEN must be supported to perform the requested operation." MSG_FOR_URT_HR(CORDBG_E_ILLEGAL_SHUTDOWN_ORDER) "Trying to shutdown out of order." MSG_FOR_URT_HR(CORDBG_E_CANNOT_DEBUG_FIBER_PROCESS) "Debugging fiber mode managed process is not supported." MSG_FOR_URT_HR(CORDBG_E_MUST_BE_IN_CREATE_PROCESS) "Must be in context of CreateProcess callback to perform requested operation." @@ -288,6 +288,7 @@ BEGIN MSG_FOR_URT_HR(CORDBG_E_MISSING_DEBUGGER_EXPORTS) "The debuggee memory space does not have the expected debugging export table." MSG_FOR_URT_HR(CORDBG_E_DATA_TARGET_ERROR) "Failure when calling a data target method." MSG_FOR_URT_HR(CORDBG_E_UNSUPPORTED_DELEGATE) "The delegate contains a delegate currently not supported by the API." + MSG_FOR_URT_HR(CORDBG_E_ASSEMBLY_UPDATES_APPLIED) "The operation is not supported because assembly updates have been applied." MSG_FOR_URT_HR(PEFMT_E_64BIT) "File is PE32+." MSG_FOR_URT_HR(PEFMT_E_32BIT) "File is PE32" MSG_FOR_URT_HR(CLR_E_BIND_ASSEMBLY_VERSION_TOO_LOW) "The bound assembly has a version that is lower than that of the request." diff --git a/src/coreclr/pal/prebuilt/inc/corerror.h b/src/coreclr/pal/prebuilt/inc/corerror.h index 6f0be602583ee..8b64c945ac9bc 100644 --- a/src/coreclr/pal/prebuilt/inc/corerror.h +++ b/src/coreclr/pal/prebuilt/inc/corerror.h @@ -214,6 +214,7 @@ #define CORDIAGIPC_E_UNKNOWN_MAGIC EMAKEHR(0x1386) #define CORDIAGIPC_E_UNKNOWN_ERROR EMAKEHR(0x1387) #define CORPROF_E_SUSPENSION_IN_PROGRESS EMAKEHR(0x1388) +#define CORPROF_E_NOT_GC_OBJECT EMAKEHR(0x1389) #define CORSEC_E_POLICY_EXCEPTION EMAKEHR(0x1416) #define CORSEC_E_MIN_GRANT_FAIL EMAKEHR(0x1417) #define CORSEC_E_NO_EXEC_PERM EMAKEHR(0x1418) diff --git a/src/coreclr/vm/proftoeeinterfaceimpl.cpp b/src/coreclr/vm/proftoeeinterfaceimpl.cpp index 1d78588b328a5..88b32052e029a 100644 --- a/src/coreclr/vm/proftoeeinterfaceimpl.cpp +++ b/src/coreclr/vm/proftoeeinterfaceimpl.cpp @@ -9141,6 +9141,15 @@ HRESULT ProfToEEInterfaceImpl::GetObjectGeneration(ObjectID objectId, IGCHeap *hp = GCHeapUtilities::GetGCHeap(); + if (hp->IsInFrozenSegment((Object*)objectId)) + { + range->generation = (COR_PRF_GC_GENERATION)-1; + range->rangeStart = 0; + range->rangeLength = 0; + range->rangeLengthReserved = 0; + return CORPROF_E_NOT_GC_OBJECT; + } + uint8_t* pStart; uint8_t* pAllocated; uint8_t* pReserved; diff --git a/src/tests/profiler/gc/nongcheap.cs b/src/tests/profiler/gc/nongcheap.cs new file mode 100644 index 0000000000000..8bdf447f88506 --- /dev/null +++ b/src/tests/profiler/gc/nongcheap.cs @@ -0,0 +1,49 @@ +// 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.Runtime.CompilerServices; +using System.Threading; + +namespace Profiler.Tests +{ + class NonGCHeapTests + { + static readonly Guid GcAllocateEventsProfilerGuid = new Guid("EF0D191C-3FC7-4311-88AF-E474CBEB2859"); + + [MethodImpl(MethodImplOptions.NoInlining)] + static void AllocateNonGcHeapObjects() + { + // When this method is invoked, JIT is expected to trigger allocations for these + // string literals and they're expected to end up in a nongc segment (also known as frozen) + Consume("string1"); + Consume("string2"); + Consume("string3"); + Consume("string4"); + Consume("string5"); + Consume("string6"); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static void Consume(object o) {} + + public static int RunTest(String[] args) + { + AllocateNonGcHeapObjects(); + 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: "NonGCHeapAllocate", + profilerClsid: GcAllocateEventsProfilerGuid); + } + } +} diff --git a/src/tests/profiler/gc/nongcheap.csproj b/src/tests/profiler/gc/nongcheap.csproj new file mode 100644 index 0000000000000..d51dcb692abfe --- /dev/null +++ b/src/tests/profiler/gc/nongcheap.csproj @@ -0,0 +1,21 @@ + + + .NETCoreApp + exe + true + true + + true + + true + + + + + + + + diff --git a/src/tests/profiler/native/CMakeLists.txt b/src/tests/profiler/native/CMakeLists.txt index a3d2d77902f38..feb446e8846df 100644 --- a/src/tests/profiler/native/CMakeLists.txt +++ b/src/tests/profiler/native/CMakeLists.txt @@ -8,6 +8,7 @@ set(SOURCES eventpipeprofiler/eventpipewritingprofiler.cpp eventpipeprofiler/eventpipemetadatareader.cpp gcallocateprofiler/gcallocateprofiler.cpp + nongcheap/nongcheap.cpp gcbasicprofiler/gcbasicprofiler.cpp gcprofiler/gcprofiler.cpp getappdomainstaticaddress/getappdomainstaticaddress.cpp diff --git a/src/tests/profiler/native/classfactory.cpp b/src/tests/profiler/native/classfactory.cpp index 418b0b680830b..7d8aa7942e5d9 100644 --- a/src/tests/profiler/native/classfactory.cpp +++ b/src/tests/profiler/native/classfactory.cpp @@ -7,6 +7,7 @@ #include "eventpipeprofiler/eventpipewritingprofiler.h" #include "getappdomainstaticaddress/getappdomainstaticaddress.h" #include "gcallocateprofiler/gcallocateprofiler.h" +#include "nongcheap/nongcheap.h" #include "gcbasicprofiler/gcbasicprofiler.h" #include "gcprofiler/gcprofiler.h" #include "handlesprofiler/handlesprofiler.h" @@ -69,6 +70,10 @@ HRESULT STDMETHODCALLTYPE ClassFactory::CreateInstance(IUnknown *pUnkOuter, REFI { profiler = new GCAllocateProfiler(); } + else if (clsid == NonGcHeapProfiler::GetClsid()) + { + profiler = new NonGcHeapProfiler(); + } else if (clsid == GCBasicProfiler::GetClsid()) { profiler = new GCBasicProfiler(); diff --git a/src/tests/profiler/native/nongcheap/nongcheap.cpp b/src/tests/profiler/native/nongcheap/nongcheap.cpp new file mode 100644 index 0000000000000..efb81e8d54564 --- /dev/null +++ b/src/tests/profiler/native/nongcheap/nongcheap.cpp @@ -0,0 +1,74 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "nongcheap.h" + +GUID NonGcHeapProfiler::GetClsid() +{ + // {EF0D191C-3FC7-4311-88AF-E474CBEB2859} + GUID clsid = { 0xef0d191c, 0x3fc7, 0x4311, { 0x88, 0xaf, 0xe4, 0x74, 0xcb, 0xeb, 0x28, 0x59 } }; + return clsid; +} + +HRESULT NonGcHeapProfiler::Initialize(IUnknown* pICorProfilerInfoUnk) +{ + Profiler::Initialize(pICorProfilerInfoUnk); + + HRESULT hr = S_OK; + if (FAILED(hr = pCorProfilerInfo->SetEventMask2( + COR_PRF_ENABLE_OBJECT_ALLOCATED | COR_PRF_MONITOR_OBJECT_ALLOCATED, + COR_PRF_HIGH_BASIC_GC))) + { + printf("FAIL: ICorProfilerInfo::SetEventMask2() failed hr=0x%x", hr); + return hr; + } + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE NonGcHeapProfiler::ObjectAllocated(ObjectID objectId, ClassID classId) +{ + COR_PRF_GC_GENERATION_RANGE gen; + HRESULT hr = pCorProfilerInfo->GetObjectGeneration(objectId, &gen); + + // non-GC objects (same for GC.GetGeneration() API) have generation = -1 + if (gen.generation == (COR_PRF_GC_GENERATION)-1) + { + if (!FAILED(hr)) + { + // We expect GetObjectGeneration to return an error (CORPROF_E_NOT_GC_OBJECT) + // for non-GC objects. + _failures++; + } + _nonGcHeapObjects++; + if (gen.rangeLength != 0 || gen.rangeLengthReserved != 0 || gen.rangeStart != 0) + { + _failures++; + } + } + else if (FAILED(hr)) + { + _failures++; + } + return S_OK; +} + +HRESULT NonGcHeapProfiler::Shutdown() +{ + if (_failures > 0) + { + printf("PROFILER TEST FAILS\n"); + } + else if (_nonGcHeapObjects == 0) + { + printf("PROFILER TEST FAILS: non-GC heap objects were not allocated\n"); + } + else + { + printf("PROFILER TEST PASSES\n"); + } + printf("Non-GC objects allocated: %d\n", (int)_nonGcHeapObjects); + printf("PROFILER TEST PASSES\n"); + fflush(stdout); + return S_OK; +} diff --git a/src/tests/profiler/native/nongcheap/nongcheap.h b/src/tests/profiler/native/nongcheap/nongcheap.h new file mode 100644 index 0000000000000..5594d0f796408 --- /dev/null +++ b/src/tests/profiler/native/nongcheap/nongcheap.h @@ -0,0 +1,24 @@ +// 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 NonGcHeapProfiler : public Profiler +{ +public: + NonGcHeapProfiler() : Profiler(), + _nonGcHeapObjects(0), + _failures(0) + {} + + static GUID GetClsid(); + virtual HRESULT STDMETHODCALLTYPE Initialize(IUnknown* pICorProfilerInfoUnk); + virtual HRESULT STDMETHODCALLTYPE ObjectAllocated(ObjectID objectId, ClassID classId); + virtual HRESULT STDMETHODCALLTYPE Shutdown(); + +private: + std::atomic _nonGcHeapObjects; + std::atomic _failures; +}; From 71f63b22c966a1fbdeed883efb5672dbc12ab464 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Wed, 19 Apr 2023 01:07:56 +0200 Subject: [PATCH 02/13] Update corerror.xml --- src/coreclr/inc/corerror.xml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/coreclr/inc/corerror.xml b/src/coreclr/inc/corerror.xml index 46cd387a09cc2..afeaf4f602a4a 100644 --- a/src/coreclr/inc/corerror.xml +++ b/src/coreclr/inc/corerror.xml @@ -198,8 +198,8 @@ COR_E_LOADING_REFERENCE_ASSEMBLY - "Reference assemblies cannot not be loaded for execution." - Reference assemblies cannot not be loaded for execution. + "Reference assemblies cannot be loaded for execution." + Reference assemblies cannot be loaded for execution @@ -1184,7 +1184,8 @@ CORPROF_E_NOT_GC_OBJECT - This object belongs to a non-gc heap. + "This object belongs to a non-gc heap." + This object belongs to a non-gc heap @@ -1793,8 +1794,8 @@ CORDBG_E_NGEN_NOT_SUPPORTED - "NGEN must be supported to perform the requested operation." - NGEN must be supported to perform the requested operation + "NGEN is not supported." + NGEN is not supported From c3754e0bc0b9e359a5f9c274201cf0f89146c238 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Wed, 19 Apr 2023 01:09:13 +0200 Subject: [PATCH 03/13] revert changes in error codes --- src/coreclr/pal/prebuilt/corerror/mscorurt.rc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/coreclr/pal/prebuilt/corerror/mscorurt.rc b/src/coreclr/pal/prebuilt/corerror/mscorurt.rc index 9f5297a657552..a30a45ef68019 100644 --- a/src/coreclr/pal/prebuilt/corerror/mscorurt.rc +++ b/src/coreclr/pal/prebuilt/corerror/mscorurt.rc @@ -18,7 +18,7 @@ BEGIN MSG_FOR_URT_HR(FUSION_E_INVALID_NAME) "The given assembly name was invalid." MSG_FOR_URT_HR(FUSION_E_CACHEFILE_FAILED) "Failed to add file to AppDomain cache." MSG_FOR_URT_HR(FUSION_E_APP_DOMAIN_LOCKED) "The requested assembly version conflicts with what is already bound in the app domain or specified in the manifest." - MSG_FOR_URT_HR(COR_E_LOADING_REFERENCE_ASSEMBLY) "Reference assemblies cannot not be loaded for execution." + MSG_FOR_URT_HR(COR_E_LOADING_REFERENCE_ASSEMBLY) "Reference assemblies cannot be loaded for execution." MSG_FOR_URT_HR(COR_E_AMBIGUOUSIMPLEMENTATION) "Ambiguous implementation found." MSG_FOR_URT_HR(CLDB_E_FILE_BADREAD) "Error occurred during a read." MSG_FOR_URT_HR(CLDB_E_FILE_BADWRITE) "Error occurred during a write." @@ -147,6 +147,7 @@ BEGIN MSG_FOR_URT_HR(CORPROF_E_NOT_YET_AVAILABLE) "Requested information is not yet available." MSG_FOR_URT_HR(CORPROF_E_TYPE_IS_PARAMETERIZED) "The given type is a generic and cannot be used with this method." MSG_FOR_URT_HR(CORPROF_E_FUNCTION_IS_PARAMETERIZED) "The given function is a generic and cannot be used with this method." + MSG_FOR_URT_HR(CORPROF_E_NOT_GC_OBJECT) "This object belongs to a non-gc heap." MSG_FOR_URT_HR(CORSEC_E_POLICY_EXCEPTION) "PolicyException thrown." MSG_FOR_URT_HR(CORSEC_E_MIN_GRANT_FAIL) "Failed to grant minimum permission requests." MSG_FOR_URT_HR(CORSEC_E_NO_EXEC_PERM) "Failed to grant permission to execute." @@ -245,7 +246,7 @@ BEGIN MSG_FOR_URT_HR(CORDBG_E_CANNOT_RESOLVE_ASSEMBLY) "We failed to resolve assembly given an AssemblyRef token. Assembly may be not loaded yet or not a valid token." MSG_FOR_URT_HR(CORDBG_E_MUST_BE_IN_LOAD_MODULE) "Must be in context of LoadModule callback to perform requested operation." MSG_FOR_URT_HR(CORDBG_E_CANNOT_BE_ON_ATTACH) "Requested operation cannot be performed during an attach operation." - MSG_FOR_URT_HR(CORDBG_E_NGEN_NOT_SUPPORTED) "NGEN must be supported to perform the requested operation." + MSG_FOR_URT_HR(CORDBG_E_NGEN_NOT_SUPPORTED) "NGEN is not supported." MSG_FOR_URT_HR(CORDBG_E_ILLEGAL_SHUTDOWN_ORDER) "Trying to shutdown out of order." MSG_FOR_URT_HR(CORDBG_E_CANNOT_DEBUG_FIBER_PROCESS) "Debugging fiber mode managed process is not supported." MSG_FOR_URT_HR(CORDBG_E_MUST_BE_IN_CREATE_PROCESS) "Must be in context of CreateProcess callback to perform requested operation." From d1268e578a39202e38f6a2fe2ca605a5348adcda Mon Sep 17 00:00:00 2001 From: EgorBo Date: Wed, 19 Apr 2023 11:34:17 +0200 Subject: [PATCH 04/13] Test commit --- src/coreclr/gc/gc.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index e343abeaa9245..11856c4da4bc7 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -45893,9 +45893,8 @@ unsigned int GCHeap::WhichGeneration (Object* object) { uint8_t* o = (uint8_t*)object; #ifdef FEATURE_BASICFREEZE - if (GCHeap::IsInFrozenSegment (object)) + if (!((o < g_gc_highest_address) && (o >= g_gc_lowest_address)) || GCHeap::IsInFrozenSegment (object)) { - // Report -1 for objects allocated on NonGC heaps (aka frozen segments) return -1; } #endif //FEATURE_BASICFREEZE From d57e1935f9b5b75cee5b99aeddd00bfc263b017c Mon Sep 17 00:00:00 2001 From: EgorBo Date: Wed, 19 Apr 2023 14:09:49 +0200 Subject: [PATCH 05/13] fix CI --- src/coreclr/gc/gc.cpp | 14 +++++++++++--- src/coreclr/gc/gcimpl.h | 2 +- src/coreclr/gc/gcinterface.h | 2 +- src/coreclr/nativeaot/Runtime/GCHelpers.cpp | 2 +- src/coreclr/vm/comutilnative.cpp | 4 ++-- 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 3a9076bc697e2..237997957e80b 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -45798,14 +45798,22 @@ void GCHeap::SetYieldProcessorScalingFactor (float scalingFactor) } } -unsigned int GCHeap::WhichGeneration (Object* object) +unsigned int GCHeap::WhichGeneration (Object* object, bool maxGenForNonGcObjects) { uint8_t* o = (uint8_t*)object; #ifdef FEATURE_BASICFREEZE - if (!((o < g_gc_highest_address) && (o >= g_gc_lowest_address)) || GCHeap::IsInFrozenSegment (object)) + if (!((o < g_gc_highest_address) && (o >= g_gc_lowest_address))) { - return -1; + return maxGenForNonGcObjects ? max_generation : -1; + } +#ifndef USE_REGIONS + if (GCHeap::IsInFrozenSegment (object)) + { + // in case if the object belongs to an in-range frozen segment + // For regions those are never in-range. + return maxGenForNonGcObjects ? max_generation : -1; } +#endif #endif //FEATURE_BASICFREEZE gc_heap* hp = gc_heap::heap_of (o); unsigned int g = hp->object_gennum (o); diff --git a/src/coreclr/gc/gcimpl.h b/src/coreclr/gc/gcimpl.h index b82f17a78f959..a5085c29b5268 100644 --- a/src/coreclr/gc/gcimpl.h +++ b/src/coreclr/gc/gcimpl.h @@ -151,7 +151,7 @@ class GCHeap : public IGCHeapInternal void SetFinalizationRun (Object* obj); //returns the generation number of an object (not valid during relocation) - unsigned WhichGeneration (Object* object); + unsigned WhichGeneration (Object* object, bool maxGenForNonGcObjects = true); // returns TRUE is the object is ephemeral bool IsEphemeral (Object* object); bool IsHeapPointer (void* object, bool small_heap_only = false); diff --git a/src/coreclr/gc/gcinterface.h b/src/coreclr/gc/gcinterface.h index 945629d02f712..d814f68c07e18 100644 --- a/src/coreclr/gc/gcinterface.h +++ b/src/coreclr/gc/gcinterface.h @@ -722,7 +722,7 @@ class IGCHeap { // Returns the generation in which obj is found. Also used by the VM // in some places, in particular syncblk code. - virtual unsigned WhichGeneration(Object* obj) PURE_VIRTUAL + virtual unsigned WhichGeneration(Object* obj, bool maxGenForNonGcObjects = true) PURE_VIRTUAL // Returns the number of GCs that have transpired in the given generation // since the beginning of the life of the process. Also used by the VM diff --git a/src/coreclr/nativeaot/Runtime/GCHelpers.cpp b/src/coreclr/nativeaot/Runtime/GCHelpers.cpp index d795d460753ea..a662d716cd61c 100644 --- a/src/coreclr/nativeaot/Runtime/GCHelpers.cpp +++ b/src/coreclr/nativeaot/Runtime/GCHelpers.cpp @@ -104,7 +104,7 @@ COOP_PINVOKE_HELPER(int32_t, RhGetGcCollectionCount, (int32_t generation, CLR_BO COOP_PINVOKE_HELPER(int32_t, RhGetGeneration, (OBJECTREF obj)) { - return GCHeapUtilities::GetGCHeap()->WhichGeneration(obj); + return GCHeapUtilities::GetGCHeap()->WhichGeneration(obj, false); } COOP_PINVOKE_HELPER(int32_t, RhGetGcLatencyMode, ()) diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index 488655ee7a62c..025b7d7ee0080 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -726,7 +726,7 @@ FCIMPL1(int, GCInterface::GetGeneration, Object* objUNSAFE) if (objUNSAFE == NULL) FCThrowArgumentNull(W("obj")); - int result = (INT32)GCHeapUtilities::GetGCHeap()->WhichGeneration(objUNSAFE); + int result = (INT32)GCHeapUtilities::GetGCHeap()->WhichGeneration(objUNSAFE, false); FC_GC_POLL_RET(); return result; } @@ -826,7 +826,7 @@ FCIMPL1(int, GCInterface::GetGenerationWR, LPVOID handle) if (temp == NULL) COMPlusThrowArgumentNull(W("wo")); - iRetVal = (INT32)GCHeapUtilities::GetGCHeap()->WhichGeneration(OBJECTREFToObject(temp)); + iRetVal = (INT32)GCHeapUtilities::GetGCHeap()->WhichGeneration(OBJECTREFToObject(temp), false); HELPER_METHOD_FRAME_END(); From 8720b4ff4186b456e1cfd6975d2551f42cdb852d Mon Sep 17 00:00:00 2001 From: EgorBo Date: Wed, 19 Apr 2023 20:09:11 +0200 Subject: [PATCH 06/13] try INT_MAX --- eng/native/configurecompiler.cmake | 1 + src/coreclr/gc/gc.cpp | 6 +++--- src/coreclr/gc/gcimpl.h | 2 +- src/coreclr/gc/gcinterface.h | 2 +- src/coreclr/nativeaot/Runtime/GCHelpers.cpp | 2 +- src/coreclr/vm/comutilnative.cpp | 4 ++-- .../SmokeTests/Preinitialization/Preinitialization.cs | 4 ++-- src/tests/profiler/gc/nongcheap.cs | 9 +++++++++ src/tests/profiler/native/nongcheap/nongcheap.cpp | 2 +- 9 files changed, 21 insertions(+), 11 deletions(-) diff --git a/eng/native/configurecompiler.cmake b/eng/native/configurecompiler.cmake index 0282e29f069ee..f62eaa53fa68a 100644 --- a/eng/native/configurecompiler.cmake +++ b/eng/native/configurecompiler.cmake @@ -87,6 +87,7 @@ if (MSVC) # Checked build specific flags add_linker_flag(/INCREMENTAL:NO CHECKED) # prevent "warning LNK4075: ignoring '/INCREMENTAL' due to '/OPT:REF' specification" + add_linker_flag(/INCREMENTAL:NO DEBUG) # prevent "warning LNK4075: ignoring '/INCREMENTAL' due to '/OPT:REF' specification" add_linker_flag(/OPT:REF CHECKED) add_linker_flag(/OPT:NOICF CHECKED) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 237997957e80b..a944a0541dea5 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -45798,20 +45798,20 @@ void GCHeap::SetYieldProcessorScalingFactor (float scalingFactor) } } -unsigned int GCHeap::WhichGeneration (Object* object, bool maxGenForNonGcObjects) +unsigned int GCHeap::WhichGeneration (Object* object) { uint8_t* o = (uint8_t*)object; #ifdef FEATURE_BASICFREEZE if (!((o < g_gc_highest_address) && (o >= g_gc_lowest_address))) { - return maxGenForNonGcObjects ? max_generation : -1; + return INT32_MAX; } #ifndef USE_REGIONS if (GCHeap::IsInFrozenSegment (object)) { // in case if the object belongs to an in-range frozen segment // For regions those are never in-range. - return maxGenForNonGcObjects ? max_generation : -1; + return INT32_MAX; } #endif #endif //FEATURE_BASICFREEZE diff --git a/src/coreclr/gc/gcimpl.h b/src/coreclr/gc/gcimpl.h index a5085c29b5268..b82f17a78f959 100644 --- a/src/coreclr/gc/gcimpl.h +++ b/src/coreclr/gc/gcimpl.h @@ -151,7 +151,7 @@ class GCHeap : public IGCHeapInternal void SetFinalizationRun (Object* obj); //returns the generation number of an object (not valid during relocation) - unsigned WhichGeneration (Object* object, bool maxGenForNonGcObjects = true); + unsigned WhichGeneration (Object* object); // returns TRUE is the object is ephemeral bool IsEphemeral (Object* object); bool IsHeapPointer (void* object, bool small_heap_only = false); diff --git a/src/coreclr/gc/gcinterface.h b/src/coreclr/gc/gcinterface.h index d814f68c07e18..945629d02f712 100644 --- a/src/coreclr/gc/gcinterface.h +++ b/src/coreclr/gc/gcinterface.h @@ -722,7 +722,7 @@ class IGCHeap { // Returns the generation in which obj is found. Also used by the VM // in some places, in particular syncblk code. - virtual unsigned WhichGeneration(Object* obj, bool maxGenForNonGcObjects = true) PURE_VIRTUAL + virtual unsigned WhichGeneration(Object* obj) PURE_VIRTUAL // Returns the number of GCs that have transpired in the given generation // since the beginning of the life of the process. Also used by the VM diff --git a/src/coreclr/nativeaot/Runtime/GCHelpers.cpp b/src/coreclr/nativeaot/Runtime/GCHelpers.cpp index a662d716cd61c..d795d460753ea 100644 --- a/src/coreclr/nativeaot/Runtime/GCHelpers.cpp +++ b/src/coreclr/nativeaot/Runtime/GCHelpers.cpp @@ -104,7 +104,7 @@ COOP_PINVOKE_HELPER(int32_t, RhGetGcCollectionCount, (int32_t generation, CLR_BO COOP_PINVOKE_HELPER(int32_t, RhGetGeneration, (OBJECTREF obj)) { - return GCHeapUtilities::GetGCHeap()->WhichGeneration(obj, false); + return GCHeapUtilities::GetGCHeap()->WhichGeneration(obj); } COOP_PINVOKE_HELPER(int32_t, RhGetGcLatencyMode, ()) diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index 025b7d7ee0080..488655ee7a62c 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -726,7 +726,7 @@ FCIMPL1(int, GCInterface::GetGeneration, Object* objUNSAFE) if (objUNSAFE == NULL) FCThrowArgumentNull(W("obj")); - int result = (INT32)GCHeapUtilities::GetGCHeap()->WhichGeneration(objUNSAFE, false); + int result = (INT32)GCHeapUtilities::GetGCHeap()->WhichGeneration(objUNSAFE); FC_GC_POLL_RET(); return result; } @@ -826,7 +826,7 @@ FCIMPL1(int, GCInterface::GetGenerationWR, LPVOID handle) if (temp == NULL) COMPlusThrowArgumentNull(W("wo")); - iRetVal = (INT32)GCHeapUtilities::GetGCHeap()->WhichGeneration(OBJECTREFToObject(temp), false); + iRetVal = (INT32)GCHeapUtilities::GetGCHeap()->WhichGeneration(OBJECTREFToObject(temp)); HELPER_METHOD_FRAME_END(); diff --git a/src/tests/nativeaot/SmokeTests/Preinitialization/Preinitialization.cs b/src/tests/nativeaot/SmokeTests/Preinitialization/Preinitialization.cs index e87d6f44f4673..6f16508f216e0 100644 --- a/src/tests/nativeaot/SmokeTests/Preinitialization/Preinitialization.cs +++ b/src/tests/nativeaot/SmokeTests/Preinitialization/Preinitialization.cs @@ -986,9 +986,9 @@ public static void Run() } { - // Expecting this to be a frozen array, and reported as Gen2 by the GC + // Expecting this to be a frozen array, and reported as int.MaxValue by the GC object val = AccessArray(); - Assert.AreEqual(2, GC.GetGeneration(val)); + Assert.AreEqual(int.MaxValue, GC.GetGeneration(val)); val = typeof(ClassWithTemplate<>).MakeGenericType(typeof(C4)).GetField("Array").GetValue(null); Assert.AreEqual(0, GC.GetGeneration(val)); diff --git a/src/tests/profiler/gc/nongcheap.cs b/src/tests/profiler/gc/nongcheap.cs index 8bdf447f88506..c2d42550dbf3e 100644 --- a/src/tests/profiler/gc/nongcheap.cs +++ b/src/tests/profiler/gc/nongcheap.cs @@ -22,6 +22,15 @@ static void AllocateNonGcHeapObjects() Consume("string4"); Consume("string5"); Consume("string6"); + + int gen = GC.GetGeneration("string7"); + if (gen != int.MaxValue) + throw new Exception("object is expected to be in a non-gc heaps"); + + GC.Collect(gen); + GC.Collect(gen, GCCollectionMode.Aggressive); + GC.Collect(gen, GCCollectionMode.Optimized, true); + GC.Collect(gen, GCCollectionMode.Forced, true, true); } [MethodImpl(MethodImplOptions.NoInlining)] diff --git a/src/tests/profiler/native/nongcheap/nongcheap.cpp b/src/tests/profiler/native/nongcheap/nongcheap.cpp index efb81e8d54564..7689f9dda5f4b 100644 --- a/src/tests/profiler/native/nongcheap/nongcheap.cpp +++ b/src/tests/profiler/native/nongcheap/nongcheap.cpp @@ -32,7 +32,7 @@ HRESULT STDMETHODCALLTYPE NonGcHeapProfiler::ObjectAllocated(ObjectID objectId, HRESULT hr = pCorProfilerInfo->GetObjectGeneration(objectId, &gen); // non-GC objects (same for GC.GetGeneration() API) have generation = -1 - if (gen.generation == (COR_PRF_GC_GENERATION)-1) + if (gen.generation == (COR_PRF_GC_GENERATION)INT32_MAX) { if (!FAILED(hr)) { From 0e422ab9b0c8642cf536ba605494860c81f60860 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Wed, 19 Apr 2023 20:10:10 +0200 Subject: [PATCH 07/13] revert unrelated change --- eng/native/configurecompiler.cmake | 1 - 1 file changed, 1 deletion(-) diff --git a/eng/native/configurecompiler.cmake b/eng/native/configurecompiler.cmake index f62eaa53fa68a..0282e29f069ee 100644 --- a/eng/native/configurecompiler.cmake +++ b/eng/native/configurecompiler.cmake @@ -87,7 +87,6 @@ if (MSVC) # Checked build specific flags add_linker_flag(/INCREMENTAL:NO CHECKED) # prevent "warning LNK4075: ignoring '/INCREMENTAL' due to '/OPT:REF' specification" - add_linker_flag(/INCREMENTAL:NO DEBUG) # prevent "warning LNK4075: ignoring '/INCREMENTAL' due to '/OPT:REF' specification" add_linker_flag(/OPT:REF CHECKED) add_linker_flag(/OPT:NOICF CHECKED) From 330f22a62e51335b40082258d64c5be74394d105 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Wed, 19 Apr 2023 22:05:17 +0200 Subject: [PATCH 08/13] oops --- src/coreclr/vm/proftoeeinterfaceimpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/proftoeeinterfaceimpl.cpp b/src/coreclr/vm/proftoeeinterfaceimpl.cpp index 88b32052e029a..0e9e39a91fd18 100644 --- a/src/coreclr/vm/proftoeeinterfaceimpl.cpp +++ b/src/coreclr/vm/proftoeeinterfaceimpl.cpp @@ -9143,7 +9143,7 @@ HRESULT ProfToEEInterfaceImpl::GetObjectGeneration(ObjectID objectId, if (hp->IsInFrozenSegment((Object*)objectId)) { - range->generation = (COR_PRF_GC_GENERATION)-1; + range->generation = (COR_PRF_GC_GENERATION)INT32_MAX; range->rangeStart = 0; range->rangeLength = 0; range->rangeLengthReserved = 0; From d63b65228df6ff35e6f4dd7598cea2315dc31651 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Wed, 19 Apr 2023 23:56:39 +0200 Subject: [PATCH 09/13] Update nongcheap.cs --- src/tests/profiler/gc/nongcheap.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tests/profiler/gc/nongcheap.cs b/src/tests/profiler/gc/nongcheap.cs index c2d42550dbf3e..b93a4de0da01a 100644 --- a/src/tests/profiler/gc/nongcheap.cs +++ b/src/tests/profiler/gc/nongcheap.cs @@ -28,7 +28,6 @@ static void AllocateNonGcHeapObjects() throw new Exception("object is expected to be in a non-gc heaps"); GC.Collect(gen); - GC.Collect(gen, GCCollectionMode.Aggressive); GC.Collect(gen, GCCollectionMode.Optimized, true); GC.Collect(gen, GCCollectionMode.Forced, true, true); } From 2135544ad88b8260912be0cc621b0a9634b5f0a6 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 21 Apr 2023 14:16:25 +0200 Subject: [PATCH 10/13] Address feedback --- src/libraries/System.Runtime/tests/System/GCTests.cs | 3 +++ src/tests/profiler/gc/nongcheap.cs | 6 +----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Runtime/tests/System/GCTests.cs b/src/libraries/System.Runtime/tests/System/GCTests.cs index d6d7488c1fc09..e59aea9ccdd1f 100644 --- a/src/libraries/System.Runtime/tests/System/GCTests.cs +++ b/src/libraries/System.Runtime/tests/System/GCTests.cs @@ -35,6 +35,9 @@ public static void Collect_Int() { GC.Collect(i); } + // Also, expect GC.Collect(int.MaxValue) to work without exception since int.MaxValue represents + // a nongc heap generation (that is exactly what GC.GetGeneration returns for a non-gc heap object) + GC.Collect(int.MaxValue); } [Fact] diff --git a/src/tests/profiler/gc/nongcheap.cs b/src/tests/profiler/gc/nongcheap.cs index b93a4de0da01a..e81925a76e9e7 100644 --- a/src/tests/profiler/gc/nongcheap.cs +++ b/src/tests/profiler/gc/nongcheap.cs @@ -25,11 +25,7 @@ static void AllocateNonGcHeapObjects() int gen = GC.GetGeneration("string7"); if (gen != int.MaxValue) - throw new Exception("object is expected to be in a non-gc heaps"); - - GC.Collect(gen); - GC.Collect(gen, GCCollectionMode.Optimized, true); - GC.Collect(gen, GCCollectionMode.Forced, true, true); + throw new Exception("object is expected to be in a non-gc heap for this test to work"); } [MethodImpl(MethodImplOptions.NoInlining)] From b50f36ad6e23566a297735b4c9d691205698c767 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 21 Apr 2023 15:17:40 +0200 Subject: [PATCH 11/13] Add asserts --- src/coreclr/gc/gc.cpp | 3 +++ src/coreclr/gc/gcimpl.h | 3 ++- src/coreclr/gc/gcinterface.h | 1 + src/coreclr/vm/threads.cpp | 1 + 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index a944a0541dea5..0426326915714 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -48665,6 +48665,9 @@ CFinalize::UpdatePromotedGenerations (int gen, BOOL gen_0_empty_p) int new_gen = g_theGCHeap->WhichGeneration (*po); if (new_gen != i) { + // We never promote objects to a non-GC heap + assert (new_gen <= max_generation); + dprintf (3, ("Moving object %p->%p from gen %d to gen %d", po, *po, i, new_gen)); if (new_gen > i) diff --git a/src/coreclr/gc/gcimpl.h b/src/coreclr/gc/gcimpl.h index b82f17a78f959..4a4c92cb50ede 100644 --- a/src/coreclr/gc/gcimpl.h +++ b/src/coreclr/gc/gcimpl.h @@ -150,7 +150,8 @@ class GCHeap : public IGCHeapInternal //Unregister an object for finalization void SetFinalizationRun (Object* obj); - //returns the generation number of an object (not valid during relocation) + // returns the generation number of an object (not valid during relocation) or + // INT32_MAX if the object belongs to a non-GC heap. unsigned WhichGeneration (Object* object); // returns TRUE is the object is ephemeral bool IsEphemeral (Object* object); diff --git a/src/coreclr/gc/gcinterface.h b/src/coreclr/gc/gcinterface.h index 945629d02f712..a6a3177acb090 100644 --- a/src/coreclr/gc/gcinterface.h +++ b/src/coreclr/gc/gcinterface.h @@ -722,6 +722,7 @@ class IGCHeap { // Returns the generation in which obj is found. Also used by the VM // in some places, in particular syncblk code. + // Returns INT32_MAX if obj belongs to a non-GC heap. virtual unsigned WhichGeneration(Object* obj) PURE_VIRTUAL // Returns the number of GCs that have transpired in the given generation diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index 2dd6b62159972..76b133b0d1248 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -5533,6 +5533,7 @@ void ThreadStore::TriggerGCForDeadThreadsIfNecessary() } unsigned exposedObjectGeneration = gcHeap->WhichGeneration(exposedObject); + _ASSERTE(exposedObjectGeneration != INT32_MAX); SIZE_T newDeadThreadGenerationCount = ++s_DeadThreadGenerationCounts[exposedObjectGeneration]; if (exposedObjectGeneration > gcGenerationToTrigger && newDeadThreadGenerationCount >= generationCountThreshold) { From 2ce158898ab99598cee0bc6ee8915d22b14f1f34 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Sun, 23 Apr 2023 02:38:35 +0200 Subject: [PATCH 12/13] Don't run the test with crossgen --- src/tests/profiler/gc/nongcheap.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tests/profiler/gc/nongcheap.csproj b/src/tests/profiler/gc/nongcheap.csproj index d51dcb692abfe..d59b11eaf4a53 100644 --- a/src/tests/profiler/gc/nongcheap.csproj +++ b/src/tests/profiler/gc/nongcheap.csproj @@ -11,6 +11,8 @@ runincontext loads even framework assemblies into the unloadable context, locals in this loop prevent unloading --> true + + false From fdcfbdd9e9fb9812e4855d844e3b25e737e00801 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Sun, 23 Apr 2023 12:00:29 +0200 Subject: [PATCH 13/13] Update nongcheap.csproj --- src/tests/profiler/gc/nongcheap.csproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/tests/profiler/gc/nongcheap.csproj b/src/tests/profiler/gc/nongcheap.csproj index d59b11eaf4a53..d51dcb692abfe 100644 --- a/src/tests/profiler/gc/nongcheap.csproj +++ b/src/tests/profiler/gc/nongcheap.csproj @@ -11,8 +11,6 @@ runincontext loads even framework assemblies into the unloadable context, locals in this loop prevent unloading --> true - - false