diff options
author | dotnet-bot <dotnet-bot@microsoft.com> | 2015-10-01 00:47:24 +0300 |
---|---|---|
committer | Scott Mosier <smosier@microsoft.com> | 2015-10-01 00:47:24 +0300 |
commit | ad0323ab91a7b1469b42ca5457ddd631b94294fe (patch) | |
tree | 88fae57e1ec3aae90288463dc07e58f7aebc1de8 /src/Native/Runtime/profheapwalkhelper.cpp | |
parent | 6763d16387778f126ec510c0421783952602f8f7 (diff) |
Initial population of CoreRT Runtime files.
Diffstat (limited to 'src/Native/Runtime/profheapwalkhelper.cpp')
-rw-r--r-- | src/Native/Runtime/profheapwalkhelper.cpp | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/src/Native/Runtime/profheapwalkhelper.cpp b/src/Native/Runtime/profheapwalkhelper.cpp new file mode 100644 index 000000000..cc6986028 --- /dev/null +++ b/src/Native/Runtime/profheapwalkhelper.cpp @@ -0,0 +1,216 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +// +// On desktop CLR, GC ETW event firing borrows heavily from code in the profiling API, +// as the GC already called hooks in the profapi to notify it of roots & references. +// This file shims up that profapi code the GC expects, though only for the purpose of +// firing ETW events (not for getting a full profapi up on redhawk). +// + +#if defined(FEATURE_EVENT_TRACE) + +#include "commontypes.h" +#include "daccess.h" +#include "debugmacrosext.h" +#include "palredhawkcommon.h" +#include "gcrhenv.h" + + +//--------------------------------------------------------------------------------------- +// +// Callback of type promote_func called by GC while scanning roots (in GCProfileWalkHeap, +// called after the collection). Wrapper around EEToProfInterfaceImpl::RootReference2, +// which does the real work. +// +// Arguments: +// ppObject - Object reference encountered +/// ppObjectRef - Address that references ppObject +// pSC - ProfilingScanContext * containing the root kind and GCReferencesData used +// by RootReference2 +// dwFlags - Properties of the root as GC_CALL* constants (this function converts +// to COR_PRF_GC_ROOT_FLAGS. +// + +void ScanRootsHelper(Object** ppObject, Object** ppObjectRef, ScanContext *pSC, DWORD dwFlags) +{ + ProfilingScanContext *pPSC = (ProfilingScanContext *)pSC; + + DWORD dwEtwRootFlags = 0; + if (dwFlags & GC_CALL_INTERIOR) + dwEtwRootFlags |= kEtwGCRootFlagsInterior; + if (dwFlags & GC_CALL_PINNED) + dwEtwRootFlags |= kEtwGCRootFlagsPinning; + + void *rootID = ppObjectRef; + + if (pPSC->dwEtwRootKind == kEtwGCRootKindFinalizer) + ppObject = ppObjectRef; + + // Notify ETW of the root + + if (ETW::GCLog::ShouldWalkHeapRootsForEtw()) + { + ETW::GCLog::RootReference( + rootID, // root address + *ppObject, // object being rooted + NULL, // pSecondaryNodeForDependentHandle is NULL, cuz this isn't a dependent handle + FALSE, // is dependent handle + pPSC, + dwFlags, // dwGCFlags + dwEtwRootFlags); + } +} + +//--------------------------------------------------------------------------------------- +// +// Callback of type walk_fn used by GCHeap::WalkObject. Keeps a count of each +// object reference found. +// +// Arguments: +// pBO - Object reference encountered in walk +// context - running count of object references encountered +// +// Return Value: +// Always returns TRUE to object walker so it walks the entire object +// + +BOOL CountContainedObjectRef(Object * pBO, void * context) +{ + LIMITED_METHOD_CONTRACT; + // Increase the count + (*((size_t *)context))++; + + return TRUE; +} + +//--------------------------------------------------------------------------------------- +// +// Callback of type walk_fn used by GCHeap::WalkObject. Stores each object reference +// encountered into an array. +// +// Arguments: +// pBO - Object reference encountered in walk +// context - Array of locations within the walked object that point to other +// objects. On entry, (*context) points to the next unfilled array +// entry. On exit, that location is filled, and (*context) is incremented +// to point to the next entry. +// +// Return Value: +// Always returns TRUE to object walker so it walks the entire object +// + +BOOL SaveContainedObjectRef(Object * pBO, void * context) +{ + LIMITED_METHOD_CONTRACT; + // Assign the value + **((Object ***)context) = pBO; + + // Now increment the array pointer + // + // Note that HeapWalkHelper has already walked the references once to count them up, + // and then allocated an array big enough to hold those references. First time this + // callback is called for a given object, (*context) points to the first entry in the + // array. So "blindly" incrementing (*context) here and using it next time around + // for the next reference, over and over again, should be safe. + (*((Object ***)context))++; + + return TRUE; +} + +//--------------------------------------------------------------------------------------- +// +// Callback of type walk_fn used by the GC when walking the heap, to help profapi +// track objects. This guy orchestrates the use of the above callbacks which dig +// into object references contained each object encountered by this callback. +// +// Arguments: +// pBO - Object reference encountered on the heap +// +// Return Value: +// BOOL indicating whether the heap walk should continue. +// TRUE=continue +// FALSE=stop +// + +BOOL HeapWalkHelper(Object * pBO, void * pvContext) +{ + OBJECTREF * arrObjRef = NULL; + size_t cNumRefs = 0; + bool bOnStack = false; + //MethodTable * pMT = pBO->GetMethodTable(); + + ProfilerWalkHeapContext * pProfilerWalkHeapContext = (ProfilerWalkHeapContext *) pvContext; + + //if (pMT->ContainsPointersOrCollectible()) + { + // First round through calculates the number of object refs for this class + GCHeap::GetGCHeap()->WalkObject(pBO, &CountContainedObjectRef, (void *)&cNumRefs); + + if (cNumRefs > 0) + { + // Create an array to contain all of the refs for this object + bOnStack = cNumRefs <= 32 ? true : false; + + if (bOnStack) + { + // It's small enough, so just allocate on the stack + arrObjRef = (OBJECTREF *)_alloca(cNumRefs * sizeof(OBJECTREF)); + } + else + { + // Otherwise, allocate from the heap + arrObjRef = new (nothrow) OBJECTREF[cNumRefs]; + + if (!arrObjRef) + { + return FALSE; + } + } + + // Second round saves off all of the ref values + OBJECTREF * pCurObjRef = arrObjRef; + GCHeap::GetGCHeap()->WalkObject(pBO, &SaveContainedObjectRef, (void *)&pCurObjRef); + } + } + + HRESULT hr = E_FAIL; + +#ifdef FEATURE_ETW + if (ETW::GCLog::ShouldWalkHeapObjectsForEtw()) + { + ETW::GCLog::ObjectReference( + pProfilerWalkHeapContext, + pBO, + ULONGLONG(pBO->get_SafeEEType()), + cNumRefs, + (Object **) arrObjRef); + } +#endif // FEATURE_ETW + + // If the data was not allocated on the stack, need to clean it up. + if ((arrObjRef != NULL) && !bOnStack) + { + delete [] arrObjRef; + } + + // Return TRUE iff we want to the heap walk to continue. The only way we'd abort the + // heap walk is if we're issuing profapi callbacks, and the profapi profiler + // intentionally returned a failed HR (as its request that we stop the walk). There's + // a potential conflict here. If a profapi profiler and an ETW profiler are both + // monitoring the heap dump, and the profapi profiler requests to abort the walk (but + // the ETW profiler may not want to abort the walk), then what do we do? The profapi + // profiler gets precedence. We don't want to accidentally send more callbacks to a + // profapi profiler that explicitly requested an abort. The ETW profiler will just + // have to deal. In theory, I could make the code more complex by remembering that a + // profapi profiler requested to abort the dump but an ETW profiler is still + // attached, and then intentionally inhibit the remainder of the profapi callbacks + // for this GC. But that's unnecessary complexity. In practice, it should be + // extremely rare that a profapi profiler is monitoring heap dumps AND an ETW + // profiler is also monitoring heap dumps. + return TRUE; +} + +#endif // defined(FEATURE_EVENT_TRACE) |