Skip to content
Merged
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
8 changes: 4 additions & 4 deletions eng/Version.Details.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
<Uri>https://github.com/dotnet/symstore</Uri>
<Sha>e09f81a0b38786cb20f66b589a8b88b6997a62da</Sha>
</Dependency>
<Dependency Name="Microsoft.Diagnostics.Runtime" Version="3.0.0-beta.23205.1">
<Dependency Name="Microsoft.Diagnostics.Runtime" Version="3.0.0-beta.23210.1">
<Uri>https://github.com/microsoft/clrmd</Uri>
<Sha>3368bf4451a9441076595022fdff0f2bbea57b1b</Sha>
<Sha>677f1b9c6fa27b1d2988eac9d51c8a5ea8698fdd</Sha>
</Dependency>
<Dependency Name="Microsoft.Diagnostics.Runtime.Utilities" Version="3.0.0-beta.23205.1">
<Dependency Name="Microsoft.Diagnostics.Runtime.Utilities" Version="3.0.0-beta.23210.1">
<Uri>https://github.com/microsoft/clrmd</Uri>
<Sha>3368bf4451a9441076595022fdff0f2bbea57b1b</Sha>
<Sha>677f1b9c6fa27b1d2988eac9d51c8a5ea8698fdd</Sha>
</Dependency>
</ProductDependencies>
<ToolsetDependencies>
Expand Down
2 changes: 1 addition & 1 deletion eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
<SystemReflectionMetadataVersion>5.0.0</SystemReflectionMetadataVersion>
<!-- Other libs -->
<MicrosoftBclAsyncInterfacesVersion>6.0.0</MicrosoftBclAsyncInterfacesVersion>
<MicrosoftDiagnosticsRuntimeVersion>3.0.0-beta.23205.1</MicrosoftDiagnosticsRuntimeVersion>
<MicrosoftDiagnosticsRuntimeVersion>3.0.0-beta.23210.1</MicrosoftDiagnosticsRuntimeVersion>
<MicrosoftDiaSymReaderNativePackageVersion>16.9.0-beta1.21055.5</MicrosoftDiaSymReaderNativePackageVersion>
<MicrosoftDiagnosticsTracingTraceEventVersion>3.0.7</MicrosoftDiagnosticsTracingTraceEventVersion>
<MicrosoftExtensionsLoggingVersion>6.0.0</MicrosoftExtensionsLoggingVersion>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Linq;
using System.Text;
using Microsoft.Diagnostics.DebugServices;
using Microsoft.Diagnostics.Runtime;
using static Microsoft.Diagnostics.ExtensionCommands.TableOutput;

