Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -2641,7 +2641,70 @@ int ISOSDacInterface.GetNestedExceptionData(ClrDataAddress exception, ClrDataAdd
}

int ISOSDacInterface.GetObjectClassName(ClrDataAddress obj, uint count, char* className, uint* pNeeded)
=> _legacyImpl is not null ? _legacyImpl.GetObjectClassName(obj, count, className, pNeeded) : HResults.E_NOTIMPL;
{
int hr = HResults.S_OK;
try
{
if (obj == 0)
throw new ArgumentException();

Contracts.IObject objectContract = _target.Contracts.Object;
Contracts.IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem;
Contracts.ILoader loader = _target.Contracts.Loader;

TargetPointer mt = objectContract.GetMethodTableAddress(obj.ToTargetPointer(_target));
Contracts.TypeHandle typeHandle = rts.GetTypeHandle(mt);

TargetPointer modulePointer = rts.GetModule(typeHandle);
Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(modulePointer);
if (!loader.TryGetLoadedImageContents(moduleHandle, out _, out _, out _))
{
OutputBufferHelpers.CopyStringToBuffer(className, count, pNeeded, "<Unloaded Type>");
}
else
{
StringBuilder classNameBuilder = new();
try
{
TypeNameBuilder.AppendType(_target, classNameBuilder, typeHandle, TypeNameFormat.FormatNamespace | TypeNameFormat.FormatFullInst);
}
catch
{
string? fallbackName = _target.Contracts.DacStreams.StringFromEEAddress(mt);
if (fallbackName != null)
{
classNameBuilder.Clear();
classNameBuilder.Append(fallbackName);
}
}
OutputBufferHelpers.CopyStringToBuffer(className, count, pNeeded, classNameBuilder.ToString());
}
}
catch (System.Exception ex)
{
hr = ex.HResult;
}

#if DEBUG
if (_legacyImpl is not null)
{
char[] classNameLocal = new char[count];
uint neededLocal;
int hrLocal;
fixed (char* ptr = classNameLocal)
{
hrLocal = _legacyImpl.GetObjectClassName(obj, count, ptr, &neededLocal);
}
Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}");
if (hr == HResults.S_OK)
{
Debug.Assert(pNeeded == null || *pNeeded == neededLocal);
Debug.Assert(className == null || new ReadOnlySpan<char>(classNameLocal, 0, (int)neededLocal - 1).SequenceEqual(new string(className)));
}
}
#endif
return hr;
}

int ISOSDacInterface.GetObjectData(ClrDataAddress objAddr, DacpObjectData* data)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,4 +253,44 @@ public void RuntimeTypeSystem_ObjectMethodTableHasIntroducedMethods(TestConfigur
Assert.Equal(0x06000000u, token & 0xFF000000u);
}
}

[ConditionalTheory]
[MemberData(nameof(TestConfigurations))]
public void RuntimeTypeSystem_ObjectMethodTableHasLoadedModule(TestConfiguration config)
{
InitializeDumpTest(config);
IRuntimeTypeSystem rts = Target.Contracts.RuntimeTypeSystem;
ILoader loader = Target.Contracts.Loader;

TargetPointer objectMTGlobal = Target.ReadGlobalPointer("ObjectMethodTable");
TargetPointer objectMT = Target.ReadPointer(objectMTGlobal);
TypeHandle handle = rts.GetTypeHandle(objectMT);

TargetPointer modulePointer = rts.GetModule(handle);
Assert.NotEqual(TargetPointer.Null, modulePointer);

ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(modulePointer);
bool isLoaded = loader.TryGetLoadedImageContents(moduleHandle, out _, out _, out _);
Assert.True(isLoaded, "System.Object's module should have loaded image contents");
}

[ConditionalTheory]
[MemberData(nameof(TestConfigurations))]
public void RuntimeTypeSystem_StringMethodTableHasLoadedModule(TestConfiguration config)
{
InitializeDumpTest(config);
IRuntimeTypeSystem rts = Target.Contracts.RuntimeTypeSystem;
ILoader loader = Target.Contracts.Loader;

TargetPointer stringMTGlobal = Target.ReadGlobalPointer("StringMethodTable");
TargetPointer stringMT = Target.ReadPointer(stringMTGlobal);
TypeHandle handle = rts.GetTypeHandle(stringMT);

TargetPointer modulePointer = rts.GetModule(handle);
Assert.NotEqual(TargetPointer.Null, modulePointer);

ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(modulePointer);
bool isLoaded = loader.TryGetLoadedImageContents(moduleHandle, out _, out _, out _);
Assert.True(isLoaded, "System.String's module should have loaded image contents");
}
}
113 changes: 113 additions & 0 deletions src/native/managed/cdac/tests/ObjectTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using Microsoft.Diagnostics.DataContractReader.Contracts;
using Microsoft.Diagnostics.DataContractReader.Legacy;
using Moq;
using Xunit;

