380 lines
13 KiB
C#
380 lines
13 KiB
C#
|
//
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//
|
||
|
|
||
|
/*******************************************************************/
|
||
|
/* WARNING */
|
||
|
/* This file should be identical in the Bartok and Singularity */
|
||
|
/* depots. Master copy resides in Bartok Depot. Changes should be */
|
||
|
/* made to Bartok Depot and propagated to Singularity Depot. */
|
||
|
/*******************************************************************/
|
||
|
|
||
|
// #define DEBUG
|
||
|
|
||
|
namespace System.GCs {
|
||
|
|
||
|
using Microsoft.Bartok.Options;
|
||
|
using Microsoft.Bartok.Runtime;
|
||
|
using System.Runtime.InteropServices;
|
||
|
using System.Runtime.CompilerServices;
|
||
|
using System.Threading;
|
||
|
using System.Collections;
|
||
|
|
||
|
[NoCCtor]
|
||
|
[RequiredByBartok]
|
||
|
internal abstract class RCCollector : Collector {
|
||
|
[PreInitRefCounts]
|
||
|
public static void Initialize() {
|
||
|
SegregatedFreeList.Initialize();
|
||
|
}
|
||
|
|
||
|
|
||
|
// This is a compiler intrinsic whose value is controlled by
|
||
|
// /StageControl.RCCollectorVerifyRefCounts.
|
||
|
internal static extern bool VerificationMode {
|
||
|
[Intrinsic]
|
||
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
||
|
get;
|
||
|
}
|
||
|
|
||
|
// This is a compiler intrinsic whose value is controlled by
|
||
|
// /StageControl.RCCollectorVerifyLeakedCycles.
|
||
|
internal static extern bool VerifyLeakedCycles {
|
||
|
[Intrinsic]
|
||
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
||
|
get;
|
||
|
}
|
||
|
|
||
|
// This is a compiler intrinsic whose value is controlled by
|
||
|
// /StageControl.RCCollectorGenerateProfile.
|
||
|
internal static extern bool ProfilingMode {
|
||
|
[Intrinsic]
|
||
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
||
|
get;
|
||
|
}
|
||
|
|
||
|
[MixinConditional("RCGC")]
|
||
|
[Mixin(typeof(PostHeader))]
|
||
|
[StructLayout(LayoutKind.Sequential)]
|
||
|
[RequiredByBartok]
|
||
|
internal struct PostHeaderRCGC {
|
||
|
[CompilerInitField(2)]
|
||
|
internal uint refState;
|
||
|
#if !SINGULARITY
|
||
|
[RequiredByBartok]
|
||
|
#endif
|
||
|
internal VTable vtableObject;
|
||
|
}
|
||
|
|
||
|
[MixinConditional("RCGC")]
|
||
|
[Mixin(typeof(Object))]
|
||
|
internal class RCGCObject : System.Object {
|
||
|
internal new PostHeaderRCGC postHeader;
|
||
|
|
||
|
internal new uint REF_STATE {
|
||
|
[Inline]
|
||
|
[ManualRefCounts]
|
||
|
[MixinOverride]
|
||
|
get {
|
||
|
return this.postHeader.refState;
|
||
|
}
|
||
|
[Inline]
|
||
|
[ManualRefCounts]
|
||
|
[MixinOverride]
|
||
|
set {
|
||
|
this.postHeader.refState = value;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[MixinConditional("RCGCVerification")]
|
||
|
[Mixin(typeof(PreHeader))]
|
||
|
[RequiredByBartok]
|
||
|
internal struct PreHeaderRCGCVerification {
|
||
|
internal UIntPtr backupRefcount;
|
||
|
internal UIntPtr dfsDiscoveryTime;
|
||
|
internal UIntPtr dfsFinishingTime;
|
||
|
}
|
||
|
|
||
|
[MixinConditional("RCGCVerification")]
|
||
|
[Mixin(typeof(Object))]
|
||
|
internal class RCGCVerificationObject : System.Object {
|
||
|
internal new PreHeaderRCGCVerification preHeader;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Every object maintains a 32-bit "reference state" (RS).
|
||
|
* The RS consists of a 29-bit reference count, with the
|
||
|
* remaining bits available for flagging purposes.
|
||
|
*
|
||
|
* In a 32-bit architecture, 4GB of memory is addressable.
|
||
|
* Of this, the upper 2GB is usually reserved for use by
|
||
|
* the system. So given an object size of at least
|
||
|
* 12 bytes (sync block index, RS field and the vtable),
|
||
|
* there can't be more than 2^28 objects. The reference
|
||
|
* count can be more, due to multiple references from an
|
||
|
* object to the same target (think of one large array of
|
||
|
* references), but will be less than 2^29.
|
||
|
*
|
||
|
* Of the remaining three bits, one is reserved to ignore
|
||
|
* reference-counting (RC) operations on objects.
|
||
|
*/
|
||
|
|
||
|
internal struct RSMasks {
|
||
|
internal const uint refCount = 0x1fffffff;
|
||
|
|
||
|
internal const uint countingFlag = 0x80000000;
|
||
|
}
|
||
|
|
||
|
// Used only in profiling mode.
|
||
|
internal struct UpdatePairs {
|
||
|
public int Increments;
|
||
|
public int Decrements;
|
||
|
|
||
|
public static UpdatePairs operator+(UpdatePairs a,
|
||
|
UpdatePairs b) {
|
||
|
a.Increments += b.Increments;
|
||
|
a.Decrements += b.Decrements;
|
||
|
|
||
|
return a;
|
||
|
}
|
||
|
|
||
|
public int Total {
|
||
|
get {
|
||
|
return Increments+Decrements;
|
||
|
}
|
||
|
}
|
||
|
} // Increment and decrement counts of one kind of RC update.
|
||
|
|
||
|
internal struct AcctRecord {
|
||
|
// General RC update, on a "maybe-null" reference.
|
||
|
public UpdatePairs MaybeNull;
|
||
|
// General RC update, but on a non-null reference.
|
||
|
public UpdatePairs NonNull;
|
||
|
|
||
|
public UpdatePairs VtableOperand;
|
||
|
public UpdatePairs RuntimeTypeOperand;
|
||
|
|
||
|
public String MethodName;
|
||
|
|
||
|
public static AcctRecord operator+(AcctRecord a,
|
||
|
AcctRecord b) {
|
||
|
VTable.Assert(Magic.addressOf(a.MethodName) ==
|
||
|
Magic.addressOf(b.MethodName),
|
||
|
@"Magic.addressOf(a.MethodName) ==
|
||
|
Magic.addressOf(b.MethodName)");
|
||
|
|
||
|
a.MaybeNull += b.MaybeNull;
|
||
|
a.NonNull += b.NonNull;
|
||
|
|
||
|
a.VtableOperand += b.VtableOperand;
|
||
|
a.RuntimeTypeOperand += b.RuntimeTypeOperand;
|
||
|
|
||
|
return a;
|
||
|
}
|
||
|
|
||
|
public int CompareTo(AcctRecord rhs) {
|
||
|
int lhsTotal = MaybeNull.Total+NonNull.Total;
|
||
|
int rhsTotal = rhs.MaybeNull.Total+rhs.NonNull.Total;
|
||
|
|
||
|
return lhsTotal < rhsTotal;
|
||
|
}
|
||
|
|
||
|
public void DispCountsHeader() {
|
||
|
VTable.DebugPrint("GIncs\t\tGDecs");
|
||
|
VTable.DebugPrint("\t\tNIncs\t\tNDecs");
|
||
|
VTable.DebugPrint("\t\tV+\t\tV-");
|
||
|
VTable.DebugPrint("\t\tR+\t\tR-");
|
||
|
}
|
||
|
|
||
|
public void DispMethodNameHeader() {
|
||
|
VTable.DebugPrint("\t\tMethod");
|
||
|
}
|
||
|
|
||
|
public void DispCounts() {
|
||
|
VTable.DebugPrint(MaybeNull.Increments);
|
||
|
if (MaybeNull.Increments < 10000000) {
|
||
|
VTable.DebugPrint("\t\t");
|
||
|
} else {
|
||
|
VTable.DebugPrint("\t");
|
||
|
}
|
||
|
VTable.DebugPrint(MaybeNull.Decrements);
|
||
|
if (MaybeNull.Decrements < 10000000) {
|
||
|
VTable.DebugPrint("\t\t");
|
||
|
} else {
|
||
|
VTable.DebugPrint("\t");
|
||
|
}
|
||
|
VTable.DebugPrint(NonNull.Increments);
|
||
|
if (NonNull.Increments < 10000000) {
|
||
|
VTable.DebugPrint("\t\t");
|
||
|
} else {
|
||
|
VTable.DebugPrint("\t");
|
||
|
}
|
||
|
VTable.DebugPrint(NonNull.Decrements);
|
||
|
if (NonNull.Decrements < 10000000) {
|
||
|
VTable.DebugPrint("\t\t");
|
||
|
} else {
|
||
|
VTable.DebugPrint("\t");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void DispMethodName() {
|
||
|
VTable.DebugPrint(MethodName);
|
||
|
}
|
||
|
|
||
|
public void Disp() {
|
||
|
DispCounts();
|
||
|
DispMethodName();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
internal override long TotalMemory {
|
||
|
get {
|
||
|
UIntPtr pageCount = UIntPtr.Zero;
|
||
|
for (UIntPtr i = UIntPtr.Zero;
|
||
|
i < PageTable.pageTableCount; i++) {
|
||
|
if (PageTable.IsMyGcPage(i) &&
|
||
|
PageTable.Type(i) !=
|
||
|
SegregatedFreeList.INIT_PAGE) {
|
||
|
pageCount++;
|
||
|
}
|
||
|
}
|
||
|
return (long)PageTable.RegionSize(pageCount);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
internal override UIntPtr FindObjectAddr(UIntPtr interiorPtr) {
|
||
|
return SegregatedFreeList.Find(interiorPtr);
|
||
|
}
|
||
|
|
||
|
internal override void VisitObjects
|
||
|
(ObjectLayout.ObjectVisitor objVisitor,
|
||
|
UIntPtr lowAddr,
|
||
|
UIntPtr highAddr) {
|
||
|
UIntPtr lowPage = PageTable.Page(lowAddr);
|
||
|
UIntPtr highPage = PageTable.Page(highAddr);
|
||
|
SegregatedFreeList.VisitObjects(lowPage,
|
||
|
highPage,
|
||
|
objVisitor);
|
||
|
}
|
||
|
|
||
|
|
||
|
[NoInline]
|
||
|
[ManualRefCounts]
|
||
|
[RequiredByBartok]
|
||
|
internal static unsafe bool AccumulateRCUpdates(String methodName,
|
||
|
int methodIndex,
|
||
|
uint maxIndex,
|
||
|
AcctRecord rec) {
|
||
|
VTable.Assert(RCCollector.ProfilingMode,
|
||
|
@"RCCollector.ProfilingMode");
|
||
|
|
||
|
// Return if the page table hasn't been set up yet.
|
||
|
if (PageTable.pageTableCount == UIntPtr.Zero) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (methods == null) {
|
||
|
// Allocate up front storage for the accounting records.
|
||
|
//
|
||
|
// This is requisitioned directly from the memory
|
||
|
// manager. Care should be taken to ensure that
|
||
|
// AccumulateRCUpdates does not indirectly call
|
||
|
// methods that may have compiler-inserted RC updates.
|
||
|
VTable vtable =
|
||
|
((RuntimeType)typeof(AcctRecord[])).classVtable;
|
||
|
UIntPtr size =
|
||
|
ObjectLayout.ArraySize(vtable, maxIndex+1);
|
||
|
|
||
|
BumpAllocator profileData =
|
||
|
new BumpAllocator(PageType.NonGC);
|
||
|
UIntPtr profileDataStart =
|
||
|
MemoryManager.AllocateMemory(size);
|
||
|
profileData.SetRange(profileDataStart, size);
|
||
|
PageManager.SetStaticDataPages(profileDataStart, size);
|
||
|
|
||
|
methods =
|
||
|
(AcctRecord[])Allocate(ref profileData, vtable, size);
|
||
|
VTable.Assert(methods != null,
|
||
|
@"methods != null");
|
||
|
|
||
|
*(uint*)(Magic.addressOf(methods)+
|
||
|
PostHeader.Size) = maxIndex+1;
|
||
|
}
|
||
|
|
||
|
VTable.Assert(methods.Length == maxIndex+1,
|
||
|
@"methods.Length == maxIndex+1");
|
||
|
|
||
|
if (methods[methodIndex].methodName == null) {
|
||
|
methodNames[methodIndex].methodName = methodName;
|
||
|
}
|
||
|
// Not "methodNames[methodIndex].methodName == methodName"
|
||
|
// because the Equality operator carries compiler-inserted
|
||
|
// RC updates!
|
||
|
VTable.Assert(Magic.addressOf(methodNames[methodIndex].
|
||
|
methodName) ==
|
||
|
Magic.addressOf(methodName),
|
||
|
@"Magic.addressOf(methodNames[methodIndex].
|
||
|
methodName) ==
|
||
|
Magic.addressOf(methodName)");
|
||
|
|
||
|
methods[methodIndex] += rec;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
[NoInline]
|
||
|
[ManualRefCounts]
|
||
|
internal static void EmitRefCountsProfile() {
|
||
|
VTable.Assert(RCCollector.ProfilingMode,
|
||
|
@"RCCollector.ProfilingMode");
|
||
|
|
||
|
if (methods == null) { // No RC updates present.
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Bubble sort in decreasing order of sums.
|
||
|
for (int i = 0; i < methods.Length; i++) {
|
||
|
for (int j = methods.Length-1; j > i; j--) {
|
||
|
if (methods[j].CompareTo(methods[j-1])) {
|
||
|
// Swap contents.
|
||
|
AcctRecord temp = methods[j];
|
||
|
methods[j] = methods[j-1];
|
||
|
methods[j-1] = temp;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
VTable.DebugPrint("\n");
|
||
|
AcctRecord.DispCountsHeader();
|
||
|
AcctRecord.DispMethodNameHeader();
|
||
|
VTable.DebugPrint("\n");
|
||
|
for (int i = 0; i < methods.Length; i++) {
|
||
|
if (methods[i].increments.Total == 0 &&
|
||
|
methods[i].decrements.Total == 0) {
|
||
|
continue;
|
||
|
}
|
||
|
methods[i].DispCounts();
|
||
|
methods[i].DispMethodName();
|
||
|
VTable.DebugPrint("\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
private static Object Allocate(ref BumpAllocator profileData,
|
||
|
VTable vtable,
|
||
|
UIntPtr numBytes) {
|
||
|
UIntPtr resultAddr =
|
||
|
profileData.AllocateFast(numBytes, vtable.baseAlignment);
|
||
|
Object result = Magic.fromAddress(resultAddr);
|
||
|
result.REF_STATE = 1 ;
|
||
|
result.vtable = vtable;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
private static AcctRecord[] methods;
|
||
|
}
|
||
|
}
|