namespace Microsoft.Diagnostics.ExtensionCommands
{
[Command(Name = "dumpobjgcrefs", Help = "A helper command to implement !dumpobj -refs")]
public sealed class DumpObjGCRefsHelper : CommandBase
{
[ServiceImport]
public ClrRuntime Runtime { get; set; }

[Argument(Name = "object")]
public string ObjectAddress { get; set; }

public override void Invoke()
{
if (!TryParseAddress(ObjectAddress, out ulong objAddress))
{
throw new ArgumentException($"Invalid object address: '{ObjectAddress}'", nameof(ObjectAddress));
}

ClrObject obj = Runtime.Heap.GetObject(objAddress);
if (!obj.IsValid)
{
Console.WriteLine($"Unable to walk object references, invalid object.");
return;
}

ClrReference[] refs = obj.EnumerateReferencesWithFields(carefully: false, considerDependantHandles: false).ToArray();
if (refs.Length == 0 )
{
Console.WriteLine("GC Refs: none");
return;
}

Console.WriteLine("GC Refs:");

int fieldNameLen = Math.Max(refs.Max(r => GetFieldName(r)?.Length ?? 0), 5);
int offsetLen = Math.Max(refs.Max(r => r.Offset.ToSignedHexString().Length), 6);

TableOutput output = new(Console, (fieldNameLen, ""), (offsetLen, ""), (16, "x12"));
output.WriteRow("Field", "Offset", "Object", "Type");
foreach (ClrReference objRef in refs)
{
output.WriteRow(GetFieldName(objRef), objRef.Offset, new DmlDumpObj(objRef.Object), objRef.Object.Type?.Name);
}
}

private static string GetFieldName(ClrReference objRef)
{
if (objRef.Field is null)
{
return null;
}

if (objRef.InnerField is null)
{
return objRef.Field?.Name;
}

StringBuilder sb = new(260);
bool foundOneFieldName = false;

for (ClrReference? curr = objRef; curr.HasValue; curr = curr.Value.InnerField)
{
if (sb.Length > 0)
{
sb.Append('.');
}

string fieldName = curr.Value.Field?.Name;
if (string.IsNullOrWhiteSpace(fieldName))
{
sb.Append("???");
}
else
{
sb.Append(fieldName);
foundOneFieldName = true;
}
}

// Make sure we don't just return "???.???.???"
return foundOneFieldName ? sb.ToString() : null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.Diagnostics.DebugServices;
using Microsoft.Diagnostics.Runtime;
using static Microsoft.Diagnostics.ExtensionCommands.TableOutput;

namespace Microsoft.Diagnostics.ExtensionCommands
{
[Command(Name = "dumpruntimetypes", Help = "Finds all System.RuntimeType objects in the GC heap and prints the type name and MethodTable they refer too.")]
public sealed class DumpRuntimeTypeCommand : CommandBase
{
[ServiceImport]
public ClrRuntime Runtime { get; set; }

public override void Invoke()
{
TableOutput output = null;

foreach (ClrObject runtimeType in Runtime.Heap.EnumerateObjects())
{
Console.CancellationToken.ThrowIfCancellationRequested();
if (!runtimeType.IsValid || !runtimeType.IsRuntimeType)
{
continue;
}

if (!runtimeType.TryReadField("m_handle", out nuint m_handle))
{
continue;
}

ClrAppDomain domain = null;
string typeName;
bool isMethodTable = (m_handle & 2) == 0;
if (isMethodTable)
{
// Only lookup the type if we have a MethodTable.
ClrType type = Runtime.GetTypeByMethodTable(m_handle);
typeName = type?.Name ?? $"methodtable: {m_handle:x}";
domain = type?.Module?.AppDomain;
}
else
{
typeName = $"typehandle: {m_handle:x} (SOS does not support resolving typehandle names.)";
}

if (output is null)
{
output = new(Console, (16, "x12"), (16, "x12"), (16, "x12"));
output.WriteRow("Address", "Domain", "MT", "Type Name");
}

output.WriteRow(new DmlDumpObj(runtimeType.Address),
domain is not null ? new DmlDumpDomain(domain.Address) : null,
isMethodTable ? new DmlDumpMT(m_handle) : m_handle,
typeName);
}

if (output is null)
{
Console.WriteLine("No System.RuntimeType objects found.");
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Diagnostics.Runtime;
Expand Down Expand Up @@ -32,6 +33,8 @@ public static string ConvertToHumanReadable(this double totalBytes)
return $"{updated:0.00}gb";
}

public static string ToSignedHexString(this int offset) => offset < 0 ? $"-{Math.Abs(offset):x2}" : offset.ToString("x2");

internal static ulong FindMostCommonPointer(this IEnumerable<ulong> enumerable)
=> (from ptr in enumerable
group ptr by ptr into g
Expand Down
17 changes: 17 additions & 0 deletions src/Microsoft.Diagnostics.ExtensionCommands/TableOutput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ private static string Format(object obj, string format)
long l => l.ToString(format),
uint ui => ui.ToString(format),
int i => i.ToString(format),
nuint uni => ((ulong)uni).ToString(format),
StringBuilder sb => sb.ToString(),
IEnumerable<byte> bytes => string.Join("", bytes.Select(b => b.ToString("x2"))),
string s => s,
Expand Down Expand Up @@ -246,6 +247,22 @@ public DmlDumpObj(ulong address)
}
}

public sealed class DmlDumpMT : DmlExec
{
public DmlDumpMT(ulong address)
: base(address, address != 0 ? $"!dumpmt /d {address:x}" : "")
{
}
}

public sealed class DmlDumpDomain : DmlExec
{
public DmlDumpDomain(ulong address)
: base(address, address != 0 ? $"!dumpdomain /d {address:x}" : "")
{
}
}

public sealed class DmlListNearObj : DmlExec
{
public DmlListNearObj(ulong address)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ internal static string GetObjectCorruptionMessage(IMemoryService memory, ClrHeap
ObjectCorruptionKind.SyncBlockZero => GetSyncBlockFailureMessage(corruption),

// Object reference failures
ObjectCorruptionKind.ObjectReferenceNotPointerAligned => $"Object {obj.Address:x} has an unaligned member at {corruption.Offset:x}: is not pointer aligned",
ObjectCorruptionKind.ObjectReferenceNotPointerAligned => $"Object {obj.Address:x} has an unaligned member at offset {corruption.Offset:x}: is not pointer aligned",
ObjectCorruptionKind.InvalidObjectReference => $"Object {obj.Address:x} has a bad member at offset {corruption.Offset:x}: {ReadPointerWithError(memory, obj + (uint)corruption.Offset)}",
ObjectCorruptionKind.FreeObjectReference => $"Object {obj.Address:x} contains free object at offset {corruption.Offset:x}: {ReadPointerWithError(memory, obj + (uint)corruption.Offset)}",

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Diagnostics.DebugServices;
using Microsoft.Diagnostics.Runtime;

namespace Microsoft.Diagnostics.ExtensionCommands
{
[Command(Name = "verifyobj", Help = "Checks the given object for signs of corruption.")]
public sealed class VerifyObjectCommand : CommandBase
{
[ServiceImport]
public ClrRuntime Runtime { get; set; }

[ServiceImport]
public IMemoryService Memory { get; set; }

[Argument(Name = "ObjectAddress", Help = "The object to verify.")]
public string ObjectAddress { get; set; }

public override void Invoke()
{
if (!TryParseAddress(ObjectAddress, out ulong objAddress))
{
throw new ArgumentException($"Invalid object address: '{ObjectAddress}'", nameof(ObjectAddress));
}

bool isNotCorrupt = Runtime.Heap.FullyVerifyObject(objAddress, out IEnumerable<ObjectCorruption> corruptionEnum);
if (isNotCorrupt)
{
Console.WriteLine($"object 0x{objAddress:x} is a valid object");
return;
}

ObjectCorruption[] corruption = corruptionEnum.OrderBy(r => r.Offset).ToArray();
int offsetColWidth = Math.Max(6, corruption.Max(r => r.Offset.ToSignedHexString().Length));
int kindColWidth = Math.Max(5, corruption.Max(ce => ce.Kind.ToString().Length));

TableOutput output = new(Console, (offsetColWidth, ""), (kindColWidth, ""))
{
AlignLeft = true,
};

output.WriteRow("Offset", "Issue", "Description");
foreach (ObjectCorruption oc in corruption)
{
output.WriteRow(oc.Offset.ToSignedHexString(), oc.Kind, VerifyHeapCommand.GetObjectCorruptionMessage(Memory, Runtime.Heap, oc));
}

Console.WriteLine();
Console.WriteLine($"{corruption.Length:n0} error{(corruption.Length == 1 ? "" : "s")} detected.");
}
}
}
2 changes: 0 additions & 2 deletions src/SOS/SOS.Hosting/Commands/SOSCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ namespace SOS.Hosting
[Command(Name = "dumpmodule", DefaultOptions = "DumpModule", Help = "Displays information about a EE module structure at the specified address.")]
[Command(Name = "dumpmt", DefaultOptions = "DumpMT", Help = "Displays information about a method table at the specified address.")]
[Command(Name = "dumpobj", DefaultOptions = "DumpObj", Aliases = new string[] { "do" }, Help = "Displays info about an object at the specified address.")]
[Command(Name = "dumpruntimetypes", DefaultOptions = "DumpRuntimeTypes", Help = "Finds all System.RuntimeType objects in the GC heap and prints the type name and MethodTable they refer too.")]
[Command(Name = "dumpsig", DefaultOptions = "DumpSig", Help = "Dumps the signature of a method or field specified by <sigaddr> <moduleaddr>.")]
[Command(Name = "dumpsigelem", DefaultOptions = "DumpSigElem", Help = "Dumps a single element of a signature object.")]
[Command(Name = "dumpstackobjects", DefaultOptions = "DumpStackObjects", Aliases = new string[] { "dso" }, Help = "Displays all managed objects found within the bounds of the current stack.")]
Expand All @@ -50,7 +49,6 @@ namespace SOS.Hosting
[Command(Name = "syncblk", DefaultOptions = "SyncBlk", Help = "Displays the SyncBlock holder info.")]
[Command(Name = "threadpool", DefaultOptions = "ThreadPool", Help = "Lists basic information about the thread pool.")]
[Command(Name = "threadstate", DefaultOptions = "ThreadState", Help = "Pretty prints the meaning of a threads state.")]
[Command(Name = "verifyobj", DefaultOptions = "VerifyObj", Help = "Checks the object for signs of corruption.")]
[Command(Name = "comstate", DefaultOptions = "COMState", Flags = CommandFlags.Windows, Help = "Lists the COM apartment model for each thread.")]
[Command(Name = "dumprcw", DefaultOptions = "DumpRCW", Flags = CommandFlags.Windows, Help = "Displays information about a Runtime Callable Wrapper.")]
[Command(Name = "dumpccw", DefaultOptions = "DumpCCW", Flags = CommandFlags.Windows, Help = "Displays information about a COM Callable Wrapper.")]
Expand Down
8 changes: 4 additions & 4 deletions src/SOS/SOS.UnitTests/Scripts/GCTests.script
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ VERIFY:\s+Fields:\s+
VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+<HEXVAL>\s+System\.String.*_string\s+
VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+<HEXVAL>\s+System\.UInt64.*52704621242434 _static\s+
VERIFY:\s+GC Refs:\s+
VERIFY:\s+offset\s+object\s+
VERIFY:\s+<DECVAL>\s+<HEXVAL>\s+
VERIFY:\s+Field\s+Offset\s+Object\s+Type\s+
VERIFY:.*\s+<HEXVAL>\s+<HEXVAL>.*

SOSCOMMAND:DumpHeap
VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+<DECVAL>\s+
Expand Down Expand Up @@ -141,8 +141,8 @@ VERIFY:\s+Fields:\s+
VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+<HEXVAL>\s+System\.String.*_string\s+
VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+<HEXVAL>\s+System\.UInt64.*52704621242434 _static\s+
VERIFY:\s+GC Refs:\s+
VERIFY:\s+offset\s+object\s+
VERIFY:\s+<DECVAL>\s+<HEXVAL>\s+
VERIFY:\s+Field\s+Offset\s+Object\s+Type\s+
VERIFY:.*\s+<HEXVAL>\s+<HEXVAL>.*

# Continue to the next DebugBreak
CONTINUE
Expand Down
Loading