Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[cdac] Implement ISOSDacInterface.GetThreadData #103324

Merged
merged 9 commits into from
Jun 18, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
19 changes: 19 additions & 0 deletions src/coreclr/debug/runtimeinfo/datadescriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,18 @@ CDAC_TYPE_BEGIN(Thread)
CDAC_TYPE_INDETERMINATE(Thread)
CDAC_TYPE_FIELD(Thread, /*uint32*/, Id, cdac_offsets<Thread>::Id)
CDAC_TYPE_FIELD(Thread, /*nuint*/, OSId, cdac_offsets<Thread>::OSId)
CDAC_TYPE_FIELD(Thread, /*uint32*/, State, cdac_offsets<Thread>::State)
CDAC_TYPE_FIELD(Thread, /*uint32*/, PreemptiveGCDisabled, cdac_offsets<Thread>::PreemptiveGCDisabled)
elinor-fung marked this conversation as resolved.
Show resolved Hide resolved
CDAC_TYPE_FIELD(Thread, /*pointer*/, AllocContextPointer, cdac_offsets<Thread>::AllocContextPointer)
elinor-fung marked this conversation as resolved.
Show resolved Hide resolved
CDAC_TYPE_FIELD(Thread, /*pointer*/, AllocContextLimit, cdac_offsets<Thread>::AllocContextLimit)
CDAC_TYPE_FIELD(Thread, /*pointer*/, Frame, cdac_offsets<Thread>::Frame)
CDAC_TYPE_FIELD(Thread, /*pointer*/, ExceptionTracker, cdac_offsets<Thread>::ExceptionTracker)
CDAC_TYPE_FIELD(Thread, GCHandle, GCHandle, cdac_offsets<Thread>::ExposedObject)
CDAC_TYPE_FIELD(Thread, GCHandle, LastThrownObject, cdac_offsets<Thread>::LastThrownObject)
CDAC_TYPE_FIELD(Thread, pointer, LinkNext, cdac_offsets<Thread>::Link)
#ifndef TARGET_UNIX
CDAC_TYPE_FIELD(Thread, /*pointer*/, TEB, cdac_offsets<Thread>::TEB)
#endif
CDAC_TYPE_END(Thread)

CDAC_TYPE_BEGIN(ThreadStore)
Expand All @@ -122,13 +131,23 @@ CDAC_TYPE_FIELD(ThreadStore, /*int32*/, PendingCount, cdac_offsets<ThreadStore>:
CDAC_TYPE_FIELD(ThreadStore, /*int32*/, DeadCount, cdac_offsets<ThreadStore>::DeadCount)
CDAC_TYPE_END(ThreadStore)

CDAC_TYPE_BEGIN(ExceptionInfo)
CDAC_TYPE_INDETERMINATE(ExceptionInfo)
#if FEATURE_EH_FUNCLETS
CDAC_TYPE_FIELD(PreviousNestedInfo, /*pointer*/, PreviousNestedInfo, offsetof(ExceptionTrackerBase, m_pPrevNestedInfo))
#else
CDAC_TYPE_FIELD(PreviousNestedInfo, /*pointer*/, PreviousNestedInfo, offsetof(ExInfo, m_pPrevNestedInfo))
#endif
CDAC_TYPE_END(ExceptionInfo)

CDAC_TYPE_BEGIN(GCHandle)
CDAC_TYPE_SIZE(sizeof(OBJECTHANDLE))
CDAC_TYPE_END(GCHandle)

CDAC_TYPES_END()

CDAC_GLOBALS_BEGIN()
CDAC_GLOBAL_POINTER(AppDomain, &AppDomain::m_pTheAppDomain)
CDAC_GLOBAL_POINTER(ThreadStore, &ThreadStore::s_pThreadStore)
CDAC_GLOBAL_POINTER(FinalizerThread, &::g_pFinalizerThread)
CDAC_GLOBAL_POINTER(GCThread, &::g_pSuspensionThread)
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/vm/appdomain.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2160,10 +2160,10 @@ class AppDomain : public BaseDomain
}
#endif