Expand Down Expand Up @@ -153,4 +154,116 @@ public void ComData(MockTarget.Architecture arch)
}
});
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void GetObjectClassName_ZeroAddress(MockTarget.Architecture arch)
{
ObjectContractHelper(arch,
(objectBuilder) => { },
(target) =>
{
ISOSDacInterface sosDac = new SOSDacImpl(target, legacyObj: null);
char[] buffer = new char[256];
uint needed;
int hr;
fixed (char* ptr = buffer)
{
hr = sosDac.GetObjectClassName(default, (uint)buffer.Length, ptr, &needed);
}
Assert.NotEqual(HResults.S_OK, hr);
});
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void GetObjectClassName_UnloadedModule(MockTarget.Architecture arch)
{
TargetPointer TestObjectAddress = default;
TargetPointer TestMethodTableAddress = default;
ObjectContractHelper(arch,
(objectBuilder) =>
{
TargetPointer eeClass = objectBuilder.RTSBuilder.AddEEClass("TestClass", attr: 0, numMethods: 0, numNonVirtualSlots: 0);
TestMethodTableAddress = objectBuilder.RTSBuilder.AddMethodTable("TestClass",
mtflags: default, mtflags2: default, baseSize: objectBuilder.Builder.TargetTestHelpers.ObjectBaseSize,
module: TargetPointer.Null, parentMethodTable: TargetPointer.Null, numInterfaces: 0, numVirtuals: 0);
objectBuilder.RTSBuilder.SetEEClassAndCanonMTRefs(eeClass, TestMethodTableAddress);
TestObjectAddress = objectBuilder.AddObject(TestMethodTableAddress);
},
(target) =>
{
var mockRts = new Mock<IRuntimeTypeSystem>();
TypeHandle handle = new TypeHandle(TestMethodTableAddress);
mockRts.Setup(r => r.GetTypeHandle(TestMethodTableAddress)).Returns(handle);
mockRts.Setup(r => r.GetModule(handle)).Returns(TargetPointer.Null);

var mockLoader = new Mock<ILoader>();
mockLoader.Setup(l => l.GetModuleHandleFromModulePtr(It.IsAny<TargetPointer>())).Returns(default(Contracts.ModuleHandle));
mockLoader.Setup(l => l.TryGetLoadedImageContents(It.IsAny<Contracts.ModuleHandle>(), out It.Ref<TargetPointer>.IsAny, out It.Ref<uint>.IsAny, out It.Ref<uint>.IsAny)).Returns(false);

var mockObject = new Mock<IObject>();
mockObject.Setup(o => o.GetMethodTableAddress(It.IsAny<TargetPointer>())).Returns(TestMethodTableAddress);

((TestPlaceholderTarget)target).SetContracts(Mock.Of<ContractRegistry>(
c => c.Object == mockObject.Object
&& c.RuntimeTypeSystem == mockRts.Object
&& c.Loader == mockLoader.Object));

ISOSDacInterface sosDac = new SOSDacImpl(target, legacyObj: null);
char[] buffer = new char[256];
uint needed;
int hr;
fixed (char* ptr = buffer)
{
hr = sosDac.GetObjectClassName(new ClrDataAddress(TestObjectAddress.Value), (uint)buffer.Length, ptr, &needed);
}
Assert.Equal(HResults.S_OK, hr);
Assert.Equal((uint)"<Unloaded Type>".Length + 1, needed);
Assert.Equal("<Unloaded Type>", new string(buffer, 0, (int)needed - 1));
});
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void GetObjectClassName_NullBufferReturnsNeededSize(MockTarget.Architecture arch)
{
TargetPointer TestObjectAddress = default;
TargetPointer TestMethodTableAddress = default;
ObjectContractHelper(arch,
(objectBuilder) =>
{
TargetPointer eeClass = objectBuilder.RTSBuilder.AddEEClass("TestClass", attr: 0, numMethods: 0, numNonVirtualSlots: 0);
TestMethodTableAddress = objectBuilder.RTSBuilder.AddMethodTable("TestClass",
mtflags: default, mtflags2: default, baseSize: objectBuilder.Builder.TargetTestHelpers.ObjectBaseSize,
module: TargetPointer.Null, parentMethodTable: TargetPointer.Null, numInterfaces: 0, numVirtuals: 0);
objectBuilder.RTSBuilder.SetEEClassAndCanonMTRefs(eeClass, TestMethodTableAddress);
TestObjectAddress = objectBuilder.AddObject(TestMethodTableAddress);
},
(target) =>
{
var mockRts = new Mock<IRuntimeTypeSystem>();
TypeHandle handle = new TypeHandle(TestMethodTableAddress);
mockRts.Setup(r => r.GetTypeHandle(TestMethodTableAddress)).Returns(handle);
mockRts.Setup(r => r.GetModule(handle)).Returns(TargetPointer.Null);

var mockLoader = new Mock<ILoader>();
mockLoader.Setup(l => l.GetModuleHandleFromModulePtr(It.IsAny<TargetPointer>())).Returns(default(Contracts.ModuleHandle));
mockLoader.Setup(l => l.TryGetLoadedImageContents(It.IsAny<Contracts.ModuleHandle>(), out It.Ref<TargetPointer>.IsAny, out It.Ref<uint>.IsAny, out It.Ref<uint>.IsAny)).Returns(false);

var mockObject = new Mock<IObject>();
mockObject.Setup(o => o.GetMethodTableAddress(It.IsAny<TargetPointer>())).Returns(TestMethodTableAddress);

((TestPlaceholderTarget)target).SetContracts(Mock.Of<ContractRegistry>(
c => c.Object == mockObject.Object
&& c.RuntimeTypeSystem == mockRts.Object
&& c.Loader == mockLoader.Object));

ISOSDacInterface sosDac = new SOSDacImpl(target, legacyObj: null);
uint needed;
int hr = sosDac.GetObjectClassName(new ClrDataAddress(TestObjectAddress.Value), 0, null, &needed);
Assert.Equal(HResults.S_OK, hr);
Assert.Equal((uint)"<Unloaded Type>".Length + 1, needed);
});
}
}
Loading