singrdk/base/Kernel/Bartok/GCs/CallStack.cs

951 lines
41 KiB
C#
Raw Normal View History

2008-03-05 09:52:00 -05:00
/*******************************************************************/
/* 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. */
/*******************************************************************/
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
namespace System.GCs {
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.Bartok.Options;
using Microsoft.Bartok.Runtime;
#if SINGULARITY_KERNEL
using Microsoft.Singularity.X86;
#elif SINGULARITY_PROCESS
using Microsoft.Singularity.X86;
using Microsoft.Singularity.V1.Services;
#endif
[NoCCtor]
[RequiredByBartok]
internal unsafe class CallStack
{
[Mixin(typeof(Thread))]
private class CallStackThread : Object {
[AccessedByRuntime("reference in brtstack.asm")]
internal UIntPtr asmStackBase; // Limit of thread's stack
[AccessedByRuntime("reference in brtstack.asm")]
internal UIntPtr asmStackLimit;
[AccessedByRuntime("reference in brtforgc.asm")]
internal unsafe TransitionRecord *asmStackMarker;
}
private static CallStackThread MixinThread(Thread t) {
return (CallStackThread) (Object) t;
}
internal static UIntPtr StackBase(Thread t) {
return MixinThread(t).asmStackBase;
}
internal static void SetStackBase(Thread t, UIntPtr value) {
MixinThread(t).asmStackBase = value;
}
internal static UIntPtr StackLimit(Thread t) {
return MixinThread(t).asmStackLimit;
}
internal static void SetStackLimit(Thread t, UIntPtr value) {
MixinThread(t).asmStackLimit = value;
}
internal static TransitionRecord* StackMarker(Thread t)
{
return MixinThread(t).asmStackMarker;
}
#if X86
[AccessedByRuntime("referenced from halforgc.asm")]
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct TransitionRecord {
[AccessedByRuntime("referenced from halforgc.asm")]
internal TransitionRecord *oldTransitionRecord;
[AccessedByRuntime("referenced from halforgc.asm")]
internal UIntPtr callAddr;
[AccessedByRuntime("referenced from halforgc.asm")]
internal UIntPtr stackBottom;
[AccessedByRuntime("referenced from halforgc.asm")]
internal UIntPtr EBX;
[AccessedByRuntime("referenced from halforgc.asm")]
internal UIntPtr EDI;
[AccessedByRuntime("referenced from halforgc.asm")]
internal UIntPtr ESI;
[AccessedByRuntime("referenced from halforgc.asm")]
internal UIntPtr EBP;
}
#elif AMD64
[AccessedByRuntime("referenced from halforgc.asm")]
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct TransitionRecord {
[AccessedByRuntime("referenced from halforgc.asm")]
internal TransitionRecord *oldTransitionRecord;
[AccessedByRuntime("referenced from halforgc.asm")]
internal UIntPtr callAddr;
[AccessedByRuntime("referenced from halforgc.asm")]
internal UIntPtr stackBottom;
[AccessedByRuntime("referenced from halforgc.asm")]
internal UIntPtr EBX;
[AccessedByRuntime("referenced from halforgc.asm")]
internal UIntPtr EDI;
[AccessedByRuntime("referenced from halforgc.asm")]
internal UIntPtr ESI;
[AccessedByRuntime("referenced from halforgc.asm")]
internal UIntPtr EBP;
[AccessedByRuntime("referenced from halforgc.asm")]
internal UIntPtr R12;
[AccessedByRuntime("referenced from halforgc.asm")]
internal UIntPtr R13;
[AccessedByRuntime("referenced from halforgc.asm")]
internal UIntPtr R14;
[AccessedByRuntime("referenced from halforgc.asm")]
internal UIntPtr R15;
}
#endif
internal static Thread threadBeingProcessed;
private struct CalleeSaveLocations {
#if SINGULARITY_KERNEL
internal void SetCalleeSaves(ThreadContext *context) {
EBX.SetCalleeReg(&context->ebx);
EDI.SetCalleeReg(&context->edi);
ESI.SetCalleeReg(&context->esi);
EBP.SetCalleeReg(&context->ebp);
}
#endif
#if X86
internal void SetCalleeSaves(TransitionRecord *transitionRecord)
{
EBX.SetCalleeReg(&transitionRecord->EBX);
EDI.SetCalleeReg(&transitionRecord->EDI);
ESI.SetCalleeReg(&transitionRecord->ESI);
EBP.SetCalleeReg(&transitionRecord->EBP);
}
internal void ClearCalleeSaves() {
EBX.ClearCalleeReg();
ESI.ClearCalleeReg();
EDI.ClearCalleeReg();
EBP.ClearCalleeReg();
}
internal
void ScanLiveRegs(uint mask,
NonNullReferenceVisitor referenceVisitor)
{
EDI.ScanLiveReg((mask >> 2) & 0x3, referenceVisitor);
ESI.ScanLiveReg((mask >> 4) & 0x3, referenceVisitor);
EBX.ScanLiveReg((mask >> 0) & 0x3, referenceVisitor);
EBP.ScanLiveReg((mask >> 6) & 0x3, referenceVisitor);
}
internal void PopFrame(UIntPtr *framePointer,
uint calleeSaveMask,
bool framePointerOmitted)
{
UIntPtr *calleeSaveStart;
if (framePointerOmitted) {
calleeSaveStart = framePointer - 1;
} else {
VTable.Assert((calleeSaveMask & 0x10) == 0,
"EBP should not be callee saved");
calleeSaveStart = framePointer;
EBP.PopFrameReg(ref calleeSaveStart);
}
if ((calleeSaveMask & 0x1) != 0) {
calleeSaveStart -=
sizeof(TransitionRecord) / sizeof(UIntPtr);
}
// Note: the order in which these appear is important!
if ((calleeSaveMask & 0x2) != 0) {
EBX.PopFrameReg(ref calleeSaveStart);
}
if ((calleeSaveMask & 0x10) != 0) {
EBP.PopFrameReg(ref calleeSaveStart);
}
if ((calleeSaveMask & 0x8) != 0) {
ESI.PopFrameReg(ref calleeSaveStart);
}
if ((calleeSaveMask & 0x4) != 0) {
EDI.PopFrameReg(ref calleeSaveStart);
}
}
internal void ClearFrame(uint calleeSaveMask,
bool framePointerOmitted)
{
if (!framePointerOmitted) {
VTable.Assert((calleeSaveMask & 0x10) == 0,
"EBP should not be callee saved");
EBP.ClearFrameReg();
}
if ((calleeSaveMask & 0x2) != 0) {
EBX.ClearFrameReg();
}
if ((calleeSaveMask & 0x10) != 0) {
EBP.ClearFrameReg();
}
if ((calleeSaveMask & 0x8) != 0) {
ESI.ClearFrameReg();
}
if ((calleeSaveMask & 0x4) != 0) {
EDI.ClearFrameReg();
}
}
#elif AMD64
internal void SetCalleeSaves(TransitionRecord *transitionRecord)
{
EBX.SetCalleeReg(&transitionRecord->EBX);
EDI.SetCalleeReg(&transitionRecord->EDI);
ESI.SetCalleeReg(&transitionRecord->ESI);
EBP.SetCalleeReg(&transitionRecord->EBP);
R12.SetCalleeReg(&transitionRecord->R12);
R13.SetCalleeReg(&transitionRecord->R13);
R14.SetCalleeReg(&transitionRecord->R14);
R15.SetCalleeReg(&transitionRecord->R15);
}
internal void ClearCalleeSaves() {
EBX.ClearCalleeReg();
ESI.ClearCalleeReg();
EDI.ClearCalleeReg();
EBP.ClearCalleeReg();
R12.ClearCalleeReg();
R13.ClearCalleeReg();
R14.ClearCalleeReg();
R15.ClearCalleeReg();
}
internal
void ScanLiveRegs(uint mask,
NonNullReferenceVisitor referenceVisitor)
{
EDI.ScanLiveReg((mask >> 2) & 0x3, referenceVisitor);
ESI.ScanLiveReg((mask >> 4) & 0x3, referenceVisitor);
EBX.ScanLiveReg((mask >> 0) & 0x3, referenceVisitor);
R12.ScanLiveReg((mask >> 6) & 0x3, referenceVisitor);
R13.ScanLiveReg((mask >> 8) & 0x3, referenceVisitor);
R14.ScanLiveReg((mask >> 10) & 0x3, referenceVisitor);
R15.ScanLiveReg((mask >> 12) & 0x3, referenceVisitor);
EBP.ScanLiveReg((mask >> 14) & 0x3, referenceVisitor);
}
internal void PopFrame(UIntPtr *framePointer,
uint calleeSaveMask,
bool framePointerOmitted)
{
UIntPtr *calleeSaveStart;
if (framePointerOmitted) {
calleeSaveStart = framePointer - 1;
} else {
VTable.Assert((calleeSaveMask & 0x100) == 0,
"EBP should not be callee saved");
calleeSaveStart = framePointer;
EBP.PopFrameReg(ref calleeSaveStart);
}
if ((calleeSaveMask & 0x1) != 0) {
calleeSaveStart -=
sizeof(TransitionRecord) / sizeof(UIntPtr);
}
// Note: the order in which these appear is important!
if ((calleeSaveMask & 0x2) != 0) {
EBX.PopFrameReg(ref calleeSaveStart);
}
if ((calleeSaveMask & 0x100) != 0) {
EBP.PopFrameReg(ref calleeSaveStart);
}
if ((calleeSaveMask & 0x8) != 0) {
ESI.PopFrameReg(ref calleeSaveStart);
}
if ((calleeSaveMask & 0x4) != 0) {
EDI.PopFrameReg(ref calleeSaveStart);
}
if ((calleeSaveMask & 0x10) != 0) {
R12.PopFrameReg(ref calleeSaveStart);
}
if ((calleeSaveMask & 0x20) != 0) {
R13.PopFrameReg(ref calleeSaveStart);
}
if ((calleeSaveMask & 0x40) != 0) {
R14.PopFrameReg(ref calleeSaveStart);
}
if ((calleeSaveMask & 0x80) != 0) {
R15.PopFrameReg(ref calleeSaveStart);
}
}
internal void ClearFrame(uint calleeSaveMask,
bool framePointerOmitted)
{
if (!framePointerOmitted) {
VTable.Assert((calleeSaveMask & 0x100) == 0,
"EBP should not be callee saved");
EBP.ClearFrameReg();
}
if ((calleeSaveMask & 0x2) != 0) {
EBX.ClearFrameReg();
}
if ((calleeSaveMask & 0x100) != 0) {
EBP.ClearFrameReg();
}
if ((calleeSaveMask & 0x8) != 0) {
ESI.ClearFrameReg();
}
if ((calleeSaveMask & 0x4) != 0) {
EDI.ClearFrameReg();
}
if ((calleeSaveMask & 0x10) != 0) {
R12.ClearFrameReg();
}
if ((calleeSaveMask & 0x20) != 0) {
R13.ClearFrameReg();
}
if ((calleeSaveMask & 0x40) != 0) {
R14.ClearFrameReg();
}
if ((calleeSaveMask & 0x80) != 0) {
R15.ClearFrameReg();
}
}
#endif
internal UIntPtr GetFramePointer()
{
return EBP.value;
}
private struct RegLocation
{
[Inline]
internal void SetCalleeReg(UIntPtr *regField)
{
this.pending = false;
this.value = *regField;
this.head = regField;
*regField = UIntPtr.Zero;
}
internal void ClearCalleeReg()
{
VTable.Deny(this.pending);
UIntPtr *scan = this.head;
while (scan != null) {
UIntPtr temp = *scan;
*scan = value;
scan = (UIntPtr *) temp;
}
this.head = null;
}
internal void ScanLiveReg(uint kind,
NonNullReferenceVisitor visitor)
{
switch (kind) {
case 0: {
// Value is not a traceable heap pointer
break;
}
case 1: {
// Value is a pointer variable
VTable.Deny(this.head == null);
if (value != UIntPtr.Zero) {
fixed (UIntPtr *valueField = &this.value) {
visitor.Visit(valueField);
}
}
ClearCalleeReg();
break;
}
case 2: {
// Value is unchanged since function entry
VTable.Deny(this.pending);
this.pending = true;
break;
}
case 3:
default: {
VTable.NotReached("ScanLiveReg 3 or default");
break;
}
}
}
internal void PopFrameReg(ref UIntPtr *calleeSaveStart)
{
if (this.head != null && !this.pending) {
ClearCalleeReg();
}
if (this.head == null) {
this.value = *calleeSaveStart;
} else {
VTable.Assert(this.pending, "pending should be true");
VTable.Assert(*calleeSaveStart == this.value,
"values are not equal");
}
this.pending = false;
*calleeSaveStart = (UIntPtr) this.head;
this.head = calleeSaveStart;
calleeSaveStart--;
}
internal void ClearFrameReg() {
UIntPtr *scan = this.head;
while (scan != null) {
UIntPtr temp = *scan;
*scan = value;
scan = (UIntPtr *) temp;
}
this.head = null;
this.pending = false;
this.value = UIntPtr.Zero;
}
internal bool pending;
internal UIntPtr value;
internal UIntPtr *head;
}
#if X86
RegLocation EBX;
RegLocation EDI;
RegLocation ESI;
RegLocation EBP;
#elif AMD64
RegLocation EBX;
RegLocation EDI;
RegLocation ESI;
RegLocation EBP;
RegLocation R12;
RegLocation R13;
RegLocation R14;
RegLocation R15;
#endif
}
[RequiredByBartok]
private static int callSiteTableCount;
[AccessedByRuntime("referenced from halexn.cpp")]
private static UIntPtr *codeBaseStartTable;
[RequiredByBartok]
private static UIntPtr **returnAddressToCallSiteSetNumbers;
[RequiredByBartok]
private static int **callSiteSetCount;
private static int CallSiteTableNumber(UIntPtr returnAddr)
{
UIntPtr address = returnAddr;
for (int i = 0; i < callSiteTableCount; i++) {
UIntPtr baseAddress = codeBaseStartTable[i];
if (address < baseAddress) {
continue;
}
UIntPtr relativeAddress = address - baseAddress;
UIntPtr *ptr = returnAddressToCallSiteSetNumbers[i];
int callSiteCount = *(callSiteSetCount[i]);
if (relativeAddress >= ptr[0] &&
relativeAddress <= ptr[callSiteCount]) {
return i;
}
}
return -1;
}
private static int CallSiteSetNumber(UIntPtr returnAddr, int index)
{
UIntPtr codeBaseAddr = codeBaseStartTable[index];
UIntPtr relativeAddr = returnAddr - codeBaseAddr;
UIntPtr *callSiteTable = returnAddressToCallSiteSetNumbers[index];
int callSiteCount = *(callSiteSetCount[index]);
int left = 0;
int right = callSiteCount;
// Loop invariant:
// callSiteTable[left] <= returnAddress < callSiteTable[right]
while (left < right-1) {
int mid = (left + right)/2;
if (callSiteTable[mid] <= relativeAddr) {
left = mid;
} else {
right = mid;
}
}
return left;
}
[StructLayout(LayoutKind.Sequential)]
private struct FullDescriptor {
internal UIntPtr mask;
internal int variableData;
}
private const uint ESCAPE32_TAG = 0x0;
private const uint ESCAPE16_TAG = 0x1;
private const uint ESCAPE8_TAG = 0x2;
// Check whether the specified stack frame contains the
// transition record: if so then we're done scanning this
// segment of the stack. NB: the caller must ensure that the
// framePointer has been recomputed in a
// framePointerOmitted-method.
private static bool FrameContainsTransitionRecord(UIntPtr *framePointer,
UIntPtr *stackPointer,
TransitionRecord *stopMarker) {
bool result = false;
if ((framePointer >= stopMarker) && (stackPointer < stopMarker)) {
result = true;
}
return result;
}
// Returns the return address in the calling method
private static
bool WalkStackFrame(ref UIntPtr *framePointer,
ref UIntPtr *stackPointer,
ref CalleeSaveLocations calleeSaves,
ref UIntPtr returnAddr,
NonNullReferenceVisitor threadReferenceVisitor,
NonNullReferenceVisitor pinnedReferenceVisitor,
UIntPtr frameDescriptorEntryPtr,
TransitionRecord *stopMarker,
Thread thread)
// BUGBUG: Remove thread argument when ThreadContext is on stack!
{
Trace.Log(Trace.Area.Stack,
"WalkStackFrame: fp={0:x}, sp={1:x}, returnAddr={2:x}, desc={3:x}",
__arglist(framePointer, stackPointer,
returnAddr, frameDescriptorEntryPtr));
bool framePointerOmitted;
uint liveRegs, calleeSaveMask;
//uint frameDescriptorEntry = (uint) frameDescriptorEntryPtr;
UIntPtr frameDescriptorEntry = frameDescriptorEntryPtr;
if ((frameDescriptorEntry & 0x1) != 0) {
// Compact descriptor
UIntPtr mask;
framePointerOmitted =
(((frameDescriptorEntry >> 1) & 0x1) != 0);
int inbetweenSlots;
if (framePointerOmitted) {
// No frame pointer
inbetweenSlots = Constants.InbetweenSlotsNoFP;
mask =
frameDescriptorEntry >> Constants.CompactStackBitMaskStartNoFP;
calleeSaveMask =
(uint)((frameDescriptorEntry >> Constants.CompactEntryMaskStart)
& Constants.CompactEntryMaskNoFP);
liveRegs = (uint)((frameDescriptorEntry >>
Constants.CompactCalleeSaveUseStartNoFP) &
Constants.CompactCalleeSaveUseMaskNoFP);
// when frame pointer is omitted, the stack top
// equals stackPointer plus frameSize
int frameSize =
unchecked((int)(frameDescriptorEntry >>
Constants.CompactFrameSizeStartNoFP) &
Constants.CompactFrameSizeMaskNoFP);
framePointer = stackPointer + frameSize;
} else {
// Use frame pointer
inbetweenSlots = Constants.InbetweenSlotsFP;
mask = frameDescriptorEntry >> Constants.CompactStackBitMaskStartFP;
calleeSaveMask =
(uint)((frameDescriptorEntry >> Constants.CompactEntryMaskStart)
& Constants.CompactEntryMaskFP);
liveRegs =
(uint) ((frameDescriptorEntry >>
Constants.CompactCalleeSaveUseStartFP) &
Constants.CompactCalleeSaveUseMaskFP);
}
Trace.Log(Trace.Area.Stack,
"Compact desc: fpo={0}, mask={1:x}, inbetweenSlots={2}, calleeSaveMask={3:x}, liveRegs={4:x}",
__arglist(framePointerOmitted, mask, inbetweenSlots,
calleeSaveMask, liveRegs));
if (FrameContainsTransitionRecord(framePointer,
stackPointer,
stopMarker)) {
return true; // true: done
}
uint stackArgSize =
(uint)((frameDescriptorEntry >> Constants.CompactArgMaskStart)
& Constants.CompactArgMask);
UIntPtr *p = framePointer + stackArgSize + inbetweenSlots;
Trace.Log(Trace.Area.Stack,
"Compact desc: stackArgSize={0:x}, p={1:x}",
__arglist(stackArgSize, p));
// Process the arguments
if (threadReferenceVisitor != null) {
for (int i = 0; mask != 0 && i <= stackArgSize; i++) {
if ((mask & 0x1) != 0 && *p != UIntPtr.Zero) {
// BUGBUG: threadReferenceVisitor.Visit(p);
VisitIfNotContext(thread, threadReferenceVisitor,
p);
}
mask >>= 1;
p--;
}
} else {
for (int i = 0; mask != 0 && i <= stackArgSize; i++) {
mask >>= 1;
}
}
// Process the local variables
bool transitionRecord = ((calleeSaveMask & 0x1) != 0);
uint savedRegs = (uint) (calleeSaveMask >> 1);
uint countRegs = ((savedRegs & 1) +
((savedRegs >> 1) & 1) +
((savedRegs >> 2) & 1) +
((savedRegs >> 3) & 1) +
((savedRegs >> 4) & 1) +
((savedRegs >> 5) & 1) +
((savedRegs >> 6) & 1) +
((savedRegs >> 7) & 1));
int transitionRecordSize =
transitionRecord ?
sizeof(TransitionRecord)/sizeof(UIntPtr) :
0;
p = framePointer - 1 - countRegs - transitionRecordSize;
Trace.Log(Trace.Area.Stack,
"Compact desc: savedRegs={0:x}, countRegs={0:x}, transSize={0:x}, p={0:x}", __arglist(savedRegs, countRegs, transitionRecordSize));
if (threadReferenceVisitor != null) {
while (mask != 0) {
if ((mask & 1) != 0 && *p != UIntPtr.Zero) {
// BUGBUG: threadReferenceVisitor.Visit(p);
VisitIfNotContext(thread, threadReferenceVisitor,
p);
}
mask >>= 1;
p--;
}
}
} else {
// Full descriptor
FullDescriptor *desc =
((FullDescriptor *) frameDescriptorEntryPtr);
UIntPtr mask = (UIntPtr) desc->mask;
framePointerOmitted = ((mask & 1) != 0);
bool pinnedVars;
uint frameTag;
if (framePointerOmitted) {
// no frame pointer
calleeSaveMask = (uint) ((mask >> Constants.FullEntryMaskStart) &
Constants.FullEntryMaskNoFP);
pinnedVars = (((mask >> Constants.FullPinnedPosNoFP) & 1) != 0);
liveRegs = (uint) (( mask >> Constants.FullCalleeSaveUseStartNoFP) &
Constants.FullCalleeSaveUseMaskNoFP);
frameTag = (uint) ((mask >> Constants.FullRecordSizePosNoFP) &
Constants.FullRecordMask);
// when frame pointer is omitted, the stack top
// equals stackPointer plus frameSize
int frameSize = unchecked((int)(mask >> Constants.FullFrameSizeStartNoFP));
framePointer = stackPointer + frameSize;
} else {
// use frame pointer
calleeSaveMask = (uint) ((mask >> Constants.FullEntryMaskStart) &
Constants.FullEntryMaskFP);
pinnedVars = (((mask >> Constants.FullPinnedPosFP) & 1) != 0);
liveRegs = (uint) (( mask >> Constants.FullCalleeSaveUseStartFP) &
Constants.FullCalleeSaveUseMaskFP);
frameTag = (uint) ((mask >> Constants.FullRecordSizePosFP) &
Constants.FullRecordMask);
}
if (FrameContainsTransitionRecord(framePointer,
stackPointer,
stopMarker)) {
return true; // true: done
}
switch (frameTag) {
case ESCAPE8_TAG: {
int count = *(byte *) &desc->variableData;
byte pinnedCount = *((byte*)&desc->variableData+1);
sbyte *offsets = (sbyte *)&desc->variableData+2;
if (threadReferenceVisitor != null) {
for (int i = 0; i < count; i++) {
UIntPtr *loc = framePointer + offsets[i];
if (*loc != UIntPtr.Zero) {
// BUGBUG: threadReferenceVisitor.Visit(loc);
VisitIfNotContext(thread, threadReferenceVisitor, loc);
}
}
}
if (pinnedVars && pinnedReferenceVisitor != null) {
int last = count + pinnedCount;
for (int i = count; i < last; i++) {
UIntPtr *loc = framePointer + offsets[i];
if (*loc != UIntPtr.Zero) {
pinnedReferenceVisitor.Visit(loc);
}
}
}
break;
}
case ESCAPE16_TAG: {
int count = *((short *) &desc->variableData);
short pinnedCount = *((short*)&desc->variableData+1);
short *offsets = (short *)&desc->variableData+2;
if (threadReferenceVisitor != null) {
for (int i = 0; i < count; i++) {
UIntPtr *loc = framePointer + offsets[i];
if (*loc != UIntPtr.Zero) {
// BUGBUG: threadReferenceVisitor.Visit(loc);
VisitIfNotContext(thread, threadReferenceVisitor, loc);
}
}
}
if (pinnedVars && pinnedReferenceVisitor != null) {
int last = count + pinnedCount;
for (int i = count; i < last; i++) {
UIntPtr *loc = framePointer + offsets[i];
if (*loc != UIntPtr.Zero) {
pinnedReferenceVisitor.Visit(loc);
}
}
}
break;
}
case ESCAPE32_TAG: {
int count = *((int *) &desc->variableData);
int pinnedCount = *((int *) &desc->variableData+1);
int *offsets = (int *) &desc->variableData+2;
if (threadReferenceVisitor != null) {
for (int i = 0; i < count; i++) {
UIntPtr *loc = framePointer + offsets[i];
if (*loc != UIntPtr.Zero) {
// BUGBUG: threadReferenceVisitor.Visit(loc);
VisitIfNotContext(thread, threadReferenceVisitor, loc);
}
}
}
if (pinnedVars && pinnedReferenceVisitor != null) {
int last = count + pinnedCount;
for (int i = count; i < last; i++) {
UIntPtr *loc = framePointer + offsets[i];
if (*loc != UIntPtr.Zero) {
pinnedReferenceVisitor.Visit(loc);
}
}
}
break;
}
default: {
VTable.NotReached("GC mask switch failed");
break;
}
}
}
if (liveRegs != 0 && threadReferenceVisitor != null) {
calleeSaves.ScanLiveRegs(liveRegs, threadReferenceVisitor);
}
UIntPtr nextReturnAddr;
if (framePointerOmitted) {
nextReturnAddr = *framePointer;
stackPointer = framePointer + 1;
} else {
nextReturnAddr = *(framePointer + 1);
stackPointer = framePointer + 2;
}
// In Singularity, the final return address of a thread is zero
if (nextReturnAddr != UIntPtr.Zero) {
calleeSaves.PopFrame(framePointer, calleeSaveMask,
framePointerOmitted);
} else {
calleeSaves.ClearFrame(calleeSaveMask, framePointerOmitted);
}
UIntPtr *calcedfp = (UIntPtr *) calleeSaves.GetFramePointer();
if (calcedfp != null) {
framePointer = calcedfp;
}
returnAddr = nextReturnAddr;
return false; // false: not done scanning: proceed to next frame
}
#if SINGULARITY_PROCESS
// BUGBUG: Get rid of this when ThreadContext is on stack! --rusa
[Inline]
private static
void VisitIfNotContext(Thread thread,
NonNullReferenceVisitor threadReferenceVisitor,
UIntPtr *p)
{
if (*p != (UIntPtr) thread.context &&
(int *) *p != &thread.context->gcStates) {
threadReferenceVisitor.Visit(p);
}
}
#else
[Inline]
private static
void VisitIfNotContext(Thread thread,
NonNullReferenceVisitor threadReferenceVisitor,
UIntPtr *p)
{
threadReferenceVisitor.Visit(p);
}
#endif
[RequiredByBartok]
private static ushort **callSetSiteNumberToIndex;
[RequiredByBartok]
private static UIntPtr **activationDescriptorTable;
[PreInitRefCounts]
internal static
int ScanStacks(NonNullReferenceVisitor VisitThreadReference,
NonNullReferenceVisitor VisitPinnedReference)
{
int limit = Thread.threadTable.Length;
int countThreads = 0;
for (int i = 0; i < limit; i++) {
Thread t = Thread.threadTable[i];
if (t != null) {
CollectorStatistics.Event(GCEvent.StackScanStart, i);
ScanStack(t, VisitThreadReference, VisitPinnedReference);
CollectorStatistics.Event(GCEvent.StackScanComplete, i);
countThreads++;
}
}
return countThreads;
}
[PreInitRefCounts]
internal static
uint ScanStack(Thread thread,
NonNullReferenceVisitor VisitThreadReference,
NonNullReferenceVisitor VisitPinnedReference)
{
Trace.Log(Trace.Area.Stack,
"Scanning stack for thread {0:x}",
__arglist(thread.threadIndex));
uint numStackFrames = 0;
threadBeingProcessed = thread;
CalleeSaveLocations calleeSaves = new CalleeSaveLocations();
#if SINGULARITY_KERNEL
TransitionRecord *marker = (TransitionRecord *) thread.context.stackMarkers;
#elif SINGULARITY_PROCESS
TransitionRecord *marker = null;
if (thread.context != null) {
marker = (TransitionRecord *) thread.context->stackMarkers;
}
#else
TransitionRecord *marker = (TransitionRecord *)
MixinThread(thread).asmStackMarker;
#endif
while (marker != null) {
Trace.Log(Trace.Area.Stack,
"Transition record: old={0:x}, callAddr={1:x}, stackBottom={2:x}, EBX={3:x}, EDI={4:x}, ESI={5:x}, EBP={6:x}", __arglist(marker->oldTransitionRecord, marker->callAddr, marker->stackBottom, marker->EBX, marker->EDI, marker->ESI, marker->EBP));
TransitionRecord *stopMarker = marker->oldTransitionRecord;
UIntPtr returnAddr = marker->callAddr;
UIntPtr *fp = (UIntPtr *) marker->EBP;
UIntPtr *sp = (UIntPtr *) marker->stackBottom;
calleeSaves.SetCalleeSaves(marker);
numStackFrames +=
ScanStackSegment(ref calleeSaves, returnAddr, fp, sp,
stopMarker, VisitThreadReference,
VisitPinnedReference, thread);
marker = marker->oldTransitionRecord;
}
threadBeingProcessed = null;
return numStackFrames;
}
[PreInitRefCounts]
private static
uint ScanStackSegment(ref CalleeSaveLocations calleeSaves,
UIntPtr returnAddr,
UIntPtr *fp,
UIntPtr *sp,
TransitionRecord *stopMarker,
NonNullReferenceVisitor VisitThreadReference,
NonNullReferenceVisitor VisitPinnedReference,
Thread thread)
// BUGBUG: Remove thread argument when ThreadContext is on stack!
{
#if SINGULARITY
UIntPtr unlinkBegin;
UIntPtr unlinkLimit;
fixed (byte *begin =
&Microsoft.Singularity.Memory.Stacks.UnlinkStackBegin) {
unlinkBegin = (UIntPtr)begin;
}
fixed (byte *limit =
&Microsoft.Singularity.Memory.Stacks.UnlinkStackLimit) {
unlinkLimit = (UIntPtr)limit;
}
#endif
uint numStackFrames = 0;
while (true) {
#if SINGULARITY
if (returnAddr >= unlinkBegin && returnAddr <= unlinkLimit) {
returnAddr = WalkStack(ref fp, ref sp);
}
#endif
// Exit loop if we have reached the of the stack segment
if (fp >= stopMarker && sp < stopMarker) {
break;
}
int tableIndex = CallSiteTableNumber(returnAddr);
if (tableIndex < 0) {
break;
}
int callSiteSet = CallSiteSetNumber(returnAddr, tableIndex);
if (callSiteSet < 0) {
break;
}
ushort *callSiteToIndexTable =
callSetSiteNumberToIndex[tableIndex];
int activationIndex = (int) callSiteToIndexTable[callSiteSet];
VTable.Assert(activationIndex >= 0);
UIntPtr *descriptorTable =
activationDescriptorTable[tableIndex];
UIntPtr frameDescriptorEntry = descriptorTable[activationIndex];
bool done = WalkStackFrame(ref fp, ref sp, ref calleeSaves,
ref returnAddr,
VisitThreadReference,
VisitPinnedReference,
frameDescriptorEntry,
stopMarker, thread);
if (done) {
break;
}
numStackFrames++;
}
calleeSaves.ClearCalleeSaves();
return numStackFrames;
}
private static UIntPtr WalkStack(ref UIntPtr *framePointer,
ref UIntPtr *stackPointer) {
// the frame pointer points to the ebp in the old frame
// since at the end of WalkStackFrame, it sets return addr
// and pop up a frame. However, since we are using linked
// stack, "pop up a frame" didn't actually set the frame
// to the caller's frame, it just goes from new frame to the
// old frame, therefore, we need to get return addr and pop
// a frame again.
stackPointer = framePointer + 2;
UIntPtr returnAddr = *(framePointer + 1);
framePointer = (UIntPtr*) *framePointer;
return returnAddr;
}
}
}