private:
// The one and only AppDomain
SPTR_DECL(AppDomain, m_pTheAppDomain);

private:
SString m_friendlyName;
PTR_Assembly m_pRootAssembly;

Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/vm/exstate.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class DebuggerExState;
class EHClauseInfo;

#include "exceptionhandling.h"
#include "cdacoffsets.h"

#if !defined(FEATURE_EH_FUNCLETS)
// ExInfo contains definitions for 32bit
Expand Down Expand Up @@ -50,6 +51,8 @@ class ThreadExceptionState
// ExceptionTracker or the ExInfo as appropriate for the platform
friend class ProfToEEInterfaceImpl;

template<typename T> friend struct ::cdac_offsets;

#ifdef FEATURE_EH_FUNCLETS
friend class ExceptionTracker;
friend struct ExInfo;
Expand Down
13 changes: 13 additions & 0 deletions src/coreclr/vm/threads.h
Original file line number Diff line number Diff line change
Expand Up @@ -4039,9 +4039,22 @@ struct cdac_offsets<Thread>
{
static constexpr size_t Id = offsetof(Thread, m_ThreadId);
static constexpr size_t OSId = offsetof(Thread, m_OSThreadId);
static constexpr size_t State = offsetof(Thread, m_State);
jkotas marked this conversation as resolved.
Show resolved Hide resolved
static constexpr size_t PreemptiveGCDisabled = offsetof(Thread, m_fPreemptiveGCDisabled);
static constexpr size_t AllocContextPointer = offsetof(Thread, m_alloc_context) + offsetof(gc_alloc_context, alloc_ptr);
static constexpr size_t AllocContextLimit = offsetof(Thread, m_alloc_context) + offsetof(gc_alloc_context, alloc_limit);
static constexpr size_t Frame = offsetof(Thread, m_pFrame);
static constexpr size_t ExposedObject = offsetof(Thread, m_ExposedObject);
static constexpr size_t LastThrownObject = offsetof(Thread, m_LastThrownObjectHandle);
static constexpr size_t Link = offsetof(Thread, m_Link);
#ifdef FEATURE_EH_FUNCLETS
static constexpr size_t ExceptionTracker = offsetof(Thread, m_ExceptionState) + offsetof(ThreadExceptionState, m_pCurrentTracker);
#else
static constexpr size_t ExceptionTracker = offsetof(Thread, m_ExceptionState) + offsetof(ThreadExceptionState, m_currentExInfo);
#endif
lambdageek marked this conversation as resolved.
Show resolved Hide resolved
#ifndef TARGET_UNIX
static constexpr size_t TEB = offsetof(Thread, m_pTEB);
#endif
};

