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

Add support for a Recycler-managed "Host Heap" to facilitate tracing of objects which relate to scriptable types #3846

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
76935ab
Merged PR 873340: Add support for a RecyclerVisitedHostHeapBlock
dlibby- Sep 13, 2017
d4e7e08
Fix build error due to bad search/replace during patch application
dlibby- Sep 28, 2017
2c06284
Add RecyclerVisitedObject interface and publish it
Oct 12, 2017
7ddb2a6
Merged PR 877160: Address code review feedback for new heap block type
dlibby- Sep 14, 2017
245aa3d
Merged PR 881772: Specialize ProcessMarkedObject for SmallRecyclerVis…
dlibby- Sep 18, 2017
4cc0027
Create exports for allocation and addref/release functions
dlibby- Sep 28, 2017
7a36147
Merged PR 895883: commit 5ef48746: Change the location of the caller …
dlibby- Sep 20, 2017
e4ae746
Forward declare Recycler in RecyclerVisitedObject.h
Sep 21, 2017
d9a1061
Merged PR 902922: Get Object #1 (WebGLActiveInfo) onto the chakra hea…
dlibby- Sep 22, 2017
3ebbed9
Change signature of IRecyclerVisitedObject::Trace to not return a boo…
dlibby- Sep 22, 2017
6f80abb
Add failfast for RecyclerVisitedObjects that are placed in the LargeH…
dlibby- Sep 25, 2017
2705f24
Merged PR 913868: Add support for RecyclerVisitedObjects in GCStress
dlibby- Sep 25, 2017
b791b59
Clean up TODO's Remove TODO's in HeapInfo.h, we're just following the…
dlibby- Sep 25, 2017
8f96d7d
Move RecyclerNative leaf objects to the pre-existing leaf heap bucket…
dlibby- Sep 25, 2017
38d7ac6
Fixup for incorrect manual merge
dlibby- Sep 28, 2017
a09bf15
Introduce #define to potentially not build recycler visited heap supp…
dlibby- Sep 28, 2017
bac5b27
Fix bad merge in ProcessMark for ARM builds
dlibby- Sep 28, 2017
4022579
Fix casing for relatively included header to fix Linux builds
dlibby- Sep 28, 2017
da7f090
Rewrite test object code to avoid prefast error
Oct 2, 2017
d2cf5dc
Enable Recycler Host Heap on Windows builds only
Oct 2, 2017
11d70bc
Convert tabs to spaces per style requirements
Oct 2, 2017
ebbb2e0
Address prefast errors triggered in regex code
Oct 2, 2017
44ecceb
Add another AnalysisAssert for the benefit of prefast checking
Oct 2, 2017
77186c3
Suppress incorrect prefast warning for helpers.cpp
Oct 2, 2017
f29659e
Update comment to fix typo
Oct 2, 2017
3f13bc1
Remove recyclerVisitedHostHeapBucket from implicit root scanning - co…
Oct 4, 2017
3dd0dcc
Conditionally compile in MarkContext::preciseStack only for RECYCLER_…
Oct 4, 2017
11f6196
Remove unused static const bool IsLeafOnly from blocks
Oct 5, 2017
c1774aa
Clarify the intent of an assert in Recycler::AddPreciselyTracedMark
Oct 5, 2017
c4faba8
Pre-process out more precise tracing code when RECYCLER_VISITED_HOST …
Oct 5, 2017
5356fa6
Eliminate more interaction between implicit root bit and recycler vis…
Oct 5, 2017
2d0adbc
Fix case sensitivity issue for including RecyclerHeapMarkingContext.h…
Oct 11, 2017
30fcaf2
Asserting the incorrect UpdateAttributesOfMarkedOjbects is not called…
Oct 12, 2017
2c6a7da
Fix OSX build with RECYCLER_VISITED_HOST ifdef check
Oct 12, 2017
6e782df
Add a comment on the overloaded use of the TrackBit
Oct 12, 2017
612cd34
Remove remaining __stdcall calling convention from IRecyclerVisitedOb…
Oct 13, 2017
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
7 changes: 7 additions & 0 deletions bin/GCStress/GCStress.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,16 +214,23 @@ void BuildObjectCreationTable()
objectCreationTable.AddWeightedEntry(&ScannedObject<1, 50>::New, 10000);
objectCreationTable.AddWeightedEntry(&BarrierObject<1, 50>::New, 2000);
objectCreationTable.AddWeightedEntry(&TrackedObject<1, 50>::New, 2000);
#ifdef RECYCLER_VISITED_HOST
objectCreationTable.AddWeightedEntry(&RecyclerVisitedObject<1, 50>::New, 2000);
#endif

