mirror of
https://github.com/VSadov/Satori.git
synced 2025-06-10 01:50:53 +09:00
[cdac] Implement ISOSDacInterface.GetThreadData (#103324)
- Implement `GetThreadData` in `Thread` contract - Finish implementing `ISOSDacInterface::GetThreadData` in cDAC - Add `ExceptionInfo` to cDAC types
This commit is contained in:
parent
01172dba2e
commit
547e69e0b7
13 changed files with 204 additions and 65 deletions
|
@ -19,61 +19,12 @@ record struct ThreadStoreCounts (
|
|||
|
||||
enum ThreadState
|
||||
{
|
||||
TS_Unknown = 0x00000000, // threads are initialized this way
|
||||
|
||||
TS_AbortRequested = 0x00000001, // Abort the thread
|
||||
|
||||
TS_GCSuspendRedirected = 0x00000004, // ThreadSuspend::SuspendRuntime has redirected the thread to suspention routine.
|
||||
|
||||
TS_DebugSuspendPending = 0x00000008, // Is the debugger suspending threads?
|
||||
TS_GCOnTransitions = 0x00000010, // Force a GC on stub transitions (GCStress only)
|
||||
|
||||
TS_LegalToJoin = 0x00000020, // Is it now legal to attempt a Join()
|
||||
|
||||
TS_ExecutingOnAltStack = 0x00000040, // Runtime is executing on an alternate stack located anywhere in the memory
|
||||
|
||||
TS_Hijacked = 0x00000080, // Return address has been hijacked
|
||||
|
||||
// unused = 0x00000100,
|
||||
TS_Background = 0x00000200, // Thread is a background thread
|
||||
TS_Unstarted = 0x00000400, // Thread has never been started
|
||||
TS_Dead = 0x00000800, // Thread is dead
|
||||
|
||||
TS_WeOwn = 0x00001000, // Exposed object initiated this thread
|
||||
TS_CoInitialized = 0x00002000, // CoInitialize has been called for this thread
|
||||
|
||||
TS_InSTA = 0x00004000, // Thread hosts an STA
|
||||
TS_InMTA = 0x00008000, // Thread is part of the MTA
|
||||
|
||||
// Some bits that only have meaning for reporting the state to clients.
|
||||
TS_ReportDead = 0x00010000, // in WaitForOtherThreads()
|
||||
TS_FullyInitialized = 0x00020000, // Thread is fully initialized and we are ready to broadcast its existence to external clients
|
||||
|
||||
TS_TaskReset = 0x00040000, // The task is reset
|
||||
|
||||
TS_SyncSuspended = 0x00080000, // Suspended via WaitSuspendEvent
|
||||
TS_DebugWillSync = 0x00100000, // Debugger will wait for this thread to sync
|
||||
|
||||
TS_StackCrawlNeeded = 0x00200000, // A stackcrawl is needed on this thread, such as for thread abort
|
||||
// See comment for s_pWaitForStackCrawlEvent for reason.
|
||||
|
||||
// unused = 0x00400000,
|
||||
|
||||
// unused = 0x00800000,
|
||||
TS_TPWorkerThread = 0x01000000, // is this a threadpool worker thread?
|
||||
|
||||
TS_Interruptible = 0x02000000, // sitting in a Sleep(), Wait(), Join()
|
||||
TS_Interrupted = 0x04000000, // was awakened by an interrupt APC. !!! This can be moved to TSNC
|
||||
|
||||
TS_CompletionPortThread = 0x08000000, // Completion port thread
|
||||
|
||||
TS_AbortInitiated = 0x10000000, // set when abort is begun
|
||||
|
||||
TS_Finalized = 0x20000000, // The associated managed Thread object has been finalized.
|
||||
// We can clean up the unmanaged part now.
|
||||
|
||||
TS_FailStarted = 0x40000000, // The thread fails during startup.
|
||||
TS_Detached = 0x80000000, // Thread was detached by DllMain
|
||||
Unknown = 0x00000000, // threads are initialized this way
|
||||
Hijacked = 0x00000080, // Return address has been hijacked
|
||||
Background = 0x00000200, // Thread is a background thread
|
||||
Unstarted = 0x00000400, // Thread has never been started
|
||||
Dead = 0x00000800, // Thread is dead
|
||||
ThreadPoolWorker = 0x01000000, // is this a threadpool worker thread?
|
||||
}
|
||||
|
||||
record struct ThreadData (
|
||||
|
|
|
@ -107,9 +107,17 @@ 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)
|
||||
CDAC_TYPE_FIELD(Thread, /*pointer*/, AllocContext, cdac_offsets<Thread>::AllocContext)
|
||||
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)
|
||||
|
@ -122,6 +130,21 @@ 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(GCAllocContext)
|
||||
CDAC_TYPE_INDETERMINATE(GCAllocContext)
|
||||
CDAC_TYPE_FIELD(GCAllocContext, /*pointer*/, Pointer, offsetof(gc_alloc_context, alloc_ptr))
|
||||
CDAC_TYPE_FIELD(GCAllocContext, /*pointer*/, Limit, offsetof(gc_alloc_context, alloc_limit))
|
||||
CDAC_TYPE_END(GCAllocContext)
|
||||
|
||||
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)
|
||||
|
@ -129,6 +152,7 @@ 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)
|
||||
|
|
|
@ -1675,10 +1675,10 @@ public:
|
|||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
// The one and only AppDomain
|
||||
SPTR_DECL(AppDomain, m_pTheAppDomain);
|
||||
|
||||
private:
|
||||
SString m_friendlyName;
|
||||
PTR_Assembly m_pRootAssembly;
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ class DebuggerExState;
|
|||
class EHClauseInfo;
|
||||
|
||||
#include "exceptionhandling.h"
|
||||
#include "cdacoffsets.h"
|
||||
|
||||
#if !defined(FEATURE_EH_FUNCLETS)
|
||||
// ExInfo contains definitions for 32bit
|
||||
|
@ -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;
|
||||
|
|
|
@ -3971,9 +3971,25 @@ 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);
|
||||
static constexpr size_t PreemptiveGCDisabled = offsetof(Thread, m_fPreemptiveGCDisabled);
|
||||
static constexpr size_t AllocContext = offsetof(Thread, m_alloc_context);
|
||||
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);
|
||||
|
||||
static_assert(std::is_same<decltype(std::declval<Thread>().m_ExceptionState), ThreadExceptionState>::value,
|
||||
"Thread::m_ExceptionState is of type ThreadExceptionState");
|
||||
#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
|
||||
|
||||
#ifndef TARGET_UNIX
|
||||
static constexpr size_t TEB = offsetof(Thread, m_pTEB);
|
||||
#endif
|
||||
};
|
||||
|
||||
// End of class Thread
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,28 @@ internal record struct ThreadStoreCounts(
|
|||
int PendingThreadCount,
|
||||
int DeadThreadCount);
|
||||
|
||||
[Flags]
|
||||
internal enum ThreadState
|
||||
{
|
||||
Unknown = 0x00000000,
|
||||
Hijacked = 0x00000080, // Return address has been hijacked
|
||||
Background = 0x00000200, // Thread is a background thread
|
||||
Unstarted = 0x00000400, // Thread has never been started
|
||||
Dead = 0x00000800, // Thread is dead
|
||||
ThreadPoolWorker = 0x01000000, // Thread is a thread pool worker thread
|
||||
}
|
||||
|
||||
internal record struct ThreadData(
|
||||
uint Id,
|
||||
TargetNUInt OSId,
|
||||
ThreadState State,
|
||||
bool PreemptiveGCDisabled,
|
||||
TargetPointer AllocContextPointer,
|
||||
TargetPointer AllocContextLimit,
|
||||
TargetPointer Frame,
|
||||
TargetPointer FirstNestedException,
|
||||
TargetPointer TEB,
|
||||
TargetPointer LastThrownObjectHandle,
|
||||
TargetPointer NextThread);
|
||||
|
||||
internal interface IThread : IContract
|
||||
|
@ -85,8 +105,29 @@ internal readonly struct Thread_1 : IThread
|
|||
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,
|
||||
(ThreadState)thread.State,
|
||||
(thread.PreemptiveGCDisabled & 0x1) != 0,
|
||||
thread.AllocContext is null ? TargetPointer.Null : thread.AllocContext.Pointer,
|
||||
thread.AllocContext is null ? TargetPointer.Null : thread.AllocContext.Limit,
|
||||
thread.Frame,
|
||||
firstNestedException,
|
||||
thread.TEB,
|
||||
thread.LastThrownObject,
|
||||
GetThreadFromLink(thread.LinkNext));
|
||||
}
|
||||
|
||||
|
|
19
src/native/managed/cdacreader/src/Data/ExceptionInfo.cs
Normal file
19
src/native/managed/cdacreader/src/Data/ExceptionInfo.cs
Normal file
|
@ -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; }
|
||||
}
|
20
src/native/managed/cdacreader/src/Data/GCAllocContext.cs
Normal file
20
src/native/managed/cdacreader/src/Data/GCAllocContext.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
// 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 GCAllocContext : IData<GCAllocContext>
|
||||
{
|
||||
static GCAllocContext IData<GCAllocContext>.Create(Target target, TargetPointer address)
|
||||
=> new GCAllocContext(target, address);
|
||||
|
||||
public GCAllocContext(Target target, TargetPointer address)
|
||||
{
|
||||
Target.TypeInfo type = target.GetTypeInfo(DataType.GCAllocContext);
|
||||
Pointer = target.ReadPointer(address + (ulong)type.Fields[nameof(Pointer)].Offset);
|
||||
Limit = target.ReadPointer(address + (ulong)type.Fields[nameof(Limit)].Offset);
|
||||
}
|
||||
|
||||
public TargetPointer Pointer { get; init; }
|
||||
public TargetPointer Limit { get; init; }
|
||||
}
|
|
@ -13,9 +13,35 @@ internal sealed class Thread : IData<Thread>
|
|||
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);
|
||||
|
||||
TargetPointer allocContextPointer = target.ReadPointer(address + (ulong)type.Fields[nameof(AllocContext)].Offset);
|
||||
if (allocContextPointer != TargetPointer.Null)
|
||||
AllocContext = target.ProcessedData.GetOrAdd<GCAllocContext>(allocContextPointer);
|
||||
|
||||
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 GCAllocContext? AllocContext { 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; }
|
||||
}
|
||||
|
|
|
@ -22,4 +22,6 @@ public enum DataType
|
|||
GCHandle,
|
||||
Thread,
|
||||
ThreadStore,
|
||||
GCAllocContext,
|
||||
ExceptionInfo,
|
||||
}
|
||||
|
|
|
@ -115,6 +115,23 @@ internal sealed partial class SOSDacImpl : ISOSDacInterface, ISOSDacInterface9
|
|||
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 - fibers are no longer supported
|
||||
|
||||
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 - lock count was .NET Framework and no longer needed
|
||||
data->pFrame = threadData.Frame;
|
||||
data->firstNestedException = threadData.FirstNestedException;
|
||||
data->teb = threadData.TEB;
|
||||
data->lastThrownObjectHandle = threadData.LastThrownObjectHandle;
|
||||
data->nextThread = threadData.NextThread;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -122,8 +139,7 @@ internal sealed partial class SOSDacImpl : ISOSDacInterface, ISOSDacInterface9
|
|||
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;
|
||||
|
|
|
@ -11,17 +11,23 @@ using Microsoft.Diagnostics.DataContractReader.Data;
|
|||
|
||||
namespace Microsoft.Diagnostics.DataContractReader;
|
||||
|
||||
public struct TargetPointer
|
||||
public readonly struct TargetPointer
|
||||
{
|
||||
public static TargetPointer Null = new(0);
|
||||
|
||||
public ulong Value;
|
||||
public readonly ulong Value;
|
||||
public TargetPointer(ulong value) => Value = value;
|
||||
|
||||
public static implicit operator ulong(TargetPointer p) => p.Value;
|
||||
public static implicit operator TargetPointer(ulong v) => new TargetPointer(v);
|
||||
}
|
||||
|
||||
public readonly struct TargetNUInt
|
||||
{
|
||||
public readonly ulong Value;
|
||||
public TargetNUInt(ulong value) => Value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Representation of the target under inspection
|
||||
/// </summary>
|
||||
|
@ -266,24 +272,37 @@ public sealed unsafe class Target
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue