diff --git a/docs/design/datacontracts/StackWalk.md b/docs/design/datacontracts/StackWalk.md index 774be93cdf7ea6..e30cc0a013302e 100644 --- a/docs/design/datacontracts/StackWalk.md +++ b/docs/design/datacontracts/StackWalk.md @@ -16,6 +16,9 @@ IEnumerable CreateStackWalk(ThreadData threadData); byte[] GetRawContext(IStackDataFrameHandle stackDataFrameHandle); // Gets the Frame address at the given stack dataframe. Returns TargetPointer.Null if the current dataframe does not have a valid Frame. TargetPointer GetFrameAddress(IStackDataFrameHandle stackDataFrameHandle); + +// Gets the Frame name associated with the given Frame identifier. If no matching Frame name found returns an empty string. +string GetFrameName(TargetPointer frameIdentifier); ``` ## Version 1 @@ -323,3 +326,8 @@ If the Frame is not valid, returns `TargetPointer.Null`. TargetPointer GetFrameAddress(IStackDataFrameHandle stackDataFrameHandle); ``` + +`GetFrameName` gets the name associated with a FrameIdentifier (pointer sized value) from the Globals stored in the contract descriptor. If no associated Frame name is found, it returns an empty string. +```csharp +string GetFrameName(TargetPointer frameIdentifier); +``` diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IStackWalk.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IStackWalk.cs index 4fed8a815f9fe5..52d5b56baea0ff 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IStackWalk.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IStackWalk.cs @@ -15,6 +15,7 @@ public interface IStackWalk : IContract public virtual IEnumerable CreateStackWalk(ThreadData threadData) => throw new NotImplementedException(); public virtual byte[] GetRawContext(IStackDataFrameHandle stackDataFrameHandle) => throw new NotImplementedException(); public virtual TargetPointer GetFrameAddress(IStackDataFrameHandle stackDataFrameHandle) => throw new NotImplementedException(); + public virtual string GetFrameName(TargetPointer frameIdentifier) => throw new NotImplementedException(); } public struct StackWalk : IStackWalk diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/FrameIterator.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/FrameIterator.cs index b48a9e9b5433a9..34dd04e874848d 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/FrameIterator.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/FrameIterator.cs @@ -66,7 +66,7 @@ public bool Next() public void UpdateContextFromFrame(IPlatformAgnosticContext context) { - switch (GetFrameType(CurrentFrame)) + switch (GetFrameType(target, CurrentFrame.Identifier)) { case FrameType.InlinedCallFrame: Data.InlinedCallFrame inlinedCallFrame = target.ProcessedData.GetOrAdd(CurrentFrame.Address); @@ -122,7 +122,7 @@ public void UpdateContextFromFrame(IPlatformAgnosticContext context) public bool IsInlineCallFrameWithActiveCall() { - if (GetFrameType(CurrentFrame) != FrameType.InlinedCallFrame) + if (GetFrameType(target, CurrentFrame.Identifier) != FrameType.InlinedCallFrame) { return false; } @@ -130,16 +130,26 @@ public bool IsInlineCallFrameWithActiveCall() return inlinedCallFrame.CallerReturnAddress != 0; } - private FrameType GetFrameType(Data.Frame frame) + public static string GetFrameName(Target target, TargetPointer frameIdentifier) + { + FrameType frameType = GetFrameType(target, frameIdentifier); + if (frameType == FrameType.Unknown) + { + return string.Empty; + } + return frameType.ToString(); + } + + private static FrameType GetFrameType(Target target, TargetPointer frameIdentifier) { foreach (FrameType frameType in Enum.GetValues()) { - TargetPointer typeVptr; + TargetPointer foundFrameIdentifier; try { // not all Frames are in all builds, so we need to catch the exception - typeVptr = target.ReadGlobalPointer(frameType.ToString() + "Identifier"); - if (frame.Identifier == typeVptr) + foundFrameIdentifier = target.ReadGlobalPointer(frameType.ToString() + "Identifier"); + if (frameIdentifier == foundFrameIdentifier) { return frameType; } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs index b5939c9a6b78c9..00705d5adf78db 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs @@ -157,6 +157,9 @@ TargetPointer IStackWalk.GetFrameAddress(IStackDataFrameHandle stackDataFrameHan return TargetPointer.Null; } + string IStackWalk.GetFrameName(TargetPointer frameIdentifier) + => FrameIterator.GetFrameName(_target, frameIdentifier); + private bool IsManaged(TargetPointer ip, [NotNullWhen(true)] out CodeBlockHandle? codeBlockHandle) { IExecutionManager eman = _target.Contracts.ExecutionManager; diff --git a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs index 5a7a79614beb94..2bb42a964e66e3 100644 --- a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs @@ -145,7 +145,54 @@ int ISOSDacInterface.GetFailedAssemblyLocation(ulong assesmbly, uint count, char int ISOSDacInterface.GetFieldDescData(ulong fieldDesc, void* data) => _legacyImpl is not null ? _legacyImpl.GetFieldDescData(fieldDesc, data) : HResults.E_NOTIMPL; int ISOSDacInterface.GetFrameName(ulong vtable, uint count, char* frameName, uint* pNeeded) - => _legacyImpl is not null ? _legacyImpl.GetFrameName(vtable, count, frameName, pNeeded) : HResults.E_NOTIMPL; + { + if (vtable == 0) + { + return HResults.E_INVALIDARG; + } + + int hr = HResults.S_OK; + try + { + IStackWalk stackWalk = _target.Contracts.StackWalk; + string name = stackWalk.GetFrameName(new(vtable)); + + if (string.IsNullOrEmpty(name)) + { + hr = HResults.E_INVALIDARG; + } + else + { + OutputBufferHelpers.CopyStringToBuffer(frameName, count, pNeeded, name); + } + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + +#if DEBUG + if (_legacyImpl is not null) + { + char[] nameLocal = new char[count]; + uint neededLocal; + int hrLocal; + fixed (char* ptr = nameLocal) + { + hrLocal = _legacyImpl.GetFrameName(vtable, 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(frameName == null || new ReadOnlySpan(nameLocal, 0, (int)neededLocal).SequenceEqual(new string(frameName)), + $"cDAC: {new string(frameName)}, DAC: {new string(nameLocal, 0, (int)neededLocal)}"); + } + } +#endif + + return hr; + } int ISOSDacInterface.GetGCHeapData(void* data) => _legacyImpl is not null ? _legacyImpl.GetGCHeapData(data) : HResults.E_NOTIMPL; int ISOSDacInterface.GetGCHeapDetails(ulong heap, void* details)