// End of class Thread
Expand Down
2 changes: 2 additions & 0 deletions src/native/managed/cdacreader/src/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ internal static class Constants
internal static class Globals
{
// See src/coreclr/debug/runtimeinfo/datadescriptor.h
internal const string AppDomain = nameof(AppDomain);
internal const string ThreadStore = nameof(ThreadStore);
internal const string FinalizerThread = nameof(FinalizerThread);
internal const string GCThread = nameof(GCThread);

internal const string FeatureEHFunclets = nameof(FeatureEHFunclets);
internal const string SOSBreakingChangeVersion = nameof(SOSBreakingChangeVersion);
}
}
30 changes: 30 additions & 0 deletions src/native/managed/cdacreader/src/Contracts/Thread.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ internal record struct ThreadStoreCounts(

internal record struct ThreadData(
uint Id,
TargetNUInt OSId,
uint State,
bool PreemptiveGCDisabled,
TargetPointer AllocContextPointer,
TargetPointer AllocContextLimit,
TargetPointer Frame,
TargetPointer FirstNestedException,
TargetPointer TEB,
TargetPointer LastThrownObjectHandle,
TargetPointer NextThread);

internal interface IThread : IContract
Expand Down Expand Up @@ -85,8 +94,29 @@ ThreadStoreCounts IThread.GetThreadCounts()
ThreadData IThread.GetThreadData(TargetPointer threadPointer)
{
Data.Thread thread = _target.ProcessedData.GetOrAdd<Data.Thread>(threadPointer);

// Exception tracker is a pointer when EH funclets are enabled
TargetPointer address = _target.ReadGlobal<byte>(Constants.Globals.FeatureEHFunclets) != 0
? _target.ReadPointer(thread.ExceptionTracker)
: thread.ExceptionTracker;
TargetPointer firstNestedException = TargetPointer.Null;
if (address != TargetPointer.Null)
{
Data.ExceptionInfo exceptionInfo = _target.ProcessedData.GetOrAdd<Data.ExceptionInfo>(address);
firstNestedException = exceptionInfo.PreviousNestedInfo;
}

return new ThreadData(
thread.Id,
thread.OSId,
thread.State,
thread.PreemptiveGCDisabled != 0,
thread.AllocContextPointer,
thread.AllocContextLimit,
thread.Frame,
firstNestedException,
thread.TEB,
thread.LastThrownObject,
GetThreadFromLink(thread.LinkNext));
}

Expand Down
19 changes: 19 additions & 0 deletions src/native/managed/cdacreader/src/Data/ExceptionInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// 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.Data;

internal sealed class ExceptionInfo : IData<ExceptionInfo>
{
static ExceptionInfo IData<ExceptionInfo>.Create(Target target, TargetPointer address)
=> new ExceptionInfo(target, address);

public ExceptionInfo(Target target, TargetPointer address)
{
Target.TypeInfo type = target.GetTypeInfo(DataType.ExceptionInfo);

PreviousNestedInfo = target.ReadPointer(address + (ulong)type.Fields[nameof(PreviousNestedInfo)].Offset);
}

public TargetPointer PreviousNestedInfo { get; init; }
}
24 changes: 24 additions & 0 deletions src/native/managed/cdacreader/src/Data/Thread.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,33 @@ public Thread(Target target, TargetPointer address)
Target.TypeInfo type = target.GetTypeInfo(DataType.Thread);

Id = target.Read<uint>(address + (ulong)type.Fields[nameof(Id)].Offset);
OSId = target.ReadNUInt(address + (ulong)type.Fields[nameof(OSId)].Offset);
State = target.Read<uint>(address + (ulong)type.Fields[nameof(State)].Offset);
PreemptiveGCDisabled = target.Read<uint>(address + (ulong)type.Fields[nameof(PreemptiveGCDisabled)].Offset);
AllocContextPointer = target.ReadPointer(address + (ulong)type.Fields[nameof(AllocContextPointer)].Offset);
AllocContextLimit = target.ReadPointer(address + (ulong)type.Fields[nameof(AllocContextLimit)].Offset);
Frame = target.ReadPointer(address + (ulong)type.Fields[nameof(Frame)].Offset);

// TEB does not exist on certain platforms
TEB = type.Fields.TryGetValue(nameof(TEB), out Target.FieldInfo fieldInfo)
? target.ReadPointer(address + (ulong)fieldInfo.Offset)
: TargetPointer.Null;
LastThrownObject = target.ReadPointer(address + (ulong)type.Fields[nameof(LastThrownObject)].Offset);
LinkNext = target.ReadPointer(address + (ulong)type.Fields[nameof(LinkNext)].Offset);

// Address of the exception tracker - how it should be read depends on EH funclets feature global value
ExceptionTracker = address + (ulong)type.Fields[nameof(ExceptionTracker)].Offset;
}