objectCreationTable.AddWeightedEntry(&LeafObject<51, 1000>::New, 10);
objectCreationTable.AddWeightedEntry(&ScannedObject<51, 1000>::New, 100);
objectCreationTable.AddWeightedEntry(&BarrierObject<51, 1000>::New, 20);
objectCreationTable.AddWeightedEntry(&TrackedObject<51, 1000>::New, 20);
#ifdef RECYCLER_VISITED_HOST
objectCreationTable.AddWeightedEntry(&RecyclerVisitedObject<51, 1000>::New, 40);
#endif

objectCreationTable.AddWeightedEntry(&LeafObject<1001, 50000>::New, 1);
objectCreationTable.AddWeightedEntry(&ScannedObject<1001, 50000>::New, 10);
objectCreationTable.AddWeightedEntry(&BarrierObject<1001, 50000>::New, 2);
// objectCreationTable.AddWeightedEntry(&TrackedObject<1001, 50000>::New, 2); // Large tracked objects are not supported
// objectCreationTable.AddWeightedEntry(&RecyclerVisitedObject<1001, 50000>::New, 2); // Large recycler visited objects are not supported
}

void BuildOperationTable()
Expand Down
1 change: 1 addition & 0 deletions bin/GCStress/RecyclerTestObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ size_t RecyclerTestObject::walkObjectCount = 0;
size_t RecyclerTestObject::walkScannedByteCount = 0;
size_t RecyclerTestObject::walkBarrierByteCount = 0;
size_t RecyclerTestObject::walkTrackedByteCount = 0;
size_t RecyclerTestObject::walkRecyclerVisitedByteCount = 0;
size_t RecyclerTestObject::walkLeafByteCount = 0;
size_t RecyclerTestObject::currentWalkDepth = 0;
size_t RecyclerTestObject::maxWalkDepth = 0;
Expand Down
158 changes: 147 additions & 11 deletions bin/GCStress/RecyclerTestObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
//-------------------------------------------------------------------------------------------------------
#include "stdafx.h"

class RecyclerTestObject : public FinalizableObject
#include "Core/RecyclerHeapMarkingContext.h"

class RecyclerTestObject : public IRecyclerVisitedObject
{
protected:
RecyclerTestObject()
Expand All @@ -13,10 +15,16 @@ class RecyclerTestObject : public FinalizableObject
}

public:
// FinalizableObject implementation
// 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 Mark(Recycler * recycler) 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()
Expand All @@ -27,6 +35,7 @@ class RecyclerTestObject : public FinalizableObject
walkScannedByteCount = 0;
walkBarrierByteCount = 0;
walkTrackedByteCount = 0;
walkRecyclerVisitedByteCount = 0;
walkLeafByteCount = 0;
maxWalkDepth = 0;

Expand Down Expand Up @@ -66,13 +75,14 @@ class RecyclerTestObject : public FinalizableObject
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("Leaf Bytes: %12llu\n"), (unsigned long long) walkLeafByteCount);
wprintf(_u("Total Bytes: %12llu\n"), (unsigned long long) (walkScannedByteCount + walkBarrierByteCount + walkTrackedByteCount + walkLeafByteCount));
wprintf(_u("Max Depth: %12llu\n"), (unsigned long long) maxWalkDepth);
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("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 + walkLeafByteCount + walkRecyclerVisitedByteCount));
wprintf(_u("Max Depth: %12llu\n"), (unsigned long long) maxWalkDepth);
}

// Virtual methods
Expand Down Expand Up @@ -100,6 +110,7 @@ class RecyclerTestObject : public FinalizableObject
static size_t walkLeafByteCount;
static size_t walkBarrierByteCount;
static size_t walkTrackedByteCount;
static size_t walkRecyclerVisitedByteCount;
static size_t currentWalkDepth;
static size_t maxWalkDepth;

