Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions docs/design/datacontracts/GCInfo.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ This contract is for fetching information related to GCInfo associated with nati

The GCInfo contract has platform specific implementations as GCInfo differs per architecture. With the exception of x86, all platforms have a common encoding scheme with different encoding lengths and normalization functions for data. x86 uses an entirely different scheme which is partially supported by this contract.

WebAssembly uses the common decoder with `Wasm32GcInfoEncoding` traits from
`src/coreclr/inc/gcinfotypes.h`: code lengths/offsets and stack offsets are
unscaled, the code-length encoding base is 6, and there is no fixed stack
parameter scratch area. The platform encoding has no interruptible-range
count in its fat header (`HAS_INTERRUPTIBLE_RANGES` is false).
`DecodeInterpreterGCInfo` uses the separate interpreter traits even on
WebAssembly, including its code-length encoding base of 8 and its
interruptible-range count field.

## APIs of contract

```csharp
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.Diagnostics.DataContractReader.Contracts.GCInfoHelpers;

// Mirrors Wasm32GcInfoEncoding in src/coreclr/inc/gcinfotypes.h.
internal sealed class WasmGCInfoTraits : IGCInfoTraits
{
public static uint DenormalizeStackBaseRegister(uint reg) => reg;
public static uint DenormalizeCodeLength(uint len) => len;
public static uint NormalizeCodeLength(uint len) => len;
public static uint DenormalizeCodeOffset(uint offset) => offset;
public static uint NormalizeCodeOffset(uint offset) => offset;
public static int DenormalizeStackSlot(int value) => value;
public static uint DenormalizeSizeOfStackArea(uint size) => size;

public static int GENERICS_INST_CONTEXT_STACK_SLOT_ENCBASE => 6;
public static int GS_COOKIE_STACK_SLOT_ENCBASE => 6;
public static int CODE_LENGTH_ENCBASE => 6;
public static int STACK_BASE_REGISTER_ENCBASE => 3;
public static int SIZE_OF_STACK_AREA_ENCBASE => 6;
public static int SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE => 3;
public static int SIZE_OF_EDIT_AND_CONTINUE_FIXED_STACK_FRAME_ENCBASE => 0;
public static int REVERSE_PINVOKE_FRAME_ENCBASE => 6;
public static int NUM_REGISTERS_ENCBASE => 3;
public static int NUM_STACK_SLOTS_ENCBASE => 5;
public static int NUM_UNTRACKED_SLOTS_ENCBASE => 5;
public static int NORM_PROLOG_SIZE_ENCBASE => 4;
public static int NORM_EPILOG_SIZE_ENCBASE => 3;
public static int INTERRUPTIBLE_RANGE_DELTA1_ENCBASE => 5;
public static int INTERRUPTIBLE_RANGE_DELTA2_ENCBASE => 5;
public static int REGISTER_ENCBASE => 3;
public static int REGISTER_DELTA_ENCBASE => 3;
public static int STACK_SLOT_ENCBASE => 6;
public static int STACK_SLOT_DELTA_ENCBASE => 4;
public static int NUM_SAFE_POINTS_ENCBASE => 4;
public static int NUM_INTERRUPTIBLE_RANGES_ENCBASE => 1;
public static bool HAS_FIXED_STACK_PARAMETER_SCRATCH_AREA => false;
public static bool HAS_INTERRUPTIBLE_RANGES => false;
public static bool IsScratchRegister(uint regNum) => false;
Comment thread
radekdoulik marked this conversation as resolved.
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public static void Register(ContractRegistry registry)
RuntimeInfoArchitecture.Arm => new GCInfo_1<ARMGCInfoTraits>(t),
RuntimeInfoArchitecture.LoongArch64 => new GCInfo_1<LoongArch64GCInfoTraits>(t),
RuntimeInfoArchitecture.RiscV64 => new GCInfo_1<RISCV64GCInfoTraits>(t),
RuntimeInfoArchitecture.Wasm => new GCInfo_1<WasmGCInfoTraits>(t),
_ => default(GCInfo),
};
});
Expand Down
46 changes: 46 additions & 0 deletions src/native/managed/cdac/tests/UnitTests/StackWalkTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,52 @@ namespace Microsoft.Diagnostics.DataContractReader.Tests;

public unsafe class StackWalkTests
{
[Theory]
[InlineData(false, new byte[] { 0x04, 0x05, 0x21, 0x02 }, uint.MaxValue)]
[InlineData(false, new byte[] { 0x81, 0x08, 0x0a, 0x30, 0x42, 0x04 }, 8u)]
[InlineData(true, new byte[] { 0x04, 0x4a, 0x04 }, uint.MaxValue)]
[InlineData(true, new byte[] { 0x81, 0x08, 0x84, 0x11, 0x22, 0x00 }, 8u)]
public void GCInfo_Wasm_DecodesPlatformAndInterpreterFormats(
bool interpreter, byte[] encoded, uint stackBaseRegister)
{
MockTarget.Architecture arch = new() { IsLittleEndian = true, Is64Bit = false };
TestPlaceholderTarget.Builder builder = new(arch);
// Length 129 and safe point 17 distinguish the platform's six-bit encoding
// from the interpreter's eight-bit encoding and exercise header alignment.
byte[] gcInfo = new byte[32];
encoded.CopyTo(gcInfo, 0);
builder.MemoryBuilder.AddHeapFragment(new MockMemorySpace.HeapFragment
{
Address = 0x1000,
Data = gcInfo,
Name = "Wasm GC info"
});
Mock<IRuntimeInfo> runtimeInfo = new();
runtimeInfo.Setup(r => r.GetTargetArchitecture()).Returns(RuntimeInfoArchitecture.Wasm);
IGCInfo contract = builder
.AddMockContract(runtimeInfo)
.AddContract<IGCInfo>(version: "c1")
.Build().Contracts.GCInfo;
IGCInfoHandle handle = interpreter
? contract.DecodeInterpreterGCInfo(new TargetPointer(0x1000), 4)
: contract.DecodePlatformSpecificGCInfo(new TargetPointer(0x1000), 4);

GCInfoHeader header = contract.GetHeader(handle);
Assert.Equal(129u, contract.GetCodeLength(handle));
Assert.Equal(129u, header.CodeSize);
Assert.Equal(stackBaseRegister, header.StackBaseRegister);
Assert.Equal(0u, header.SizeOfStackParameterArea);
Assert.Equal(new uint[] { 17 }, contract.GetSafePoints(handle));
Assert.Empty(contract.GetInterruptibleRanges(handle));
Assert.Equal(interpreter, contract.TryGetGenericContextStorage(
handle, GenericContextLoc.ThisPtr, 0, out GenericContextStorage storage));
if (interpreter)
{
Assert.Equal(GenericContextStorageKind.InterpreterArgumentRelative, storage.Kind);
Assert.Equal(0, storage.Offset);
}
}

[Fact]
public void LoongArch64Unwind_EpilogReturn_DoesNotRepeatStackAdjustment()
{
Expand Down
Loading