Renames the RegisterService to ThreadService and adds methods to implement these issues:
https://github.com/dotnet/diagnostics/issues/558 "setthread should support different types of thread ids"
https://github.com/dotnet/diagnostics/issues/559 "Sort threads by thread id"
/// <summary>
/// Current OS thread Id
/// </summary>
- public int CurrentThreadId { get; set; }
+ public uint? CurrentThreadId { get; set; }
/// <summary>
/// Cancellation token for current command
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+namespace Microsoft.Diagnostics.DebugServices
+{
+ /// <summary>
+ /// Diagnostics exception
+ /// </summary>
+ public class DiagnosticsException : Exception
+ {
+ public DiagnosticsException()
+ : base()
+ {
+ }
+
+ public DiagnosticsException(string message)
+ : base(message)
+ {
+ }
+
+ public DiagnosticsException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System;
+
+namespace Microsoft.Diagnostics.DebugServices
+{
+ /// <summary>
+ /// Provides thread and register info and values
+ /// </summary>
+ public interface IThreadService
+ {
+ /// <summary>
+ /// Details on all the supported registers
+ /// </summary>
+ IEnumerable<RegisterInfo> Registers { get; }
+
+ /// <summary>
+ /// The instruction pointer register index
+ /// </summary>
+ int InstructionPointerIndex { get; }
+
+ /// <summary>
+ /// The frame pointer register index
+ /// </summary>
+ int FramePointerIndex { get; }
+
+ /// <summary>
+ /// The stack pointer register index
+ /// </summary>
+ int StackPointerIndex { get; }
+
+ /// <summary>
+ /// Return the register index for the register name
+ /// </summary>
+ /// <param name="name">register name</param>
+ /// <param name="registerIndex">returns register index or -1</param>
+ /// <returns>true if name found</returns>
+ bool GetRegisterIndexByName(string name, out int registerIndex);
+
+ /// <summary>
+ /// Returns the register info (name, offset, size, etc).
+ /// </summary>
+ /// <param name="registerIndex">register index</param>
+ /// <param name="info">RegisterInfo</param>
+ /// <returns>true if index found</returns>
+ bool GetRegisterInfo(int registerIndex, out RegisterInfo info);
+
+ /// <summary>
+ /// Returns the register value for the thread and register index. This function
+ /// can only return register values that are 64 bits or less and currently the
+ /// clrmd data targets don't return any floating point or larger registers.
+ /// </summary>
+ /// <param name="threadId">thread id</param>
+ /// <param name="registerIndex">register index</param>
+ /// <param name="value">value returned</param>
+ /// <returns>true if value found</returns>
+ bool GetRegisterValue(uint threadId, int registerIndex, out ulong value);
+
+ /// <summary>
+ /// Returns the raw context buffer bytes for the specified thread.
+ /// </summary>
+ /// <param name="threadId">thread id</param>
+ /// <returns>register context</returns>
+ /// <exception cref="DiagnosticsException">invalid thread id</exception>
+ byte[] GetThreadContext(uint threadId);
+
+ /// <summary>
+ /// Enumerate all the native threads
+ /// </summary>
+ /// <returns>ThreadInfos for all the threads</returns>
+ IEnumerable<ThreadInfo> EnumerateThreads();
+
+ /// <summary>
+ /// Get the thread info from the thread index
+ /// </summary>
+ /// <param name="threadIndex">index</param>
+ /// <returns>thread info</returns>
+ /// <exception cref="DiagnosticsException">invalid thread index</exception>
+ ThreadInfo GetThreadInfoFromIndex(int threadIndex);
+
+ /// <summary>
+ /// Get the thread info from the OS thread id
+ /// </summary>
+ /// <param name="threadId">os id</param>
+ /// <returns>thread info</returns>
+ /// <exception cref="DiagnosticsException">invalid thread id</exception>
+ ThreadInfo GetThreadInfoFromId(uint threadId);
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace Microsoft.Diagnostics.DebugServices
+{
+ /// <summary>
+ /// Details about a register
+ /// </summary>
+ public struct RegisterInfo
+ {
+ public readonly int RegisterIndex;
+ public readonly int RegisterOffset;
+ public readonly int RegisterSize;
+ public readonly string RegisterName;
+
+ public RegisterInfo(int registerIndex, int registerOffset, int registerSize, string registerName)
+ {
+ RegisterIndex = registerIndex;
+ RegisterOffset = registerOffset;
+ RegisterSize = registerSize;
+ RegisterName = registerName;
+ }
+ }
+}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using Microsoft.Diagnostics.Runtime;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
-using System.Reflection;
-using System.Runtime.InteropServices;
-using Architecture = Microsoft.Diagnostics.Runtime.Architecture;
-
-namespace Microsoft.Diagnostics.DebugServices
-{
- /// <summary>
- /// Provides register info and values
- /// </summary>
- public class RegisterService
- {
- public struct RegisterInfo
- {
- public readonly int RegisterIndex;
- public readonly int RegisterOffset;
- public readonly int RegisterSize;
- public readonly string RegisterName;
-
- internal RegisterInfo(int registerIndex, int registerOffset, int registerSize, string registerName)
- {
- RegisterIndex = registerIndex;
- RegisterOffset = registerOffset;
- RegisterSize = registerSize;
- RegisterName = registerName;
- }
- }
-
- private readonly DataTarget _target;
- private readonly int _contextSize;
- private readonly uint _contextFlags;
- private readonly Dictionary<string, RegisterInfo> _lookupByName;
- private readonly Dictionary<int, RegisterInfo> _lookupByIndex;
- private readonly Dictionary<uint, byte[]> _threadContextCache = new Dictionary<uint, byte[]>();
-
- public IEnumerable<RegisterInfo> Registers { get; }
-
- public int InstructionPointerIndex { get; }
-
- public int FramePointerIndex { get; }
-
- public int StackPointerIndex { get; }
-
- public RegisterService(DataTarget target)
- {
- _target = target;
-
- Type contextType;
- switch (target.Architecture)
- {
- case Architecture.Amd64:
- // Dumps generated with newer dbgeng have bigger context buffers and clrmd requires the context size to at least be that size.
- _contextSize = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? 0x700 : AMD64Context.Size;
- _contextFlags = AMD64Context.ContextControl | AMD64Context.ContextInteger | AMD64Context.ContextSegments;
- contextType = typeof(AMD64Context);
- break;
-
- case Architecture.X86:
- _contextSize = X86Context.Size;
- _contextFlags = X86Context.ContextControl | X86Context.ContextInteger | X86Context.ContextSegments;
- contextType = typeof(X86Context);
- break;
-
- case Architecture.Arm64:
- _contextSize = Arm64Context.Size;
- _contextFlags = Arm64Context.ContextControl | Arm64Context.ContextInteger;
- contextType = typeof(Arm64Context);
- break;
-
- case Architecture.Arm:
- _contextSize = ArmContext.Size;
- _contextFlags = ArmContext.ContextControl | ArmContext.ContextInteger;
- contextType = typeof(ArmContext);
- break;
-
- default:
- throw new PlatformNotSupportedException($"Unsupported architecture: {target.Architecture}");
- }
-
- var registers = new List<RegisterInfo>();
- int index = 0;
-
- FieldInfo[] fields = contextType.GetFields(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic);
- foreach (FieldInfo field in fields) {
- RegisterAttribute registerAttribute = field.GetCustomAttributes<RegisterAttribute>(inherit: false).SingleOrDefault();
- if (registerAttribute == null) {
- continue;
- }
- RegisterType registerType = registerAttribute.RegisterType & RegisterType.TypeMask;
- switch (registerType)
- {
- case RegisterType.Control:
- case RegisterType.General:
- case RegisterType.Segments:
- break;
- default:
- continue;
- }
- if ((registerAttribute.RegisterType & RegisterType.ProgramCounter) != 0) {
- InstructionPointerIndex = index;
- }
- if ((registerAttribute.RegisterType & RegisterType.StackPointer) != 0) {
- StackPointerIndex = index;
- }
- if ((registerAttribute.RegisterType & RegisterType.FramePointer) != 0) {
- FramePointerIndex = index;
- }
- FieldOffsetAttribute offsetAttribute = field.GetCustomAttributes<FieldOffsetAttribute>(inherit: false).Single();
- var registerInfo = new RegisterInfo(index, offsetAttribute.Value, Marshal.SizeOf(field.FieldType), registerAttribute.Name ?? field.Name.ToLower());
- registers.Add(registerInfo);
- index++;
- }
-
- _lookupByName = registers.ToDictionary((info) => info.RegisterName);
- _lookupByIndex = registers.ToDictionary((info) => info.RegisterIndex);
-
- Registers = registers;
- }
-
- /// <summary>
- /// Return the register index for the register name
- /// </summary>
- /// <param name="name">register name</param>
- /// <param name="index">returns register index or -1</param>
- /// <returns>true if name found</returns>
- public bool GetRegisterIndexByName(string name, out int index)
- {
- if (_lookupByName.TryGetValue(name, out RegisterInfo info))
- {
- index = info.RegisterIndex;
- return true;
- }
- index = int.MaxValue;
- return false;
- }
-
- /// <summary>
- /// Returns the register info (name, offset, size, etc).
- /// </summary>
- /// <param name="index">register index</param>
- /// <param name="info">RegisterInfo</param>
- /// <returns>true if index found</returns>
- public bool GetRegisterInfo(int index, out RegisterInfo info)
- {
- return _lookupByIndex.TryGetValue(index, out info);
- }
-
- /// <summary>
- /// Returns the register value for the thread and register index. This function
- /// can only return register values that are 64 bits or less and currently the
- /// clrmd data targets don't return any floating point or larger registers.
- /// </summary>
- /// <param name="threadId">thread id</param>
- /// <param name="index">register index</param>
- /// <param name="value">value returned</param>
- /// <returns>true if value found</returns>
- public bool GetRegisterValue(uint threadId, int index, out ulong value)
- {
- value = 0;
-
- if (_lookupByIndex.TryGetValue(index, out RegisterInfo info))
- {
- byte[] threadContext = GetThreadContext(threadId);
- if (threadContext != null)
- {
- unsafe
- {
- fixed (byte* ptr = threadContext)
- {
- switch (info.RegisterSize)
- {
- case 1:
- value = *((byte*)(ptr + info.RegisterOffset));
- return true;
- case 2:
- value = *((ushort*)(ptr + info.RegisterOffset));
- return true;
- case 4:
- value = *((uint*)(ptr + info.RegisterOffset));
- return true;
- case 8:
- value = *((ulong*)(ptr + info.RegisterOffset));
- return true;
- }
- }
- }
- }
- }
- return false;
- }
-
- /// <summary>
- /// Returns the raw context buffer bytes for the specified thread.
- /// </summary>
- /// <param name="threadId">thread id</param>
- /// <returns>register context or null if error</returns>
- public byte[] GetThreadContext(uint threadId)
- {
- if (_threadContextCache.TryGetValue(threadId, out byte[] threadContext))
- {
- return threadContext;
- }
- else
- {
- unsafe
- {
- threadContext = new byte[_contextSize];
- fixed (byte* ptr = threadContext)
- {
- try
- {
- if (_target.DataReader.GetThreadContext(threadId, _contextFlags, (uint)_contextSize, new IntPtr(ptr)))
- {
- _threadContextCache.Add(threadId, threadContext);
- return threadContext;
- }
- }
- catch (ClrDiagnosticsException ex)
- {
- Trace.TraceError(ex.ToString());
- }
- }
- }
- }
- return null;
- }
- }
-}
\ No newline at end of file
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace Microsoft.Diagnostics.DebugServices
+{
+ /// <summary>
+ /// Details about a native thread
+ /// </summary>
+ public struct ThreadInfo
+ {
+ public readonly int ThreadIndex;
+ public readonly uint ThreadId;
+ public readonly ulong ThreadTeb;
+
+ public ThreadInfo(int index, uint id, ulong teb)
+ {
+ ThreadIndex = index;
+ ThreadId = id;
+ ThreadTeb = teb;
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Diagnostics.Runtime;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using Architecture = Microsoft.Diagnostics.Runtime.Architecture;
+
+namespace Microsoft.Diagnostics.DebugServices
+{
+ /// <summary>
+ /// Provides thread and register info and values for the clrmd IDataReader
+ /// </summary>
+ public class ThreadService : IThreadService
+ {
+ private readonly IDataReader _dataReader;
+ private readonly int _contextSize;
+ private readonly uint _contextFlags;
+ private readonly int _instructionPointerIndex;
+ private readonly int _framePointerIndex;
+ private readonly int _stackPointerIndex;
+ private readonly Dictionary<string, RegisterInfo> _lookupByName;
+ private readonly Dictionary<int, RegisterInfo> _lookupByIndex;
+ private readonly IEnumerable<RegisterInfo> _registers;
+ private readonly Dictionary<uint, byte[]> _threadContextCache = new Dictionary<uint, byte[]>();
+ private IEnumerable<ThreadInfo> _threadInfos;
+
+ public ThreadService(IDataReader dataReader)
+ {
+ _dataReader = dataReader;
+
+ Type contextType;
+ switch (dataReader.GetArchitecture())
+ {
+ case Architecture.Amd64:
+ // Dumps generated with newer dbgeng have bigger context buffers and clrmd requires the context size to at least be that size.
+ _contextSize = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? 0x700 : AMD64Context.Size;
+ _contextFlags = AMD64Context.ContextControl | AMD64Context.ContextInteger | AMD64Context.ContextSegments;
+ contextType = typeof(AMD64Context);
+ break;
+
+ case Architecture.X86:
+ _contextSize = X86Context.Size;
+ _contextFlags = X86Context.ContextControl | X86Context.ContextInteger | X86Context.ContextSegments;
+ contextType = typeof(X86Context);
+ break;
+
+ case Architecture.Arm64:
+ _contextSize = Arm64Context.Size;
+ _contextFlags = Arm64Context.ContextControl | Arm64Context.ContextInteger;
+ contextType = typeof(Arm64Context);
+ break;
+
+ case Architecture.Arm:
+ _contextSize = ArmContext.Size;
+ _contextFlags = ArmContext.ContextControl | ArmContext.ContextInteger;
+ contextType = typeof(ArmContext);
+ break;
+
+ default:
+ throw new PlatformNotSupportedException($"Unsupported architecture: {dataReader.GetArchitecture()}");
+ }
+
+ var registers = new List<RegisterInfo>();
+ int index = 0;
+
+ FieldInfo[] fields = contextType.GetFields(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic);
+ foreach (FieldInfo field in fields) {
+ RegisterAttribute registerAttribute = field.GetCustomAttributes<RegisterAttribute>(inherit: false).SingleOrDefault();
+ if (registerAttribute == null) {
+ continue;
+ }
+ RegisterType registerType = registerAttribute.RegisterType & RegisterType.TypeMask;
+ switch (registerType)
+ {
+ case RegisterType.Control:
+ case RegisterType.General:
+ case RegisterType.Segments:
+ break;
+ default:
+ continue;
+ }
+ if ((registerAttribute.RegisterType & RegisterType.ProgramCounter) != 0) {
+ _instructionPointerIndex = index;
+ }
+ if ((registerAttribute.RegisterType & RegisterType.StackPointer) != 0) {
+ _stackPointerIndex = index;
+ }
+ if ((registerAttribute.RegisterType & RegisterType.FramePointer) != 0) {
+ _framePointerIndex = index;
+ }
+ FieldOffsetAttribute offsetAttribute = field.GetCustomAttributes<FieldOffsetAttribute>(inherit: false).Single();
+ var registerInfo = new RegisterInfo(index, offsetAttribute.Value, Marshal.SizeOf(field.FieldType), registerAttribute.Name ?? field.Name.ToLower());
+ registers.Add(registerInfo);
+ index++;
+ }
+
+ _lookupByName = registers.ToDictionary((info) => info.RegisterName);
+ _lookupByIndex = registers.ToDictionary((info) => info.RegisterIndex);
+ _registers = registers;
+ }
+
+ /// <summary>
+ /// Flush the register service
+ /// </summary>
+ public void Flush()
+ {
+ _threadContextCache.Clear();
+ }
+
+ /// <summary>
+ /// Details on all the supported registers
+ /// </summary>
+ IEnumerable<RegisterInfo> IThreadService.Registers { get { return _registers; } }
+
+ /// <summary>
+ /// The instruction pointer register index
+ /// </summary>
+ int IThreadService.InstructionPointerIndex { get { return _instructionPointerIndex; } }
+
+ /// <summary>
+ /// The frame pointer register index
+ /// </summary>
+ int IThreadService.FramePointerIndex { get { return _framePointerIndex; } }
+
+ /// <summary>
+ /// The stack pointer register index
+ /// </summary>
+ int IThreadService.StackPointerIndex { get { return _stackPointerIndex; } }
+
+ /// <summary>
+ /// Return the register index for the register name
+ /// </summary>
+ /// <param name="name">register name</param>
+ /// <param name="index">returns register index or -1</param>
+ /// <returns>true if name found</returns>
+ bool IThreadService.GetRegisterIndexByName(string name, out int index)
+ {
+ if (_lookupByName.TryGetValue(name, out RegisterInfo info))
+ {
+ index = info.RegisterIndex;
+ return true;
+ }
+ index = int.MaxValue;
+ return false;
+ }
+
+ /// <summary>
+ /// Returns the register info (name, offset, size, etc).
+ /// </summary>
+ /// <param name="index">register index</param>
+ /// <param name="info">RegisterInfo</param>
+ /// <returns>true if index found</returns>
+ bool IThreadService.GetRegisterInfo(int index, out RegisterInfo info)
+ {
+ return _lookupByIndex.TryGetValue(index, out info);
+ }
+
+ /// <summary>
+ /// Returns the register value for the thread and register index. This function
+ /// can only return register values that are 64 bits or less and currently the
+ /// clrmd data targets don't return any floating point or larger registers.
+ /// </summary>
+ /// <param name="threadId">thread id</param>
+ /// <param name="index">register index</param>
+ /// <param name="value">value returned</param>
+ /// <returns>true if value found</returns>
+ bool IThreadService.GetRegisterValue(uint threadId, int index, out ulong value)
+ {
+ value = 0;
+
+ if (_lookupByIndex.TryGetValue(index, out RegisterInfo info))
+ {
+ try
+ {
+ byte[] threadContext = ((IThreadService)this).GetThreadContext(threadId);
+ unsafe
+ {
+ fixed (byte* ptr = threadContext)
+ {
+ switch (info.RegisterSize)
+ {
+ case 1:
+ value = *((byte*)(ptr + info.RegisterOffset));
+ return true;
+ case 2:
+ value = *((ushort*)(ptr + info.RegisterOffset));
+ return true;
+ case 4:
+ value = *((uint*)(ptr + info.RegisterOffset));
+ return true;
+ case 8:
+ value = *((ulong*)(ptr + info.RegisterOffset));
+ return true;
+ }
+ }
+ }
+ }
+ catch (DiagnosticsException)
+ {
+ }
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Returns the raw context buffer bytes for the specified thread.
+ /// </summary>
+ /// <param name="threadId">thread id</param>
+ /// <returns>register context</returns>
+ /// <exception cref="DiagnosticsException">invalid thread id</exception>
+ byte[] IThreadService.GetThreadContext(uint threadId)
+ {
+ if (_threadContextCache.TryGetValue(threadId, out byte[] threadContext))
+ {
+ return threadContext;
+ }
+ else
+ {
+ unsafe
+ {
+ threadContext = new byte[_contextSize];
+ fixed (byte* ptr = threadContext)
+ {
+ try
+ {
+ if (_dataReader.GetThreadContext(threadId, _contextFlags, (uint)_contextSize, new IntPtr(ptr)))
+ {
+ _threadContextCache.Add(threadId, threadContext);
+ return threadContext;
+ }
+ }
+ catch (ClrDiagnosticsException ex)
+ {
+ throw new DiagnosticsException(ex.Message, ex);
+ }
+ }
+ }
+ }
+ throw new DiagnosticsException();
+ }
+
+ /// <summary>
+ /// Enumerate all the native threads
+ /// </summary>
+ /// <returns>ThreadInfos for all the threads</returns>
+ IEnumerable<ThreadInfo> IThreadService.EnumerateThreads()
+ {
+ if (_threadInfos == null)
+ {
+ _threadInfos = _dataReader.EnumerateAllThreads()
+ .OrderBy((uint threadId) => threadId)
+ .Select((uint threadId, int threadIndex) => {
+ ulong teb = 0;
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ try
+ {
+ teb = _dataReader.GetThreadTeb(threadId);
+ }
+ catch (NotImplementedException)
+ {
+ }
+ }
+ return new ThreadInfo(threadIndex, threadId, teb);
+ });
+ }
+ return _threadInfos;
+ }
+
+ /// <summary>
+ /// Get the thread info from the thread index
+ /// </summary>
+ /// <param name="threadIndex">index</param>
+ /// <returns>thread info</returns>
+ /// <exception cref="DiagnosticsException">invalid thread index</exception>
+ ThreadInfo IThreadService.GetThreadInfoFromIndex(int threadIndex)
+ {
+ try
+ {
+ return ((IThreadService)this).EnumerateThreads().ElementAt(threadIndex);
+ }
+ catch (ArgumentOutOfRangeException ex)
+ {
+ throw new DiagnosticsException($"Invalid thread index: {threadIndex}", ex);
+ }
+ }
+
+ /// <summary>
+ /// Get the thread info from the OS thread id
+ /// </summary>
+ /// <param name="threadId">os id</param>
+ /// <returns>thread info</returns>
+ /// <exception cref="DiagnosticsException">invalid thread id</exception>
+ ThreadInfo IThreadService.GetThreadInfoFromId(uint threadId)
+ {
+ try
+ {
+ return ((IThreadService)this).EnumerateThreads().First((ThreadInfo info) => info.ThreadId == threadId);
+ }
+ catch (InvalidOperationException ex)
+ {
+ throw new DiagnosticsException($"Invalid thread id: {threadId}", ex);
+ }
+ }
+ }
+}
builder.AddMethod(new SetCurrentThreadIdDelegate(soshost.SetCurrentThreadId));
builder.AddMethod(new GetCurrentThreadSystemIdDelegate(soshost.GetCurrentThreadSystemId));
builder.AddMethod(new GetThreadIdBySystemIdDelegate(soshost.GetThreadIdBySystemId));
- builder.AddMethod(new GetThreadContextByIdDelegate(GetThreadContextById));
+ builder.AddMethod(new GetThreadContextByIdDelegate(soshost.GetThreadContextById));
builder.AddMethod(new GetValueByNameDelegate(GetValueByName));
builder.AddMethod(new GetInstructionOffsetDelegate(soshost.GetInstructionOffset));
return S_OK;
}
- int GetThreadContextById(
- IntPtr self,
- uint threadId,
- uint contextFlags,
- uint contextSize,
- IntPtr context)
- {
- if (_soshost.DataReader.GetThreadContext(threadId, contextFlags, contextSize, context)) {
- return S_OK;
- }
- return E_FAIL;
- }
-
int GetValueByName(
IntPtr self,
string name,
internal readonly IDataReader DataReader;
internal readonly AnalyzeContext AnalyzeContext;
- private readonly RegisterService _registerService;
+ private readonly IThreadService _threadService;
private readonly MemoryService _memoryService;
private readonly IConsoleService _console;
private readonly COMCallableIUnknown _ccw;
_console = serviceProvider.GetService<IConsoleService>();
AnalyzeContext = serviceProvider.GetService<AnalyzeContext>();
_memoryService = serviceProvider.GetService<MemoryService>();
- _registerService = serviceProvider.GetService<RegisterService>();
+ _threadService = serviceProvider.GetService<IThreadService>();
_versionCache = new ReadVirtualCache(_memoryService);
string rid = InstallHelper.GetRid();
IntPtr context,
uint contextSize)
{
- uint threadId = (uint)AnalyzeContext.CurrentThreadId;
- byte[] registerContext = _registerService.GetThreadContext(threadId);
- if (registerContext == null) {
+ if (!AnalyzeContext.CurrentThreadId.HasValue)
+ {
+ return E_FAIL;
+ }
+ return GetThreadContextById(self, AnalyzeContext.CurrentThreadId.Value, 0, contextSize, context);
+ }
+
+ internal int GetThreadContextById(
+ IntPtr self,
+ uint threadId,
+ uint contextFlags,
+ uint contextSize,
+ IntPtr context)
+ {
+ byte[] registerContext;
+ try
+ {
+ registerContext = _threadService.GetThreadContext(threadId);
+ }
+ catch (DiagnosticsException)
+ {
return E_FAIL;
}
try
IntPtr self,
out uint number)
{
- number = (uint)DataReader.EnumerateAllThreads().Count();
+ number = (uint)_threadService.EnumerateThreads().Count();
return DebugClient.S_OK;
}
out uint total,
out uint largestProcess)
{
- total = (uint)DataReader.EnumerateAllThreads().Count();
+ total = (uint)_threadService.EnumerateThreads().Count();
largestProcess = total;
return DebugClient.S_OK;
}
IntPtr self,
out uint id)
{
- return GetThreadIdBySystemId(self, (uint)AnalyzeContext.CurrentThreadId, out id);
+ if (!AnalyzeContext.CurrentThreadId.HasValue)
+ {
+ id = 0;
+ return E_FAIL;
+ }
+ return GetThreadIdBySystemId(self, AnalyzeContext.CurrentThreadId.Value, out id);
}
internal int SetCurrentThreadId(
{
try
{
- unchecked {
- AnalyzeContext.CurrentThreadId = (int)DataReader.EnumerateAllThreads().ElementAt((int)id);
- }
+ ThreadInfo threadInfo = _threadService.GetThreadInfoFromIndex(unchecked((int)id));
+ AnalyzeContext.CurrentThreadId = threadInfo.ThreadId;
}
- catch (ArgumentOutOfRangeException)
+ catch (InvalidOperationException)
{
return E_FAIL;
}
IntPtr self,
out uint sysId)
{
- sysId = (uint)AnalyzeContext.CurrentThreadId;
+ if (!AnalyzeContext.CurrentThreadId.HasValue)
+ {
+ sysId = 0;
+ return E_FAIL;
+ }
+ sysId = AnalyzeContext.CurrentThreadId.Value;
return S_OK;
}
uint* ids,
uint* sysIds)
{
- uint id = 0;
int index = 0;
- foreach (uint s in DataReader.EnumerateAllThreads())
+ foreach (ThreadInfo threadInfo in _threadService.EnumerateThreads())
{
- if (id >= count) {
+ uint id = (uint)threadInfo.ThreadIndex;
+ if (index >= count) {
break;
}
if (id >= start)
ids[index] = id;
}
if (sysIds != null) {
- sysIds[index] = s;
+ sysIds[index] = threadInfo.ThreadId;
}
index++;
}
- id++;
}
return DebugClient.S_OK;
}
uint sysId,
out uint id)
{
- id = 0;
if (sysId != 0)
{
- foreach (uint s in DataReader.EnumerateAllThreads())
+ try
+ {
+ ThreadInfo threadInfo = _threadService.GetThreadInfoFromId(sysId);
+ id = (uint)threadInfo.ThreadIndex;
+ return S_OK;
+ }
+ catch (InvalidOperationException)
{
- if (s == sysId) {
- return S_OK;
- }
- id++;
}
}
+ id = 0;
return E_FAIL;
}
IntPtr self,
ulong* offset)
{
- uint threadId = (uint)AnalyzeContext.CurrentThreadId;
- ulong teb = DataReader.GetThreadTeb(threadId);
- Write(offset, teb);
- return S_OK;
+ if (!AnalyzeContext.CurrentThreadId.HasValue)
+ {
+ return E_FAIL;
+ }
+ uint threadId = AnalyzeContext.CurrentThreadId.Value;
+ try
+ {
+ ulong teb = _threadService.GetThreadInfoFromId(threadId).ThreadTeb;
+ Write(offset, teb);
+ return S_OK;
+ }
+ catch (InvalidOperationException)
+ {
+ return E_FAIL;
+ }
}
internal int GetInstructionOffset(
IntPtr self,
out ulong offset)
{
- return GetRegister(_registerService.InstructionPointerIndex, out offset);
+ return GetRegister(_threadService.InstructionPointerIndex, out offset);
}
internal int GetStackOffset(
IntPtr self,
out ulong offset)
{
- return GetRegister(_registerService.StackPointerIndex, out offset);
+ return GetRegister(_threadService.StackPointerIndex, out offset);
}
internal int GetFrameOffset(
IntPtr self,
out ulong offset)
{
- return GetRegister(_registerService.FramePointerIndex, out offset);
+ return GetRegister(_threadService.FramePointerIndex, out offset);
}
internal int GetIndexByName(
string name,
out uint index)
{
- if (_registerService.GetRegisterIndexByName(name, out int value)) {
+ if (_threadService.GetRegisterIndexByName(name, out int value)) {
index = 0;
return E_INVALIDARG;
}
out ulong value)
{
value = 0;
- if (!_registerService.GetRegisterIndexByName(register, out int index)) {
+ if (!_threadService.GetRegisterIndexByName(register, out int index)) {
return E_INVALIDARG;
}
return GetRegister(index, out value);
int index,
out ulong value)
{
- uint threadId = (uint)AnalyzeContext.CurrentThreadId;
- if (!_registerService.GetRegisterValue(threadId, index, out value)) {
+ if (!AnalyzeContext.CurrentThreadId.HasValue)
+ {
+ value = 0;
+ return E_FAIL;
+ }
+ uint threadId = AnalyzeContext.CurrentThreadId.Value;
+ if (!_threadService.GetRegisterValue(threadId, index, out value)) {
return E_FAIL;
}
return S_OK;
COMMAND:threads
VERIFY:\s*<DECVAL>\s+0x<HEXVAL>\s+\(<DECVAL>\)\s+
COMMAND:registers
-VERIFY:\s*([r|e]ip|pc) = <HEXVAL>\s+
+VERIFY:\s*([r|e]ip|pc) = 0x<HEXVAL>\s+
ENDIF:DOTNETDUMP
# Issue: https://github.com/dotnet/diagnostics/issues/503
COMMAND:threads
VERIFY:\s*<DECVAL>\s+0x<HEXVAL>\s+\(<DECVAL>\)\s+
COMMAND:registers
-VERIFY:\s*([r|e]ip|pc) = <HEXVAL>\s+
+VERIFY:\s*([r|e]ip|pc) = 0x<HEXVAL>\s+
ENDIF:DOTNETDUMP
# Verify that ClrStack with no options works
// Create common analyze context for commands
var analyzeContext = new AnalyzeContext() {
- CurrentThreadId = unchecked((int)target.DataReader.EnumerateAllThreads().FirstOrDefault())
+ CurrentThreadId = target.DataReader.EnumerateAllThreads().FirstOrDefault()
};
_serviceProvider.AddService(analyzeContext);
- // Add the register, memory, SOSHost and ClrRuntime services
- var registerService = new RegisterService(target);
- _serviceProvider.AddService(registerService);
+ // Add the thread, memory, SOSHost and ClrRuntime services
+ var threadService = new ThreadService(target.DataReader);
+ _serviceProvider.AddService<IThreadService>(threadService);
var memoryService = new MemoryService(target.DataReader);
_serviceProvider.AddService(memoryService);
using Microsoft.Diagnostics.DebugServices;
using Microsoft.Diagnostics.Repl;
-using Microsoft.Diagnostics.Runtime;
-using System;
-using System.Collections.Generic;
-using System.CommandLine;
-using System.Linq;
namespace Microsoft.Diagnostics.Tools.Dump
{
[Command(Name = "registers", Help = "Displays the thread's registers.")]
+ [CommandAlias(Name = "r")]
public class RegistersCommand : CommandBase
{
[Argument(Help = "The thread index to display, otherwise use the current thread.")]
public int? ThreadIndex { get; set; } = null;
- public DataTarget DataTarget { get; set; }
-
public AnalyzeContext AnalyzeContext { get; set; }
- public RegisterService RegisterService { get; set; }
+ public IThreadService ThreadService { get; set; }
public override void Invoke()
{
- IEnumerable<uint> threads = DataTarget.DataReader.EnumerateAllThreads();
uint threadId;
-
if (ThreadIndex.HasValue)
{
- if (ThreadIndex.Value >= threads.Count()) {
- throw new InvalidOperationException($"Invalid thread index {ThreadIndex.Value}");
- }
- threadId = threads.ElementAt(ThreadIndex.Value);
+ threadId = ThreadService.GetThreadInfoFromIndex(ThreadIndex.Value).ThreadId;
}
else
{
- threadId = (uint)AnalyzeContext.CurrentThreadId;
+ threadId = AnalyzeContext.CurrentThreadId.Value;
}
-
- foreach (RegisterService.RegisterInfo register in RegisterService.Registers)
+ foreach (RegisterInfo register in ThreadService.Registers)
{
- if (RegisterService.GetRegisterValue(threadId, register.RegisterIndex, out ulong value))
+ if (ThreadService.GetRegisterValue(threadId, register.RegisterIndex, out ulong value))
{
switch (register.RegisterSize)
{
case 1:
- WriteLine("{0} = {1:X1}", register.RegisterName, value);
+ WriteLine("{0} = 0x{1:X1}", register.RegisterName, value);
break;
case 2:
- WriteLine("{0} = {1:X4}", register.RegisterName, value);
+ WriteLine("{0} = 0x{1:X4}", register.RegisterName, value);
break;
case 4:
- WriteLine("{0} = {1:X8}", register.RegisterName, value);
+ WriteLine("{0} = 0x{1:X8}", register.RegisterName, value);
break;
case 8:
- WriteLine("{0} = {1:X16}", register.RegisterName, value);
+ WriteLine("{0} = 0x{1:X16}", register.RegisterName, value);
break;
}
}
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using Microsoft.Diagnostics.DebugServices;
using Microsoft.Diagnostics.Repl;
using SOS;
using System;
-using System.CommandLine;
using System.IO;
using System.Linq;
[Command(Name = "dumpmodule", AliasExpansion = "DumpModule", Help = "Displays information about a EE module structure at the specified address.")]
[Command(Name = "dumpmt", AliasExpansion = "DumpMT", Help = "Displays information about a method table at the specified address.")]
[Command(Name = "dumpobj", AliasExpansion = "DumpObj", Help = "Displays info about an object at the specified address.")]
+ [CommandAlias(Name = "do")]
[Command(Name = "dumpvc", AliasExpansion = "DumpVC", Help = "Displays info about the fields of a value class.")]
[Command(Name = "dumpstackobjects", AliasExpansion = "DumpStackObjects", Help = "Displays all managed objects found within the bounds of the current stack.")]
[CommandAlias(Name = "dso")]
using Microsoft.Diagnostics.DebugServices;
using Microsoft.Diagnostics.Repl;
-using Microsoft.Diagnostics.Runtime;
-using System;
-using System.Collections.Generic;
-using System.CommandLine;
-using System.Linq;
namespace Microsoft.Diagnostics.Tools.Dump
{
[CommandAlias(Name = "threads")]
public class SetThreadCommand : CommandBase
{
- [Argument(Help = "The thread index to set, otherwise displays the list of threads.")]
- public int? ThreadIndex { get; set; } = null;
+ [Argument(Help = "The thread index or id to set, otherwise displays the list of threads.")]
+ public int? Thread { get; set; } = null;
- public DataTarget DataTarget { get; set; }
+ [Option(Name = "--tid", Help = "<thread> is an OS thread id.")]
+ [OptionAlias(Name = "-t")]
+ public bool ThreadId { get; set; }
public AnalyzeContext AnalyzeContext { get; set; }
+ public IThreadService ThreadService { get; set; }
+
public override void Invoke()
{
- if (ThreadIndex.HasValue)
+ if (Thread.HasValue)
{
- IEnumerable<uint> threads = DataTarget.DataReader.EnumerateAllThreads();
- if (ThreadIndex.Value >= threads.Count()) {
- throw new InvalidOperationException($"Invalid thread index {ThreadIndex.Value}");
+ ThreadInfo threadInfo;
+ if (ThreadId)
+ {
+ threadInfo = ThreadService.GetThreadInfoFromId((uint)Thread.Value);
+ }
+ else
+ {
+ threadInfo = ThreadService.GetThreadInfoFromIndex(Thread.Value);
}
- AnalyzeContext.CurrentThreadId = unchecked((int)threads.ElementAt(ThreadIndex.Value));
+ AnalyzeContext.CurrentThreadId = threadInfo.ThreadId;
}
else
{
- int index = 0;
- foreach (uint threadId in DataTarget.DataReader.EnumerateAllThreads())
+ foreach (ThreadInfo thread in ThreadService.EnumerateThreads())
{
- WriteLine("{0}{1} 0x{2:X4} ({2})", threadId == AnalyzeContext.CurrentThreadId ? "*" : " ", index, threadId);
- index++;
+ WriteLine("{0}{1} 0x{2:X4} ({2})", thread.ThreadId == AnalyzeContext.CurrentThreadId.Value ? "*" : " ", thread.ThreadIndex, thread.ThreadId);
}
}
}