public uint Id { get; init; }
public TargetNUInt OSId { get; init; }
public uint State { get; init; }
public uint PreemptiveGCDisabled { get; init; }
public TargetPointer AllocContextPointer { get; init; }
public TargetPointer AllocContextLimit { get; init; }
public TargetPointer Frame { get; init; }
public TargetPointer TEB { get; init; }
public TargetPointer LastThrownObject { get; init; }
public TargetPointer LinkNext { get; init; }
public TargetPointer ExceptionTracker { get; init; }
}
1 change: 1 addition & 0 deletions src/native/managed/cdacreader/src/DataType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ public enum DataType
GCHandle,
Thread,
ThreadStore,
ExceptionInfo,
}
20 changes: 18 additions & 2 deletions src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,15 +115,31 @@ public unsafe int GetThreadData(ulong thread, DacpThreadData* data)
Contracts.IThread contract = _target.Contracts.Thread;
Contracts.ThreadData threadData = contract.GetThreadData(thread);
data->corThreadId = (int)threadData.Id;
data->osThreadId = (int)threadData.OSId.Value;
data->state = (int)threadData.State;
data->preemptiveGCDisabled = (uint)(threadData.PreemptiveGCDisabled ? 1 : 0);
data->allocContextPtr = threadData.AllocContextPointer;
data->allocContextLimit = threadData.AllocContextLimit;
data->fiberData = 0; // Always set to 0
elinor-fung marked this conversation as resolved.
Show resolved Hide resolved

TargetPointer appDomainPointer = _target.ReadGlobalPointer(Constants.Globals.AppDomain);
TargetPointer appDomain = _target.ReadPointer(appDomainPointer);
data->context = appDomain;
data->domain = appDomain;

data->lockCount = -1; // Always set to -1
elinor-fung marked this conversation as resolved.
Show resolved Hide resolved
data->pFrame = threadData.Frame;
data->firstNestedException = threadData.FirstNestedException;
data->teb = threadData.TEB;
data->lastThrownObjectHandle = threadData.LastThrownObjectHandle;
data->nextThread = threadData.NextThread;
}
catch (Exception ex)
{
return ex.HResult;
}

// TODO: [cdac] Implement/populate rest of thread data fields
return HResults.E_NOTIMPL;
return HResults.S_OK;
}
public unsafe int GetThreadFromThinlockID(uint thinLockId, ulong* pThread) => HResults.E_NOTIMPL;
public unsafe int GetThreadLocalModuleData(ulong thread, uint index, void* data) => HResults.E_NOTIMPL;
Expand Down
32 changes: 27 additions & 5 deletions src/native/managed/cdacreader/src/Target.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ public struct TargetPointer
public static implicit operator TargetPointer(ulong v) => new TargetPointer(v);
}

public struct TargetNUInt
elinor-fung marked this conversation as resolved.
Show resolved Hide resolved
{
public ulong Value;
public TargetNUInt(ulong value) => Value = value;

public static implicit operator ulong(TargetNUInt p) => p.Value;
public static implicit operator TargetNUInt(ulong v) => new TargetNUInt(v);
}
lambdageek marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// Representation of the target under inspection
/// </summary>
Expand Down Expand Up @@ -266,24 +275,37 @@ public TargetPointer ReadPointer(ulong address)
return pointer;
}

public TargetNUInt ReadNUInt(ulong address)
{
if (!TryReadNUInt(address, _config, _reader, out ulong value))
throw new InvalidOperationException($"Failed to read nuint at 0x{address:x8}.");

return new TargetNUInt(value);
}

private static bool TryReadPointer(ulong address, Configuration config, Reader reader, out TargetPointer pointer)
{
pointer = TargetPointer.Null;

Span<byte> buffer = stackalloc byte[config.PointerSize];
if (reader.ReadFromTarget(address, buffer) < 0)
if (!TryReadNUInt(address, config, reader, out ulong value))
return false;

pointer = new TargetPointer(value);
return true;
}

private static bool TryReadNUInt(ulong address, Configuration config, Reader reader, out ulong value)
{
value = 0;
if (config.PointerSize == sizeof(uint)
&& TryRead(address, config.IsLittleEndian, reader, out uint value32))
{
pointer = new TargetPointer(value32);
value = value32;
return true;
}
else if (config.PointerSize == sizeof(ulong)
&& TryRead(address, config.IsLittleEndian, reader, out ulong value64))
{
pointer = new TargetPointer(value64);
value = value64;
return true;
}

Expand Down
Loading