| //------------------------------------------------------------------------------------------------------- |
| // Copyright (C) Microsoft. All rights reserved. |
| // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. |
| //------------------------------------------------------------------------------------------------------- |
| #include "stdafx.h" |
| |
| #include "Core/RecyclerHeapMarkingContext.h" |
| |
| class RecyclerTestObject : public IRecyclerVisitedObject |
| { |
| protected: |
| RecyclerTestObject() |
| { |
| generation = currentGeneration; |
| } |
| |
| public: |
| // IRecyclerVisitedObject implementation. We don't use FinalizableObject here as |
| // RecyclerVisitedObjects need to have Trace called on them, which is not allowed for |
| // FinalizableObject. |
| virtual void Finalize(bool isShutdown) override { VerifyCondition(false); }; |
| virtual void Dispose(bool isShutdown) override { VerifyCondition(false); }; |
| virtual void OnMark() override {} |
| virtual void Mark(RecyclerHeapHandle recycler) override { Mark(static_cast<Recycler*>(recycler)); }; |
| virtual void Trace(IRecyclerHeapMarkingContext* markContext) override { VerifyCondition(false); }; |
| |
| virtual void Mark(Recycler * recycler) { VerifyCondition(false); }; |
| |
| public: |
| static void BeginWalk() |
| { |
| currentGeneration++; |
| |
| walkObjectCount = 0; |
| walkScannedByteCount = 0; |
| walkBarrierByteCount = 0; |
| walkTrackedByteCount = 0; |
| walkFinalizedByteCount = 0; |
| walkRecyclerVisitedByteCount = 0; |
| walkLeafByteCount = 0; |
| maxWalkDepth = 0; |
| |
| currentWalkDepth = 0; |
| |
| wprintf(_u("-------------------------------------------\n")); |
| wprintf(_u("Full heap walk starting. Current generation: %12llu\n"), (unsigned long long) currentGeneration); |
| } |
| |
| static void WalkReference(RecyclerTestObject * object) |
| { |
| if (object != nullptr) |
| { |
| // See if we've already seen the location in this traversal. |
| if (object->generation != currentGeneration) |
| { |
| // Haven't see it yet. Must be from the previous generation; increment generation and validate that. |
| // Update to current generation to indicate we've seen it |
| object->generation++; |
| VerifyCondition(object->generation == currentGeneration); |
| |
| walkObjectCount++; |
| |
| currentWalkDepth++; |
| maxWalkDepth = max(currentWalkDepth, maxWalkDepth); |
| |
| // Call virtual for object-specific behavior |
| object->DoWalkObject(); |
| |
| currentWalkDepth--; |
| } |
| } |
| } |
| |
| static void EndWalk() |
| { |
| VerifyCondition(currentWalkDepth == 0); |
| |
| wprintf(_u("Full heap walk finished\n")); |
| wprintf(_u("Object Count: %12llu\n"), (unsigned long long) walkObjectCount); |
| wprintf(_u("Scanned Bytes: %12llu\n"), (unsigned long long) walkScannedByteCount); |
| wprintf(_u("Barrier Bytes: %12llu\n"), (unsigned long long) walkBarrierByteCount); |
| wprintf(_u("Tracked Bytes: %12llu\n"), (unsigned long long) walkTrackedByteCount); |
| wprintf(_u("Finalized Bytes: %12llu\n"), (unsigned long long) walkFinalizedByteCount); |
| wprintf(_u("RecyclerVisited Bytes: %12llu\n"), (unsigned long long) walkRecyclerVisitedByteCount); |
| wprintf(_u("Leaf Bytes: %12llu\n"), (unsigned long long) walkLeafByteCount); |
| wprintf(_u("Total Bytes: %12llu\n"), (unsigned long long) (walkScannedByteCount + walkBarrierByteCount + walkTrackedByteCount + walkFinalizedByteCount + walkLeafByteCount + walkRecyclerVisitedByteCount)); |
| wprintf(_u("Max Depth: %12llu\n"), (unsigned long long) maxWalkDepth); |
| } |
| |
| // Virtual methods |
| virtual bool TryGetRandomLocation(Location * location) |
| { |
| // Return false to indicate no internal locations |
| // Subclasses can override this to handle their internal locations as appropriate |
| return false; |
| } |
| |
| virtual void DoWalkObject() = 0; |
| |
| protected: |
| // Global variables |
| |
| // This global variable contains the "generation" of GC objects |
| // It is used to validate the correctness of GC objects |
| // It is assigned initially during object creation, and |
| // updated when we walk the entire object graph in TraverseAllObjects |
| static size_t currentGeneration; |
| |
| // These globals contain statistical data generated by WalkAllReferences |
| static size_t walkObjectCount; |
| static size_t walkScannedByteCount; |
| static size_t walkLeafByteCount; |
| static size_t walkBarrierByteCount; |
| static size_t walkTrackedByteCount; |
| static size_t walkFinalizedByteCount; |
| static size_t walkRecyclerVisitedByteCount; |
| static size_t currentWalkDepth; |
| static size_t maxWalkDepth; |
| |
| private: |
| // Instance variables |
| |
| // See comments above re currentGeneration |
| Field(size_t) generation; |
| }; |
| |
| template <unsigned int minCount, unsigned int maxCount> |
| class LeafObject : public RecyclerTestObject |
| { |
| private: |
| LeafObject(unsigned int count) : |
| count(count) |
| { |
| for (unsigned int i = 0; i < count; i++) |
| { |
| data[i] = i; |
| } |
| } |
| |
| public: |
| static RecyclerTestObject * New() |
| { |
| unsigned int count = minCount + GetRandomInteger(maxCount - minCount + 1); |
| |
| return RecyclerNewPlusLeaf(recyclerInstance, sizeof(size_t) * count, LeafObject, count); |
| } |
| |
| protected: |
| virtual void DoWalkObject() override |
| { |
| walkLeafByteCount += sizeof(LeafObject) + count * sizeof(size_t); |
| } |
| |
| private: |
| Field(unsigned int) count; |
| Field(size_t ) data[0]; |
| }; |
| |
| template <unsigned int minCount, unsigned int maxCount> |
| class ScannedObject : public RecyclerTestObject |
| { |
| private: |
| ScannedObject(unsigned int count) : |
| count(count) |
| { |
| for (unsigned int i = 0; i < count; i++) |
| { |
| references[i] = nullptr; |
| } |
| } |
| |
| public: |
| static RecyclerTestObject * New() |
| { |
| unsigned int count = minCount + GetRandomInteger(maxCount - minCount + 1); |
| |
| return RecyclerNewPlus(recyclerInstance, sizeof(RecyclerTestObject *) * count, ScannedObject, count); |
| } |
| |
| virtual bool TryGetRandomLocation(Location * location) override |
| { |
| // Get a random slot and construct a Location for it |
| *location = Location::Scanned(&references[GetRandomInteger(count)]); |
| |
| return true; |
| } |
| |
| protected: |
| virtual void DoWalkObject() override |
| { |
| walkScannedByteCount += sizeof(ScannedObject) + count * sizeof(RecyclerTestObject *); |
| |
| for (unsigned int i = 0; i < count; i++) |
| { |
| RecyclerTestObject::WalkReference(references[i]); |
| } |
| } |
| |
| private: |
| Field(unsigned int) count; |
| FieldNoBarrier(RecyclerTestObject *) references[0]; // SWB-TODO: is this correct? |
| }; |
| |
| template <unsigned int minCount, unsigned int maxCount> |
| class BarrierObject : public RecyclerTestObject |
| { |
| private: |
| BarrierObject(unsigned int count) : |
| count(count) |
| { |
| for (unsigned int i = 0; i < count; i++) |
| { |
| references[i] = nullptr; |
| } |
| } |
| |
| public: |
| static RecyclerTestObject * New() |
| { |
| unsigned int count = minCount + GetRandomInteger(maxCount - minCount + 1); |
| |
| return RecyclerNewWithBarrierPlus(recyclerInstance, sizeof(RecyclerTestObject *) * count, BarrierObject, count); |
| } |
| |
| virtual bool TryGetRandomLocation(Location * location) override |
| { |
| // Get a random slot and construct a Location for it |
| *location = Location::Barrier(&references[GetRandomInteger(count)]); |
| |
| return true; |
| } |
| |
| protected: |
| virtual void DoWalkObject() override |
| { |
| walkBarrierByteCount += sizeof(BarrierObject) + count * sizeof(RecyclerTestObject *); |
| |
| for (unsigned int i = 0; i < count; i++) |
| { |
| RecyclerTestObject::WalkReference(references[i]); |
| } |
| } |
| |
| private: |
| Field(unsigned int) count; |
| FieldNoBarrier(RecyclerTestObject *) references[0]; // SWB-TODO: is this correct? |
| }; |
| |
| // TrackedObject must be a FinalizableObject (in order to be 'new'ed with RecyclerNewTrackedLeafPlusZ) |
| // but it also must be a RecyclerTestObject to participate in GCStress. It must inherit from RecyclerTestObject |
| // first so that the algined pointer is returned from New. |
| // Fortunately, the v-tables for RecyclerTestObject and FinalizableObject line up, so the |
| // IRecyclerVisitedObject/FinalizableObject calls end up in the right place. |
| template <unsigned int minCount, unsigned int maxCount> |
| class TrackedObject : public RecyclerTestObject, public FinalizableObject |
| { |
| private: |
| TrackedObject(unsigned int count) : |
| count(count) |
| { |
| for (unsigned int i = 0; i < count; i++) |
| { |
| references[i] = nullptr; |
| } |
| } |
| |
| public: |
| static RecyclerTestObject * New() |
| { |
| unsigned int count = minCount + GetRandomInteger(maxCount - minCount + 1); |
| |
| return RecyclerNewTrackedLeafPlusZ(recyclerInstance, sizeof(RecyclerTestObject *) * count, TrackedObject, count); |
| } |
| |
| virtual bool TryGetRandomLocation(Location * location) override |
| { |
| // Get a random slot and construct a Location for it |
| *location = Location::Tagged(&references[GetRandomInteger(count)]); |
| |
| return true; |
| } |
| |
| // Tracked object implementation |
| virtual void Mark(Recycler * recycler) override |
| { |
| for (unsigned int i = 0; i < count; i++) |
| { |
| RecyclerTestObject * object = Location::Untag(references[i]); |
| if (object != nullptr) |
| { |
| recycler->TryMarkNonInterior(object); |
| } |
| } |
| }; |
| |
| // Tracked objects are always finalize as well. Just do nothing. |
| virtual void Finalize(bool isShutdown) override { } |
| virtual void Dispose(bool isShutdown) override { }; |
| |
| |
| protected: |
| virtual void DoWalkObject() override |
| { |
| walkTrackedByteCount += sizeof(TrackedObject) + count * sizeof(RecyclerTestObject *); |
| |
| for (unsigned int i = 0; i < count; i++) |
| { |
| RecyclerTestObject::WalkReference(Location::Untag(references[i])); |
| } |
| } |
| |
| private: |
| Field(unsigned int) count; |
| FieldNoBarrier(RecyclerTestObject *) references[0]; // SWB-TODO: is this correct? |
| }; |
| |
| // A type of object that is finalizable, but not traced/tracked so that it can be used to test finalization |
| // for LargeHeapBlock (which currently supports the FinalizeBit, but not TrackBit) |
| template <unsigned int minCount, unsigned int maxCount> |
| class FinalizedObject : public RecyclerTestObject, public FinalizableObject |
| { |
| private: |
| FinalizedObject(unsigned int count) : |
| count(count) |
| { |
| for (unsigned int i = 0; i < count; i++) |
| { |
| references[i] = nullptr; |
| } |
| } |
| |
| public: |
| static RecyclerTestObject * New() |
| { |
| unsigned int count = minCount + GetRandomInteger(maxCount - minCount + 1); |
| |
| return RecyclerNewFinalizedPlus(recyclerInstance, sizeof(RecyclerTestObject *) * count, FinalizedObject, count); |
| } |
| |
| virtual bool TryGetRandomLocation(Location * location) override |
| { |
| // Get a random slot and construct a Location for it |
| *location = Location::Scanned(&references[GetRandomInteger(count)]); |
| |
| return true; |
| } |
| |
| virtual void Mark(Recycler * recycler) override { VerifyCondition(false); }; |
| |
| // Finalize implementation. |
| virtual void Finalize(bool isShutdown) override { } |
| virtual void Dispose(bool isShutdown) override { } |
| |
| |
| protected: |
| virtual void DoWalkObject() override |
| { |
| walkFinalizedByteCount += sizeof(FinalizedObject) + count * sizeof(RecyclerTestObject *); |
| |
| for (unsigned int i = 0; i < count; i++) |
| { |
| RecyclerTestObject::WalkReference(references[i]); |
| } |
| } |
| |
| private: |
| Field(unsigned int) count; |
| FieldNoBarrier(RecyclerTestObject *) references[0]; // SWB-TODO: is this correct? |
| }; |
| |
| #ifdef RECYCLER_VISITED_HOST |
| |
| template <unsigned int minCount, unsigned int maxCount> |
| class RecyclerVisitedObject : public RecyclerTestObject |
| { |
| public: |
| static RecyclerTestObject * New() |
| { |
| // Determine a random amount of RecyclerTestObject* references to influence the size of this object. |
| const unsigned int count = minCount + GetRandomInteger(maxCount - minCount + 1); |
| |
| void* mem = nullptr; |
| const size_t size = sizeof(RecyclerVisitedObject) + (sizeof(RecyclerTestObject*) * count); |
| |
| // Randomly select the type of object to create |
| AllocationType allocType = static_cast<AllocationType>(GetRandomInteger(static_cast<unsigned int>(AllocationType::Count))); |
| switch (allocType) |
| { |
| case AllocationType::TraceAndFinalized: |
| mem = RecyclerAllocVisitedHostTracedAndFinalized(recyclerInstance, size); |
| break; |
| case AllocationType::TraceOnly: |
| mem = RecyclerAllocVisitedHostTraced(recyclerInstance, size); |
| break; |
| case AllocationType::FinalizeLeaf: |
| mem = RecyclerAllocVisitedHostFinalized(recyclerInstance, size); |
| break; |
| default: |
| Assert(allocType == AllocationType::Leaf); |
| mem = RecyclerAllocLeaf(recyclerInstance, size); |
| } |
| |
| // Construct the v-table, allocType, and count information for the new object. |
| RecyclerVisitedObject* obj = new (mem) RecyclerVisitedObject(allocType, count); |
| return obj; |
| } |
| |
| virtual bool TryGetRandomLocation(Location * location) override |
| { |
| // Leaf types should not return a location |
| if (type == AllocationType::Leaf || type == AllocationType::FinalizeLeaf) |
| { |
| return false; |
| } |
| |
| // Get a random slot and construct a Location for it |
| // Make this a Tagged location so that we won't inadvertently keep objects alive |
| // in the case where this object gets put on the wrong mark stack. |
| *location = Location::Tagged(&references[GetRandomInteger(count)]); |
| |
| return true; |
| } |
| |
| virtual void Trace(IRecyclerHeapMarkingContext* markContext) override |
| { |
| VerifyCondition(type == AllocationType::TraceAndFinalized || type == AllocationType::TraceOnly); |
| // Note that the pointers in the references arrary are technically tagged. However, this is ok |
| // as the Mark that we're performing is an interior mark, which gets us to the right object(s). |
| markContext->MarkObjects(reinterpret_cast<void**>(&references[0]), count, this); |
| } |
| |
| virtual void Finalize(bool isShutdown) override |
| { |
| // Only types that request finalization should have Finalize called |
| VerifyCondition(IsFinalizable()); |
| } |
| virtual void Dispose(bool isShutdown) override |
| { |
| // Only types that request finalization should have Finalize called |
| VerifyCondition(IsFinalizable()); |
| VerifyCondition(unmanagedResource != nullptr); |
| BOOL success = ::HeapFree(GetProcessHeap(), 0, unmanagedResource); |
| VerifyCondition(success != FALSE); |
| unmanagedResource = nullptr; |
| } |
| |
| |
| protected: |
| virtual void DoWalkObject() override |
| { |
| walkRecyclerVisitedByteCount += sizeof(RecyclerVisitedObject) + count * sizeof(RecyclerTestObject *); |
| |
| for (unsigned int i = 0; i < count; i++) |
| { |
| RecyclerTestObject::WalkReference(Location::Untag(references[i])); |
| } |
| } |
| |
| private: |
| enum class AllocationType : unsigned int |
| { |
| TraceAndFinalized = 0, |
| TraceOnly, |
| FinalizeLeaf, |
| Leaf, |
| Count, |
| }; |
| |
| bool IsFinalizable() const { return type == AllocationType::TraceAndFinalized || type == AllocationType::FinalizeLeaf; } |
| RecyclerVisitedObject(AllocationType allocType, unsigned int count) : |
| count(count), |
| type(allocType) |
| { |
| for (unsigned int i = 0; i < count; i++) |
| { |
| references[i] = nullptr; |
| } |
| if (IsFinalizable()) |
| { |
| unmanagedResource = ::HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, GetRandomInteger(1024)); |
| VerifyCondition(unmanagedResource != nullptr); |
| } |
| } |
| |
| |
| Field(AllocationType) type; |
| Field(void*) unmanagedResource; |
| Field(unsigned int) count; |
| FieldNoBarrier(RecyclerTestObject *) references[0]; // SWB-TODO: is this correct? (copied from TrackedObject) |
| }; |
| #endif |