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
2 changes: 1 addition & 1 deletion docs/design/datacontracts/ExecutionManager.md
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,7 @@ Unwind info (`RUNTIME_FUNCTION`) uses relative addressing. For managed code, the

* For Jitted code (`EEJitManager`) a pointer to the `DebugInfo` is stored on the `RealCodeHeader` which is accessed in the same way as `GetMethodInfo` described above. `hasFlagByte` is `true` if either the global `FeatureOnStackReplacement` is `true` or `StoreRichDebugInfo` is `true` on the `EEJitManager`.

* For R2R code (`ReadyToRunJitManager`) the `DebugInfo` is stored as part of the R2R image. The relevant `ReadyToRunInfo` stores a pointer to the an `ImageDataDirectory` representing the `DebugInfo` directory. Read the `VirtualAddress` of this data directory as a `NativeArray` containing the `DebugInfos`. To find the specific `DebugInfo`, index into the array using the `index` of the beginning of the R2R function as found like in `GetMethodInfo` above. This yields an offset `offset` value relative to the image base. Read the first variable length uint at `imageBase + offset`, `lookBack`. If `lookBack != 0`, return `imageBase + offset - lookback`. Otherwise return `offset + size of reading lookback`.
* For R2R code (`ReadyToRunJitManager`) the `DebugInfo` is stored as part of the R2R image. The relevant `ReadyToRunInfo` stores a pointer to the an `ImageDataDirectory` representing the `DebugInfo` directory. If this pointer is null (the image was compiled without debug info, e.g. with `--strip-debug-info`), return `TargetPointer.Null`. Otherwise, read the `VirtualAddress` of this data directory as a `NativeArray` containing the `DebugInfos`. To find the specific `DebugInfo`, index into the array using the `index` of the beginning of the R2R function as found like in `GetMethodInfo` above. This yields an offset `offset` value relative to the image base. Read the first variable length uint at `imageBase + offset`, `lookBack`. If `lookBack != 0`, return `imageBase + offset - lookback`. Otherwise return `offset + size of reading lookback`.
For R2R images, `hasFlagByte` is always `false`.

* For interpreted code (`InterpreterJitManager`), a pointer to the `DebugInfo` is stored on the `InterpreterRealCodeHeader` which is accessed in the same way as the EE JitManager's `GetMethodInfo` (nibble map lookup followed by code header read). `hasFlagByte` is always `false`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@ public override TargetPointer GetDebugInfo(RangeSection rangeSection, TargetCode

// ReadyToRunJitManager::GetDebugInfo
Data.ReadyToRunInfo r2rInfo = GetReadyToRunInfo(rangeSection);

// Images compiled with --strip-debug-info have no DebugInfo section
if (r2rInfo.DebugInfoSection == TargetPointer.Null)
return TargetPointer.Null;

if (!GetRuntimeFunction(rangeSection, r2rInfo, jittedCodeAddress, out TargetPointer codeBase, out uint index))
return TargetPointer.Null;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,73 @@ public void GetMethodDesc_R2R_OneRuntimeFunction(string version, MockTarget.Arch
}
}

[Theory]
[MemberData(nameof(StdArchAllVersions))]
public void GetDebugInfo_R2R_NoDebugInfoSection_ReturnsNull(string version, MockTarget.Architecture arch)
{
const ulong CodeRangeStart = 0x0a0a_0000u;
const uint CodeRangeSize = 0xc000u;
const ulong JitManagerAddress = 0x000b_ff00;
const ulong MethodDescAddress = 0x0101_aaa0;
const uint RuntimeFunction = 0x100;
const uint CodeOffset = 4;
// Make the low addresses readable, as linear address 0 is on wasm, so that dereferencing
// a null DebugInfoSection decodes zeros instead of failing the read.
const ulong ReadableZeroPageSize = 0x1000;

MockExecutionManagerBuilder emBuilder = new(version, arch, MockExecutionManagerBuilder.DefaultAllocationRange);
MockExecutionManagerBuilder.JittedCodeRange jittedCode = emBuilder.AllocateJittedCodeRange(CodeRangeStart, CodeRangeSize);
MockReadyToRunInfo r2rInfo = emBuilder.AddReadyToRunInfo([RuntimeFunction], []);
new MockHashMapBuilder(emBuilder.Builder).PopulatePtrMap(
r2rInfo.EntryPointToMethodDescMapAddress,
[(jittedCode.RangeStart + RuntimeFunction, MethodDescAddress)]);
MockLoaderModule r2rModule = emBuilder.AddReadyToRunModule(r2rInfo.Address);
MockRangeSection rangeSection = emBuilder.AddReadyToRunRangeSection(jittedCode, JitManagerAddress, r2rModule.Address);
_ = emBuilder.AddRangeSectionFragment(jittedCode, rangeSection.Address);
Assert.Equal(0ul, r2rInfo.DebugInfoSection);

TargetCodePointer methodStart = new(CodeRangeStart + RuntimeFunction);
TargetCodePointer pCode = new(CodeRangeStart + RuntimeFunction + CodeOffset);
NativeCodeVersionHandle nativeCodeVersion = NativeCodeVersionHandle.CreateSynthetic(new TargetPointer(MethodDescAddress));
Mock<ICodeVersions> codeVersions = new();
codeVersions.Setup(c => c.GetNativeCodeVersionForIP(pCode)).Returns(nativeCodeVersion);
codeVersions.Setup(c => c.GetNativeCode(nativeCodeVersion)).Returns(methodStart);

MockMemorySpace.MemoryContext memoryContext = emBuilder.Builder.GetMemoryContext();
Target target = CreateTarget(emBuilder, configureTarget: targetBuilder => targetBuilder
.UseReader(ReadWithReadableZeroPage)
.AddContract<IDebugInfo>(version: "c1")
.AddMockContract(codeVersions));
IExecutionManager em = target.Contracts.ExecutionManager;

CodeBlockHandle? handle = em.GetCodeBlockHandle(pCode);
Assert.NotNull(handle);
TargetPointer debugInfo = em.GetDebugInfo(handle.Value, out bool hasFlagByte);
Assert.Equal(TargetPointer.Null, debugInfo);
Assert.False(hasFlagByte);

IDebugInfo debugInfoContract = target.Contracts.DebugInfo;
Assert.False(debugInfoContract.HasDebugInfo(pCode));
Assert.Empty(debugInfoContract.GetMethodVarInfo(pCode, out uint varInfoCodeOffset));
Assert.Equal(CodeOffset, varInfoCodeOffset);
Assert.Empty(debugInfoContract.GetMethodNativeMap(pCode, preferUninstrumented: false, out uint nativeMapCodeOffset));
Assert.Equal(CodeOffset, nativeMapCodeOffset);

int ReadWithReadableZeroPage(ulong address, Span<byte> buffer)
{
if (address < ReadableZeroPageSize)
{
if (address + (ulong)buffer.Length > ReadableZeroPageSize)
return -1;

buffer.Clear();
return 0;
}

return memoryContext.ReadFromTarget(address, buffer);
}
}

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