Expand Down Expand Up @@ -232,8 +243,13 @@ class BarrierObject : public RecyclerTestObject
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
class TrackedObject : public RecyclerTestObject, public FinalizableObject
{
private:
TrackedObject(unsigned int count) :
Expand Down Expand Up @@ -295,4 +311,124 @@ class TrackedObject : public RecyclerTestObject
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 = RecyclerAllocVisitedHostTracedAndFinalizedZero(recyclerInstance, size);
break;
case AllocationType::TraceOnly:
mem = RecyclerAllocVisitedHostTracedZero(recyclerInstance, size);
break;
case AllocationType::FinalizeLeaf:
mem = RecyclerAllocVisitedHostFinalizedZero(recyclerInstance, size);
break;
default:
Assert(allocType == AllocationType::Leaf);
mem = RecyclerAllocLeafZero(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
4 changes: 4 additions & 0 deletions bin/ch/Helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,9 @@ HRESULT Helpers::LoadScriptFromFile(LPCSTR filename, LPCSTR& contents, UINT* len
// wrongly classified as ANSI
//
{
#pragma warning(push)
// suppressing prefast warning that "readable size is bufferLength bytes but 2 may be read" as bufferLength is clearly > 2 in the code that follows
#pragma warning(disable:6385)
C_ASSERT(sizeof(WCHAR) == 2);
if (bufferLength > 2)
{
Expand All @@ -211,6 +214,7 @@ HRESULT Helpers::LoadScriptFromFile(LPCSTR filename, LPCSTR& contents, UINT* len
#pragma prefast(pop)
}
}
#pragma warning(pop)
}

contents = reinterpret_cast<LPCSTR>(pRawBytes);
Expand Down
4 changes: 4 additions & 0 deletions lib/Common/CommonDefines.h
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,10 @@
#error "Background page zeroing can't be turned on if freeing pages in the background is disabled"
#endif

#ifdef _WIN32
#define RECYCLER_VISITED_HOST
#endif

// JIT features

#if DISABLE_JIT
Expand Down
1 change: 1 addition & 0 deletions lib/Common/CommonMinMemory.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,6 @@ class FinalizableObject;
#include "Memory/RecyclerSweep.h"
#include "Memory/RecyclerHeuristic.h"
#include "Memory/MarkContext.h"
#include "Memory/MarkContextWrapper.h"
#include "Memory/RecyclerWatsonTelemetry.h"
#include "Memory/Recycler.h"
28 changes: 13 additions & 15 deletions lib/Common/Core/FinalizableObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,22 @@
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#pragma once
class FinalizableObject
#include "RecyclerVisitedObject.h"

class FinalizableObject : public IRecyclerVisitedObject
{
public:
// Called right after finish marking and this object is determined to be dead.
// Should contain only simple clean up code.
// Can't run another script
// Can't cause a re-entrant collection

virtual void Finalize(bool isShutdown) = 0;

// Call after sweeping is done.
// Can call other script or cause another collection.
virtual void OnMark() {}

virtual void Dispose(bool isShutdown) = 0;
void Mark(RecyclerHeapHandle recycler) final
{
Mark(static_cast<Recycler*>(recycler));
}

// Used only by TrackableObjects (created with TrackedBit on by RecyclerNew*Tracked)
virtual void Mark(Recycler * recycler) = 0;
void Trace(IRecyclerHeapMarkingContext* markingContext) final
{
AssertMsg(false, "Trace called on object that isn't implemented by the host");
}

// Special behavior on certain GC's
virtual void OnMark() {}
virtual void Mark(Recycler* recycler) = 0;
};
11 changes: 11 additions & 0 deletions lib/Common/Core/RecyclerHeapMarkingContext.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#pragma once

interface IRecyclerHeapMarkingContext
{
virtual void MarkObjects(void** objects, size_t count, void* parent) = 0;
};

31 changes: 31 additions & 0 deletions lib/Common/Core/RecyclerVisitedObject.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#pragma once

interface IRecyclerHeapMarkingContext;
typedef void* RecyclerHeapHandle;

interface IRecyclerVisitedObject
{
// Called right after finish marking and this object is determined to be dead.
// Should contain only simple clean up code.
// Can't run another script
// Can't cause a re-entrant collection
virtual void Finalize(bool isShutdown) = 0;

// Call after sweeping is done.
// Can call other script or cause another collection.
virtual void Dispose(bool isShutdown) = 0;

// Used only by TrackableObjects (created with TrackedBit on by RecyclerNew*Tracked)
virtual void Mark(RecyclerHeapHandle recycler) = 0;

// Special behavior on certain GC's
virtual void OnMark() = 0;

// Used only by RecyclerVisitedHost objects (created with RecyclerAllocVistedHost_Traced*)
virtual void Trace(IRecyclerHeapMarkingContext* markingContext) = 0;
};

1 change: 1 addition & 0 deletions lib/Common/Exceptions/ReportError.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ enum ErrorReason
Fatal_JsReentrancy_Error = 19,
Fatal_TTDAbort = 20,
Fatal_Failed_API_Result = 21,
Fatal_RecyclerVisitedHost_LargeHeapBlock = 22,
};

extern "C" void ReportFatalException(
Expand Down
10 changes: 10 additions & 0 deletions lib/Common/Memory/HeapBlock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ HeapBlock::AsFinalizableBlock()
return static_cast<SmallFinalizableHeapBlockT<TBlockAttributes> *>(this);
}

#ifdef RECYCLER_VISITED_HOST
template <typename TBlockAttributes>
SmallRecyclerVisitedHostHeapBlockT<TBlockAttributes> *
HeapBlock::AsRecyclerVisitedHostBlock()
{
Assert(IsRecyclerVisitedHostBlock());
return static_cast<SmallRecyclerVisitedHostHeapBlockT<TBlockAttributes> *>(this);
}
#endif

#ifdef RECYCLER_WRITE_BARRIER
template <typename TBlockAttributes>
SmallNormalWithBarrierHeapBlockT<TBlockAttributes> *
Expand Down
Loading