Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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>1.1.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>
<!-- Use pinned version to avoid picking up latest (which doesn't support netcoreapp3.1) during source-build -->
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):x}" : offset.ToString("x");

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 {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