<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>
<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>
--- /dev/null
+// 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;
+ }
+ }
+}
--- /dev/null
+// 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.");
+ }
+ }
+ }
+}
// 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;
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
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,
}
}
+ 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)
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)}",
--- /dev/null
+// 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.");
+ }
+ }
+}
[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.")]
[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.")]
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+
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
}
}
-BOOL GCHeapTraverse(const GCHeapDetails &heap, AllocInfo* pallocInfo, VISITGCHEAPFUNC pFunc, LPVOID token, BOOL verify)
-{
- DWORD_PTR dwAddrSeg = 0;
- DWORD_PTR dwAddr = 0;
- DWORD_PTR dwAddrCurrObj = 0;
- DWORD_PTR dwAddrPrevObj = 0;
- size_t s, sPrev = 0;
-
- DacpHeapSegmentData segment;
- if (heap.has_regions)
- {
- BOOL bPrevFree = FALSE;
- for (UINT n = 0; n <= GetMaxGeneration(); n++)
- {
- dwAddrSeg = (DWORD_PTR)heap.generation_table[n].start_segment;
- while (dwAddrSeg != 0)
- {
- if (IsInterrupt())
- {
- ExtOut("<heap walk interrupted>\n");
- return FALSE;
- }
- if (segment.Request(g_sos, dwAddrSeg, heap.original_heap_details) != S_OK)
- {
- ExtOut("Error requesting heap segment %p\n", SOS_PTR(dwAddrSeg));
- return FALSE;
- }
- dwAddrCurrObj = (DWORD_PTR)segment.mem;
- DWORD_PTR end_of_segment = (DWORD_PTR)segment.highAllocMark;
-
- while (true)
- {
- if (dwAddrCurrObj - SIZEOF_OBJHEADER == end_of_segment - Align(min_obj_size))
- break;
-
- if (dwAddrCurrObj >= (DWORD_PTR)end_of_segment)
- {
- if (dwAddrCurrObj > (DWORD_PTR)end_of_segment)
- {
- ExtOut("curr_object: %p > heap_segment_allocated (seg: %p)\n",
- SOS_PTR(dwAddrCurrObj), SOS_PTR(dwAddrSeg));
- if (dwAddrPrevObj)
- {
- ExtOut("Last good object: %p\n", SOS_PTR(dwAddrPrevObj));
- }
- return FALSE;
- }
- // done with this segment
- break;
- }
-
- if (dwAddrSeg == (DWORD_PTR)heap.ephemeral_heap_segment
- && dwAddrCurrObj >= end_of_segment)
- {
- if (dwAddrCurrObj > end_of_segment)
- {
- // prev_object length is too long
- ExtOut("curr_object: %p > end_of_segment: %p\n",
- SOS_PTR(dwAddrCurrObj), SOS_PTR(end_of_segment));
- if (dwAddrPrevObj)
- {
- DMLOut("Last good object: %s\n", DMLObject(dwAddrPrevObj));
- }
- return FALSE;
- }
- return FALSE;
- }
-
- DWORD_PTR dwAddrMethTable = 0;
- if (FAILED(GetMTOfObject(dwAddrCurrObj, &dwAddrMethTable)))
- {
- return FALSE;
- }
-
- dwAddrMethTable = dwAddrMethTable & ~sos::Object::METHODTABLE_PTR_LOW_BITMASK;
- if (dwAddrMethTable == 0)
- {
- // Is this the beginning of an allocation context?
- int i;
- for (i = 0; i < pallocInfo->num; i++)
- {
- if (dwAddrCurrObj == (DWORD_PTR)pallocInfo->array[i].alloc_ptr)
- {
- dwAddrCurrObj =
- (DWORD_PTR)pallocInfo->array[i].alloc_limit + Align(min_obj_size);
- break;
- }
- }
- if (i < pallocInfo->num)
- continue;
-
- // We also need to look at the gen0 alloc context.
- if (dwAddrCurrObj == (DWORD_PTR)heap.generation_table[0].allocContextPtr)
- {
- dwAddrCurrObj = (DWORD_PTR)heap.generation_table[0].allocContextLimit + Align(min_obj_size);
- continue;
- }
- }
-
- BOOL bContainsPointers;
- size_t s;
- BOOL bMTOk = GetSizeEfficient(dwAddrCurrObj, dwAddrMethTable, FALSE, s, bContainsPointers);
- if (verify && bMTOk)
- bMTOk = VerifyObject(heap, dwAddrCurrObj, dwAddrMethTable, s, TRUE);
- if (!bMTOk)
- {
- DMLOut("curr_object: %s\n", DMLListNearObj(dwAddrCurrObj));
- if (dwAddrPrevObj)
- DMLOut("Last good object: %s\n", DMLObject(dwAddrPrevObj));
-
- ExtOut("----------------\n");
- return FALSE;
- }
-
- pFunc(dwAddrCurrObj, s, dwAddrMethTable, token);
-
- // We believe we did this alignment in ObjectSize above.
- assert((s & ALIGNCONST) == 0);
- dwAddrPrevObj = dwAddrCurrObj;
- sPrev = s;
- bPrevFree = IsMTForFreeObj(dwAddrMethTable);
-
- dwAddrCurrObj += s;
- }
- dwAddrSeg = (DWORD_PTR)segment.next;
- }
- }
- }
- else
- {
- DWORD_PTR begin_youngest;
- DWORD_PTR end_youngest;
-
- begin_youngest = (DWORD_PTR)heap.generation_table[0].allocation_start;
- dwAddr = (DWORD_PTR)heap.ephemeral_heap_segment;
-
- end_youngest = (DWORD_PTR)heap.alloc_allocated;
-
- dwAddrSeg = (DWORD_PTR)heap.generation_table[GetMaxGeneration()].start_segment;
- dwAddr = dwAddrSeg;
-
- if (segment.Request(g_sos, dwAddr, heap.original_heap_details) != S_OK)
- {
- ExtOut("Error requesting heap segment %p\n", SOS_PTR(dwAddr));
- return FALSE;
- }
-
- // DWORD_PTR dwAddrCurrObj = (DWORD_PTR)heap.generation_table[GetMaxGeneration()].allocation_start;
- dwAddrCurrObj = (DWORD_PTR)segment.mem;
-
- BOOL bPrevFree = FALSE;
-
- while (1)
- {
- if (IsInterrupt())
- {
- ExtOut("<heap walk interrupted>\n");
- return FALSE;
- }
- DWORD_PTR end_of_segment = (DWORD_PTR)segment.allocated;
- if (dwAddrSeg == (DWORD_PTR)heap.ephemeral_heap_segment)
- {
- end_of_segment = end_youngest;
- if (dwAddrCurrObj - SIZEOF_OBJHEADER == end_youngest - Align(min_obj_size))
- break;
- }
- if (dwAddrCurrObj >= (DWORD_PTR)end_of_segment)
- {
- if (dwAddrCurrObj > (DWORD_PTR)end_of_segment)
- {
- ExtOut ("curr_object: %p > heap_segment_allocated (seg: %p)\n",
- SOS_PTR(dwAddrCurrObj), SOS_PTR(dwAddrSeg));
- if (dwAddrPrevObj) {
- ExtOut ("Last good object: %p\n", SOS_PTR(dwAddrPrevObj));
- }
- return FALSE;
- }
- dwAddrSeg = (DWORD_PTR)segment.next;
- if (dwAddrSeg)
- {
- dwAddr = dwAddrSeg;
- if (segment.Request(g_sos, dwAddr, heap.original_heap_details) != S_OK)
- {
- ExtOut("Error requesting heap segment %p\n", SOS_PTR(dwAddr));
- return FALSE;
- }
- dwAddrCurrObj = (DWORD_PTR)segment.mem;
- continue;
- }
- else
- {
- break; // Done Verifying Heap
- }
- }
-
- if (dwAddrSeg == (DWORD_PTR)heap.ephemeral_heap_segment
- && dwAddrCurrObj >= end_youngest)
- {
- if (dwAddrCurrObj > end_youngest)
- {
- // prev_object length is too long
- ExtOut("curr_object: %p > end_youngest: %p\n",
- SOS_PTR(dwAddrCurrObj), SOS_PTR(end_youngest));
- if (dwAddrPrevObj) {
- DMLOut("Last good object: %s\n", DMLObject(dwAddrPrevObj));
- }
- return FALSE;
- }
- return FALSE;
- }
-
- DWORD_PTR dwAddrMethTable = 0;
- if (FAILED(GetMTOfObject(dwAddrCurrObj, &dwAddrMethTable)))
- {
- return FALSE;
- }
-
- dwAddrMethTable = dwAddrMethTable & ~sos::Object::METHODTABLE_PTR_LOW_BITMASK;
- if (dwAddrMethTable == 0)
- {
- // Is this the beginning of an allocation context?
- int i;
- for (i = 0; i < pallocInfo->num; i ++)
- {
- if (dwAddrCurrObj == (DWORD_PTR)pallocInfo->array[i].alloc_ptr)
- {
- dwAddrCurrObj =
- (DWORD_PTR)pallocInfo->array[i].alloc_limit + Align(min_obj_size);
- break;
- }
- }
- if (i < pallocInfo->num)
- continue;
-
- // We also need to look at the gen0 alloc context.
- if (dwAddrCurrObj == (DWORD_PTR) heap.generation_table[0].allocContextPtr)
- {
- dwAddrCurrObj = (DWORD_PTR) heap.generation_table[0].allocContextLimit + Align(min_obj_size);
- continue;
- }
- }
-
- BOOL bContainsPointers;
- BOOL bMTOk = GetSizeEfficient(dwAddrCurrObj, dwAddrMethTable, FALSE, s, bContainsPointers);
- if (verify && bMTOk)
- bMTOk = VerifyObject (heap, dwAddrCurrObj, dwAddrMethTable, s, TRUE);
- if (!bMTOk)
- {
- DMLOut("curr_object: %s\n", DMLListNearObj(dwAddrCurrObj));
- if (dwAddrPrevObj)
- DMLOut("Last good object: %s\n", DMLObject(dwAddrPrevObj));
-
- ExtOut ("----------------\n");
- return FALSE;
- }
-
- pFunc (dwAddrCurrObj, s, dwAddrMethTable, token);
-
- // We believe we did this alignment in ObjectSize above.
- assert((s & ALIGNCONST) == 0);
- dwAddrPrevObj = dwAddrCurrObj;
- sPrev = s;
- bPrevFree = IsMTForFreeObj(dwAddrMethTable);
-
- dwAddrCurrObj += s;
- }
- }
-
- // Now for the large object and pinned object generations:
-
- BOOL bPinnedDone = FALSE;
-
- dwAddrSeg = (DWORD_PTR)heap.generation_table[GetMaxGeneration()+1].start_segment;
- dwAddr = dwAddrSeg;
-
- if (segment.Request(g_sos, dwAddr, heap.original_heap_details) != S_OK)
- {
- ExtOut("Error requesting heap segment %p\n", SOS_PTR(dwAddr));
- return FALSE;
- }
-
- // dwAddrCurrObj = (DWORD_PTR)heap.generation_table[GetMaxGeneration()+1].allocation_start;
- dwAddrCurrObj = (DWORD_PTR)segment.mem;
-
- dwAddrPrevObj=0;
-
- while(1)
- {
- if (IsInterrupt())
- {
- ExtOut("<heap traverse interrupted>\n");
- return FALSE;
- }
-
- DWORD_PTR end_of_segment = (DWORD_PTR)segment.allocated;
-
- if (dwAddrCurrObj >= (DWORD_PTR)end_of_segment)
- {
- if (dwAddrCurrObj > (DWORD_PTR)end_of_segment)
- {
- ExtOut("curr_object: %p > heap_segment_allocated (seg: %p)\n",
- SOS_PTR(dwAddrCurrObj), SOS_PTR(dwAddrSeg));
- if (dwAddrPrevObj) {
- ExtOut("Last good object: %p\n", SOS_PTR(dwAddrPrevObj));
- }
- return FALSE;
- }
-
- dwAddrSeg = (DWORD_PTR)segment.next;
- if (dwAddrSeg)
- {
- dwAddr = dwAddrSeg;
- if (segment.Request(g_sos, dwAddr, heap.original_heap_details) != S_OK)
- {
- ExtOut("Error requesting heap segment %p\n", SOS_PTR(dwAddr));
- return FALSE;
- }
- dwAddrCurrObj = (DWORD_PTR)segment.mem;
- continue;
- }
- else if (heap.has_poh && !bPinnedDone)
- {
- bPinnedDone = TRUE;
- dwAddrSeg = (DWORD_PTR)heap.generation_table[GetMaxGeneration() + 2].start_segment;
- dwAddr = dwAddrSeg;
-
- if (segment.Request(g_sos, dwAddr, heap.original_heap_details) != S_OK)
- {
- ExtOut("Error requesting heap segment %p\n", SOS_PTR(dwAddr));
- return FALSE;
- }
-
- dwAddrCurrObj = (DWORD_PTR)segment.mem;
- continue;
- }
- else
- {
- break; // Done Verifying Heap
- }
- }
-
- DWORD_PTR dwAddrMethTable = 0;
- if (FAILED(GetMTOfObject(dwAddrCurrObj, &dwAddrMethTable)))
- {
- return FALSE;
- }
-
- dwAddrMethTable = dwAddrMethTable & ~sos::Object::METHODTABLE_PTR_LOW_BITMASK;
- BOOL bContainsPointers;
- BOOL bMTOk = GetSizeEfficient(dwAddrCurrObj, dwAddrMethTable, TRUE, s, bContainsPointers);
- if (verify && bMTOk)
- bMTOk = VerifyObject (heap, dwAddrCurrObj, dwAddrMethTable, s, TRUE);
- if (!bMTOk)
- {
- DMLOut("curr_object: %s\n", DMLListNearObj(dwAddrCurrObj));
-
- if (dwAddrPrevObj)
- DMLOut("Last good object: %s\n", dwAddrPrevObj);
-
- ExtOut ("----------------\n");
- return FALSE;
- }
-
- pFunc (dwAddrCurrObj, s, dwAddrMethTable, token);
-
- // We believe we did this alignment in ObjectSize above.
- assert((s & ALIGNCONSTLARGE) == 0);
- dwAddrPrevObj = dwAddrCurrObj;
- dwAddrCurrObj += s;
- }
-
- return TRUE;
-}
-
-BOOL GCHeapsTraverse(VISITGCHEAPFUNC pFunc, LPVOID token, BOOL verify)
-{
- // Obtain allocation context for each managed thread.
- AllocInfo allocInfo;
- allocInfo.Init();
-
- if (!IsServerBuild())
- {
- DacpGcHeapDetails dacHeapDetails;
- if (dacHeapDetails.Request(g_sos) != S_OK)
- {
- ExtOut("Error requesting gc heap details\n");
- return FALSE;
- }
-
- GCHeapDetails heapDetails(dacHeapDetails);
- return GCHeapTraverse (heapDetails, &allocInfo, pFunc, token, verify);
- }
- else
- {
- DacpGcHeapData gcheap;
- if (gcheap.Request(g_sos) != S_OK)
- {
- ExtOut("Error requesting GC Heap data\n");
- return FALSE;
- }
-
- DWORD dwAllocSize;
- DWORD dwNHeaps = gcheap.HeapCount;
- if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
- {
- ExtOut("Failed to get GCHeaps: integer overflow error\n");
- return FALSE;
- }
- CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
- if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
- {
- ExtOut("Failed to get GCHeaps\n");
- return FALSE;
- }
-
- DWORD n;
- for (n = 0; n < dwNHeaps; n ++)
- {
- DacpGcHeapDetails dacHeapDetails;
- if (dacHeapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
- {
- ExtOut("Error requesting details\n");
- return FALSE;
- }
-
- GCHeapDetails heapDetails(dacHeapDetails, heapAddrs[n]);
- if (!GCHeapTraverse (heapDetails, &allocInfo, pFunc, token, verify))
- {
- ExtOut("Traversing a gc heap failed\n");
- return FALSE;
- }
- }
- }
-
- return TRUE;
-}
-
GCHeapSnapshot::GCHeapSnapshot()
{
m_isBuilt = FALSE;
return pos;
}
-
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// Some defines for cards taken from gc code
-//
-#define card_word_width ((size_t)32)
-
-//
-// The value of card_size is determined empirically according to the average size of an object
-// In the code we also rely on the assumption that one card_table entry (DWORD) covers an entire os page
-//
-#if defined (_TARGET_WIN64_)
-#define card_size ((size_t)(2*DT_GC_PAGE_SIZE/card_word_width))
-#else
-#define card_size ((size_t)(DT_GC_PAGE_SIZE/card_word_width))
-#endif //_TARGET_WIN64_
-
-// so card_size = 128 on x86, 256 on x64
-
-inline
-size_t card_word (size_t card)
-{
- return card / card_word_width;
-}
-
-inline
-unsigned card_bit (size_t card)
-{
- return (unsigned)(card % card_word_width);
-}
-
-inline
-size_t card_of ( BYTE* object)
-{
- return (size_t)(object) / card_size;
-}
-
-BOOL CardIsSet(const GCHeapDetails &heap, TADDR objAddr)
-{
- // The card table has to be translated to look at the refcount, etc.
- // g_card_table[card_word(card_of(g_lowest_address))].
-
- TADDR card_table = TO_TADDR(heap.card_table);
- card_table = card_table + card_word(card_of((BYTE *)heap.lowest_address))*sizeof(DWORD);
-
- do
- {
- TADDR card_table_lowest_addr;
- TADDR card_table_next;
-
- if (MOVE(card_table_lowest_addr, ALIGN_DOWN(card_table, 0x1000) + sizeof(PVOID)) != S_OK)
- {
- ExtErr("Error getting card table lowest address\n");
- return FALSE;
- }
-
- if (MOVE(card_table_next, card_table - sizeof(PVOID)) != S_OK)
- {
- ExtErr("Error getting next card table\n");
- return FALSE;
- }
-
- size_t card = (objAddr - card_table_lowest_addr) / card_size;
- TADDR card_addr = card_table + (card_word(card) * sizeof(DWORD));
- DWORD value;
- if (MOVE(value, card_addr) != S_OK)
- {
- ExtErr("Error reading card bits - obj %p card %08x card_addr %p card_table %p\n", objAddr, card, card_addr, card_table);
- return FALSE;
- }
-
- if (value & 1<<card_bit(card))
- return TRUE;
-
- card_table = card_table_next;
- }
- while(card_table);
-
- return FALSE;
-}
-
-BOOL NeedCard(TADDR parent, TADDR child)
-{
- int iChildGen = g_snapshot.GetGeneration(child);
-
- if (iChildGen == 2)
- return FALSE;
-
- int iParentGen = g_snapshot.GetGeneration(parent);
-
- return (iChildGen < iParentGen);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// Some defines for mark_array taken from gc code
-//
-
-#define mark_bit_pitch 8
-#define mark_word_width 32
-#define mark_word_size (mark_word_width * mark_bit_pitch)
-#define heap_segment_flags_swept 16
-
-inline
-size_t mark_bit_bit_of(CLRDATA_ADDRESS add)
-{
- return (size_t)((add / mark_bit_pitch) % mark_word_width);
-}
-
-inline
-size_t mark_word_of(CLRDATA_ADDRESS add)
-{
- return (size_t)(add / mark_word_size);
-}
-
-inline BOOL mark_array_marked(const GCHeapDetails &heap, CLRDATA_ADDRESS add)
-{
-
- DWORD entry = 0;
- HRESULT hr = MOVE(entry, heap.mark_array + sizeof(DWORD) * mark_word_of(add));
-
- if (FAILED(hr))
- ExtOut("Failed to read card table entry.\n");
-
- return entry & (1 << mark_bit_bit_of(add));
-}
-
-BOOL background_object_marked(const GCHeapDetails &heap, CLRDATA_ADDRESS o)
-{
- BOOL m = TRUE;
-
- if ((o >= heap.background_saved_lowest_address) && (o < heap.background_saved_highest_address))
- m = mark_array_marked(heap, o);
-
- return m;
-}
-
-BOOL fgc_should_consider_object(const GCHeapDetails &heap,
- CLRDATA_ADDRESS o,
- const DacpHeapSegmentData &seg,
- BOOL consider_bgc_mark_p,
- BOOL check_current_sweep_p,
- BOOL check_saved_sweep_p)
-{
- // the logic for this function must be kept in sync with the analogous function in gc.cpp
- BOOL no_bgc_mark_p = FALSE;
-
- if (consider_bgc_mark_p)
- {
- if (check_current_sweep_p && (o < heap.next_sweep_obj))
- {
- no_bgc_mark_p = TRUE;
- }
-
- if (!no_bgc_mark_p)
- {
- if(check_saved_sweep_p && (o >= heap.saved_sweep_ephemeral_start))
- {
- no_bgc_mark_p = TRUE;
- }
-
- if (!check_saved_sweep_p)
- {
- CLRDATA_ADDRESS background_allocated = seg.background_allocated;
- if (o >= background_allocated)
- {
- no_bgc_mark_p = TRUE;
- }
- }
- }
- }
- else
- {
- no_bgc_mark_p = TRUE;
- }
-
- return no_bgc_mark_p ? TRUE : background_object_marked(heap, o);
-}
-
-enum c_gc_state
-{
- c_gc_state_marking,
- c_gc_state_planning,
- c_gc_state_free
-};
-
-inline BOOL in_range_for_segment(const DacpHeapSegmentData &seg, CLRDATA_ADDRESS addr)
-{
- return (addr >= seg.mem) && (addr < seg.reserved);
-}
-
-void should_check_bgc_mark(const GCHeapDetails &heap,
- const DacpHeapSegmentData &seg,
- BOOL* consider_bgc_mark_p,
- BOOL* check_current_sweep_p,
- BOOL* check_saved_sweep_p)
-{
- // the logic for this function must be kept in sync with the analogous function in gc.cpp
- *consider_bgc_mark_p = FALSE;
- *check_current_sweep_p = FALSE;
- *check_saved_sweep_p = FALSE;
-
- if (heap.current_c_gc_state == c_gc_state_planning)
- {
- // We are doing the next_sweep_obj comparison here because we have yet to
- // turn on the swept flag for the segment but in_range_for_segment will return
- // FALSE if the address is the same as reserved.
- if ((seg.flags & heap_segment_flags_swept) || (heap.next_sweep_obj == seg.reserved))
- {
- // this seg was already swept.
- }
- else
- {
- *consider_bgc_mark_p = TRUE;
-
- if ((heap.saved_sweep_ephemeral_seg != -1) && (seg.segmentAddr == heap.saved_sweep_ephemeral_seg))
- {
- *check_saved_sweep_p = TRUE;
- }
-
- if (in_range_for_segment(seg, heap.next_sweep_obj))
- {
- *check_current_sweep_p = TRUE;
- }
- }
- }
-}
-
-// TODO: FACTOR TOGETHER THE OBJECT MEMBER WALKING CODE FROM
-// TODO: VerifyObjectMember(), GetListOfRefs(), HeapTraverser::PrintRefs()
-BOOL VerifyObjectMember(const GCHeapDetails &heap, DWORD_PTR objAddr)
-{
- BOOL ret = TRUE;
- BOOL bCheckCard = TRUE;
- size_t size = 0;
- {
- DWORD_PTR dwAddrCard = objAddr;
- while (dwAddrCard < objAddr + size)
- {
- if (CardIsSet(heap, dwAddrCard))
- {
- bCheckCard = FALSE;
- break;
- }
- dwAddrCard += card_size;
- }
-
- if (bCheckCard)
- {
- dwAddrCard = objAddr + size - 2*sizeof(PVOID);
- if (CardIsSet(heap, dwAddrCard))
- {
- bCheckCard = FALSE;
- }
- }
- }
-
- for (sos::RefIterator itr(TO_TADDR(objAddr)); itr; ++itr)
- {
- TADDR dwAddr1 = (DWORD_PTR)*itr;
- if (dwAddr1)
- {
- TADDR dwChild = dwAddr1;
- // Try something more efficient than IsObject here. Is the methodtable valid?
- size_t s;
- BOOL bPointers;
- TADDR dwAddrMethTable;
- if (FAILED(GetMTOfObject(dwAddr1, &dwAddrMethTable)) ||
- (GetSizeEfficient(dwAddr1, dwAddrMethTable, FALSE, s, bPointers) == FALSE))
- {
- DMLOut("object %s: bad member %p at %p\n", DMLObject(objAddr), SOS_PTR(dwAddr1), SOS_PTR(itr.GetOffset()));
- ret = FALSE;
- }
-
- if (IsMTForFreeObj(dwAddrMethTable))
- {
- DMLOut("object %s contains free object %p at %p\n", DMLObject(objAddr),
- SOS_PTR(dwAddr1), SOS_PTR(objAddr+itr.GetOffset()));
- ret = FALSE;
- }
-
- // verify card table
- if (bCheckCard && NeedCard(objAddr+itr.GetOffset(), dwAddr1))
- {
- DMLOut("object %s:%s missing card_table entry for %p\n",
- DMLObject(objAddr), (dwChild == dwAddr1) ? "" : " maybe",
- SOS_PTR(objAddr+itr.GetOffset()));
- ret = FALSE;
- }
- }
- }
-
- return ret;
-}
-
-// search for can_verify_deep in gc.cpp for examples of how these functions are used.
-BOOL VerifyObject(const GCHeapDetails &heap, const DacpHeapSegmentData &seg, DWORD_PTR objAddr, DWORD_PTR MTAddr, size_t objSize,
- BOOL bVerifyMember)
-{
- if (IsMTForFreeObj(MTAddr))
- {
- return TRUE;
- }
-
- if (objSize < min_obj_size)
- {
- DMLOut("object %s: size %d too small\n", DMLObject(objAddr), objSize);
- return FALSE;
- }
-
- // If we requested to verify the object's members, the GC may be in a state where that's not possible.
- // Here we check to see if the object in question needs to have its members updated. If so, we turn off
- // verification for the object.
- if (bVerifyMember)
- {
- BOOL consider_bgc_mark = FALSE, check_current_sweep = FALSE, check_saved_sweep = FALSE;
- should_check_bgc_mark(heap, seg, &consider_bgc_mark, &check_current_sweep, &check_saved_sweep);
- bVerifyMember = fgc_should_consider_object(heap, objAddr, seg, consider_bgc_mark, check_current_sweep, check_saved_sweep);
- }
-
- return bVerifyMember ? VerifyObjectMember(heap, objAddr) : TRUE;
-}
-
-
-BOOL FindSegment(const GCHeapDetails &heap, DacpHeapSegmentData &seg, CLRDATA_ADDRESS addr)
-{
- if (heap.has_regions)
- {
- CLRDATA_ADDRESS dwAddrSeg;
- for (UINT n = 0; n <= GetMaxGeneration(); n++)
- {
- dwAddrSeg = (DWORD_PTR)heap.generation_table[n].start_segment;
- while (dwAddrSeg != 0)
- {
- if (IsInterrupt())
- return FALSE;
- if (seg.Request(g_sos, dwAddrSeg, heap.original_heap_details) != S_OK)
- {
- ExtOut("Error requesting heap segment %p\n", SOS_PTR(dwAddrSeg));
- return FALSE;
- }
- if (addr >= TO_TADDR(seg.mem) && addr < seg.highAllocMark)
- {
- return TRUE;
- }
- dwAddrSeg = (DWORD_PTR)seg.next;
- }
- }
- return FALSE;
- }
- else
- {
- CLRDATA_ADDRESS dwAddrSeg = heap.generation_table[GetMaxGeneration()].start_segment;
-
- // Request the initial segment.
- if (seg.Request(g_sos, dwAddrSeg, heap.original_heap_details) != S_OK)
- {
- ExtOut("Error requesting heap segment %p.\n", SOS_PTR(dwAddrSeg));
- return FALSE;
- }
-
- // Loop while the object is not in range of the segment.
- while (addr < TO_TADDR(seg.mem) ||
- addr >= (dwAddrSeg == heap.ephemeral_heap_segment ? heap.alloc_allocated : TO_TADDR(seg.allocated)))
- {
- // get the next segment
- dwAddrSeg = seg.next;
-
- // We reached the last segment without finding the object.
- if (dwAddrSeg == NULL)
- return FALSE;
-
- if (seg.Request(g_sos, dwAddrSeg, heap.original_heap_details) != S_OK)
- {
- ExtOut("Error requesting heap segment %p.\n", SOS_PTR(dwAddrSeg));
- return FALSE;
- }
- }
- }
-
- return TRUE;
-}
-
-BOOL VerifyObject(const GCHeapDetails &heap, DWORD_PTR objAddr, DWORD_PTR MTAddr, size_t objSize, BOOL bVerifyMember)
-{
- // This is only used by the other VerifyObject function if bVerifyMember is true,
- // so we only initialize it if we need it for verifying object members.
- DacpHeapSegmentData seg;
-
- if (bVerifyMember)
- {
- // if we fail to find the segment, we cannot verify the object's members
- bVerifyMember = FindSegment(heap, seg, objAddr);
- }
-
- return VerifyObject(heap, seg, objAddr, MTAddr, objSize, bVerifyMember);
-}
-
-void sos::ObjectIterator::BuildError(char *out, size_t count, const char *format, ...) const
-{
- if (out == NULL || count == 0)
- return;
-
- va_list args;
- va_start(args, format);
-
- int written = vsprintf_s(out, count, format, args);
- if (written > 0 && mLastObj)
- sprintf_s(out+written, count-written, "\nLast good object: %p.\n", (int*)mLastObj);
-
- va_end(args);
-}
-
-bool sos::ObjectIterator::VerifyObjectMembers(char *reason, size_t count) const
-{
- if (!mCurrObj.HasPointers())
- return true;
-
- size_t size = mCurrObj.GetSize();
- size_t objAddr = (size_t)mCurrObj.GetAddress();
- TADDR mt = mCurrObj.GetMT();
-
- INT_PTR nEntries;
- MOVE(nEntries, mt-sizeof(PVOID));
- if (nEntries < 0)
- nEntries = -nEntries;
-
- size_t nSlots = 1 + nEntries * sizeof(CGCDescSeries)/sizeof(DWORD_PTR);
- ArrayHolder<DWORD_PTR> buffer = new DWORD_PTR[nSlots];
-
- if (FAILED(g_ExtData->ReadVirtual(TO_CDADDR(mt - nSlots*sizeof(DWORD_PTR)),
- buffer, (ULONG) (nSlots*sizeof(DWORD_PTR)), NULL)))
- {
- BuildError(reason, count, "Object %s has a bad GCDesc.", DMLObject(objAddr));
- return false;
- }
-
- CGCDesc *map = (CGCDesc *)(buffer+nSlots);
- CGCDescSeries* cur = map->GetHighestSeries();
- CGCDescSeries* last = map->GetLowestSeries();
-
- const size_t bufferSize = sizeof(size_t)*128;
- size_t objBuffer[bufferSize/sizeof(size_t)];
- size_t dwBeginAddr = (size_t)objAddr;
- size_t bytesInBuffer = bufferSize;
- if (size < bytesInBuffer)
- bytesInBuffer = size;
-
-
- if (FAILED(g_ExtData->ReadVirtual(TO_CDADDR(dwBeginAddr), objBuffer, (ULONG) bytesInBuffer,NULL)))
- {
- BuildError(reason, count, "Object %s: Failed to read members.", DMLObject(objAddr));
- return false;
- }
-
- BOOL bCheckCard = TRUE;
- {
- DWORD_PTR dwAddrCard = (DWORD_PTR)objAddr;
- while (dwAddrCard < objAddr + size)
- {
- if (CardIsSet (mHeaps[mCurrHeap], dwAddrCard))
- {
- bCheckCard = FALSE;
- break;
- }
- dwAddrCard += card_size;
- }
- if (bCheckCard)
- {
- dwAddrCard = objAddr + size - 2*sizeof(PVOID);
- if (CardIsSet (mHeaps[mCurrHeap], dwAddrCard))
- {
- bCheckCard = FALSE;
- }
- }
- }
-
- if (cur >= last)
- {
- do
- {
- BYTE** parm = (BYTE**)((objAddr) + cur->GetSeriesOffset());
- BYTE** ppstop =
- (BYTE**)((BYTE*)parm + cur->GetSeriesSize() + (size));
- while (parm < ppstop)
- {
- CheckInterrupt();
- size_t dwAddr1;
-
- // Do we run out of cache?
- if ((size_t)parm >= dwBeginAddr+bytesInBuffer)
- {
- // dwBeginAddr += bytesInBuffer;
- dwBeginAddr = (size_t)parm;
- if (dwBeginAddr >= objAddr + size)
- {
- return true;
- }
- bytesInBuffer = bufferSize;
- if (objAddr+size-dwBeginAddr < bytesInBuffer)
- {
- bytesInBuffer = objAddr+size-dwBeginAddr;
- }
- if (FAILED(g_ExtData->ReadVirtual(TO_CDADDR(dwBeginAddr), objBuffer, (ULONG) bytesInBuffer, NULL)))
- {
- BuildError(reason, count, "Object %s: Failed to read members.", DMLObject(objAddr));
- return false;
- }
- }
- dwAddr1 = objBuffer[((size_t)parm-dwBeginAddr)/sizeof(size_t)];
- if (dwAddr1) {
- DWORD_PTR dwChild = dwAddr1;
- // Try something more efficient than IsObject here. Is the methodtable valid?
- size_t s;
- BOOL bPointers;
- DWORD_PTR dwAddrMethTable;
- if (FAILED(GetMTOfObject(dwAddr1, &dwAddrMethTable)) ||
- (GetSizeEfficient(dwAddr1, dwAddrMethTable, FALSE, s, bPointers) == FALSE))
- {
- BuildError(reason, count, "object %s: bad member %p at %p", DMLObject(objAddr),
- SOS_PTR(dwAddr1), SOS_PTR(objAddr+(size_t)parm-objAddr));
-
- return false;
- }
-
- if (IsMTForFreeObj(dwAddrMethTable))
- {
- sos::Throw<HeapCorruption>("object %s contains free object %p at %p", DMLObject(objAddr),
- SOS_PTR(dwAddr1), SOS_PTR(objAddr+(size_t)parm-objAddr));
- }
-
- // verify card table
- if (bCheckCard &&
- NeedCard(objAddr+(size_t)parm-objAddr,dwChild))
- {
- BuildError(reason, count, "Object %s: %s missing card_table entry for %p",
- DMLObject(objAddr), (dwChild == dwAddr1)? "" : " maybe",
- SOS_PTR(objAddr+(size_t)parm-objAddr));
-
- return false;
- }
- }
- parm++;
- }
- cur--;
- CheckInterrupt();
-
- } while (cur >= last);
- }
- else
- {
- int cnt = (int) map->GetNumSeries();
- BYTE** parm = (BYTE**)((objAddr) + cur->startoffset);
- while ((BYTE*)parm < (BYTE*)((objAddr)+(size)-plug_skew))
- {
- for (int __i = 0; __i > cnt; __i--)
- {
- CheckInterrupt();
-
- unsigned skip = cur->val_serie[__i].skip;
- unsigned nptrs = cur->val_serie[__i].nptrs;
- BYTE** ppstop = parm + nptrs;
- do
- {
- size_t dwAddr1;
- // Do we run out of cache?
- if ((size_t)parm >= dwBeginAddr+bytesInBuffer)
- {
- // dwBeginAddr += bytesInBuffer;
- dwBeginAddr = (size_t)parm;
- if (dwBeginAddr >= objAddr + size)
- return true;
-
- bytesInBuffer = bufferSize;
- if (objAddr+size-dwBeginAddr < bytesInBuffer)
- bytesInBuffer = objAddr+size-dwBeginAddr;
-
- if (FAILED(g_ExtData->ReadVirtual(TO_CDADDR(dwBeginAddr), objBuffer, (ULONG) bytesInBuffer, NULL)))
- {
- BuildError(reason, count, "Object %s: Failed to read members.", DMLObject(objAddr));
- return false;
- }
- }
- dwAddr1 = objBuffer[((size_t)parm-dwBeginAddr)/sizeof(size_t)];
- {
- if (dwAddr1)
- {
- DWORD_PTR dwChild = dwAddr1;
- // Try something more efficient than IsObject here. Is the methodtable valid?
- size_t s;
- BOOL bPointers;
- DWORD_PTR dwAddrMethTable;
- if (FAILED(GetMTOfObject(dwAddr1, &dwAddrMethTable)) ||
- (GetSizeEfficient(dwAddr1, dwAddrMethTable, FALSE, s, bPointers) == FALSE))
- {
- BuildError(reason, count, "Object %s: Bad member %p at %p.\n", DMLObject(objAddr),
- SOS_PTR(dwAddr1), SOS_PTR(objAddr+(size_t)parm-objAddr));
-
- return false;
- }
-
- if (IsMTForFreeObj(dwAddrMethTable))
- {
- BuildError(reason, count, "Object %s contains free object %p at %p.", DMLObject(objAddr),
- SOS_PTR(dwAddr1), SOS_PTR(objAddr+(size_t)parm-objAddr));
- return false;
- }
-
- // verify card table
- if (bCheckCard &&
- NeedCard (objAddr+(size_t)parm-objAddr,dwAddr1))
- {
- BuildError(reason, count, "Object %s:%s missing card_table entry for %p",
- DMLObject(objAddr), (dwChild == dwAddr1) ? "" : " maybe",
- SOS_PTR(objAddr+(size_t)parm-objAddr));
-
- return false;
- }
- }
- }
- parm++;
- CheckInterrupt();
- } while (parm < ppstop);
- parm = (BYTE**)((BYTE*)parm + skip);
- }
- }
- }
-
- return true;
-}
-
-bool sos::ObjectIterator::Verify(char *reason, size_t count) const
-{
- try
- {
- TADDR mt = mCurrObj.GetMT();
-
- if (MethodTable::GetFreeMT() == mt)
- {
- return true;
- }
-
- size_t size = mCurrObj.GetSize();
- if (size < min_obj_size)
- {
- BuildError(reason, count, "Object %s: Size %d is too small.", DMLObject(mCurrObj.GetAddress()), size);
- return false;
- }
-
- if (mCurrObj.GetAddress() + mCurrObj.GetSize() > mSegmentEnd)
- {
- BuildError(reason, count, "Object %s is too large. End of segment at %p.", DMLObject(mCurrObj), mSegmentEnd);
- return false;
- }
-
- BOOL bVerifyMember = TRUE;
-
- // If we requested to verify the object's members, the GC may be in a state where that's not possible.
- // Here we check to see if the object in question needs to have its members updated. If so, we turn off
- // verification for the object.
- BOOL consider_bgc_mark = FALSE, check_current_sweep = FALSE, check_saved_sweep = FALSE;
- should_check_bgc_mark(mHeaps[mCurrHeap], mSegment, &consider_bgc_mark, &check_current_sweep, &check_saved_sweep);
- bVerifyMember = fgc_should_consider_object(mHeaps[mCurrHeap], mCurrObj.GetAddress(), mSegment,
- consider_bgc_mark, check_current_sweep, check_saved_sweep);
-
- if (bVerifyMember)
- return VerifyObjectMembers(reason, count);
- }
- catch(const sos::Exception &e)
- {
- BuildError(reason, count, e.GetMesssage());
- return false;
- }
-
- return true;
-}
-
-bool sos::ObjectIterator::Verify() const
-{
- char *c = NULL;
- return Verify(c, 0);
-}
return (size_t)stInfo.m_StringLength;
}
-
- RefIterator::RefIterator(TADDR obj, LinearReadCache *cache)
- : mCache(cache), mGCDesc(0), mArrayOfVC(false), mDone(false), mBuffer(0), mCurrSeries(0), mLoaderAllocatorObjectHandle(0),
- i(0), mCount(0), mCurr(0), mStop(0), mObject(obj), mObjSize(0)
- {
- Init();
- }
-
- RefIterator::RefIterator(TADDR obj, CGCDesc *desc, bool arrayOfVC, LinearReadCache *cache)
- : mCache(cache), mGCDesc(desc), mArrayOfVC(arrayOfVC), mDone(false), mBuffer(0), mCurrSeries(0), mLoaderAllocatorObjectHandle(0),
- i(0), mCount(0), mCurr(0), mStop(0), mObject(obj), mObjSize(0)
- {
- Init();
- }
-
- RefIterator::~RefIterator()
- {
- if (mBuffer)
- delete [] mBuffer;
- }
-
- const RefIterator &RefIterator::operator++()
- {
- if (mDone)
- Throw<Exception>("Attempt to move past the end of the iterator.");
-
- if (mCurr == mLoaderAllocatorObjectHandle)
- {
- // The mLoaderAllocatorObjectHandle is always the last reference returned
- mDone = true;
- return *this;
- }
-
- if (!mArrayOfVC)
- {
- mCurr += sizeof(TADDR);
- if (mCurr >= mStop)
- {
- mCurrSeries--;
- if (mCurrSeries < mGCDesc->GetLowestSeries())
- {
- mDone = true;
- }
- else
- {
- mCurr = mObject + mCurrSeries->GetSeriesOffset();
- mStop = mCurr + mCurrSeries->GetSeriesSize() + mObjSize;
- }
- }
- }
- else
- {
- mCurr += sizeof(TADDR);
- if (mCurr >= mStop)
- {
- int i_last = i;
- i--;
-
- if (i == mCount)
- i = 0;
-
- mCurr += mCurrSeries->val_serie[i_last].skip;
- mStop = mCurr + mCurrSeries->val_serie[i].nptrs * sizeof(TADDR);
- }
-
- if (mCurr >= mObject + mObjSize - plug_skew)
- mDone = true;
- }
-
- if (mDone && mLoaderAllocatorObjectHandle != NULL)
- {
- // The iteration over all regular object references is done, but there is one more
- // reference for collectible types - the LoaderAllocator for GC
- mCurr = mLoaderAllocatorObjectHandle;
- mDone = false;
- }
-
- return *this;
- }
-
- TADDR RefIterator::operator*() const
- {
- return ReadPointer(mCurr);
- }
-
- TADDR RefIterator::GetOffset() const
- {
- return mCurr - mObject;
- }
-
- void RefIterator::Init()
- {
- TADDR mt = ReadPointer(mObject);
- BOOL bContainsPointers = FALSE;
- BOOL bCollectible = FALSE;
- TADDR loaderAllocatorObjectHandle;
-
- if (!GetSizeEfficient(mObject, mt, FALSE, mObjSize, bContainsPointers))
- Throw<DataRead>("Failed to get size of object.");
-
- if (!GetCollectibleDataEfficient(mt, bCollectible, loaderAllocatorObjectHandle))
- Throw<DataRead>("Failed to get collectible info of object.");
-
- if (!bContainsPointers && !bCollectible)
- {
- mDone = true;
- return;
- }
-
- if (bContainsPointers)
- {
- if (!mGCDesc)
- {
- int entries = 0;
-
- if (FAILED(MOVE(entries, mt-sizeof(TADDR))))
- Throw<DataRead>("Failed to request number of entries for %p MT %p", mObject, mt);
-
- // array of vc?
- if (entries < 0)
- {
- entries = -entries;
- mArrayOfVC = true;
- }
- else
- {
- mArrayOfVC = false;
- }
-
- size_t slots = 1 + entries * sizeof(CGCDescSeries)/sizeof(TADDR);
-
- ArrayHolder<TADDR> buffer = new TADDR[slots];
-
- ULONG fetched = 0;
- CLRDATA_ADDRESS address = TO_CDADDR(mt - slots*sizeof(TADDR));
- if (FAILED(g_ExtData->ReadVirtual(address, buffer, (ULONG)(slots*sizeof(TADDR)), &fetched)))
- Throw<DataRead>("Failed to request GCDesc.");
-
- mBuffer = buffer.Detach();
- mGCDesc = (CGCDesc*)(mBuffer + slots);
- }
-
- mCurrSeries = mGCDesc->GetHighestSeries();
-
- if (!mArrayOfVC)
- {
- mCurr = mObject + mCurrSeries->GetSeriesOffset();
- mStop = mCurr + mCurrSeries->GetSeriesSize() + mObjSize;
- }
- else
- {
- i = 0;
- mCurr = mObject + mCurrSeries->startoffset;
- mStop = mCurr + mCurrSeries->val_serie[i].nptrs * sizeof(TADDR);
- mCount = (int)mGCDesc->GetNumSeries();
- }
-
- if (mCurr == mStop)
- operator++();
- else if (mCurr >= mObject + mObjSize - plug_skew)
- mDone = true;
- }
- else
- {
- mDone = true;
- }
-
- if (bCollectible)
- {
- mLoaderAllocatorObjectHandle = loaderAllocatorObjectHandle;
- if (mDone)
- {
- // There are no object references, but there is still a reference for
- // collectible types - the LoaderAllocator for GC
- mCurr = mLoaderAllocatorObjectHandle;
- mDone = false;
- }
- }
- }
-
-
const TADDR GCHeap::HeapStart = 0;
const TADDR GCHeap::HeapEnd = ~0;
mutable WCHAR *mTypeName;
};
- /* Enumerates all the GC references (objects) contained in an object. This uses the GCDesc
- * map exactly as the GC does.
- */
- class RefIterator
- {
- public:
- RefIterator(TADDR obj, LinearReadCache *cache = NULL);
- RefIterator(TADDR obj, CGCDesc *desc, bool arrayOfVC, LinearReadCache *cache = NULL);
- ~RefIterator();
-
- /* Moves to the next reference in the object.
- */
- const RefIterator &operator++();
-
- /* Returns the address of the current reference.
- */
- TADDR operator*() const;
-
- /* Gets the offset into the object where the current reference comes from.
- */
- TADDR GetOffset() const;
-
- /* Returns true if there are more objects in the iteration, false otherwise.
- * Used as:
- * if (itr)
- * ...
- */
- inline operator void *() const
- {
- return (void*)!mDone;
- }
-
- bool IsLoaderAllocator() const
- {
- return mLoaderAllocatorObjectHandle == mCurr;
- }
-
- private:
- void Init();
- inline TADDR ReadPointer(TADDR addr) const
- {
- if (mCache)
- {
- if (!mCache->Read(addr, &addr, false))
- Throw<DataRead>("Could not read address %p.", addr);
- }
- else
- {
- MOVE(addr, addr);
- }
-
- return addr;
- }
-
- private:
- LinearReadCache *mCache;
- CGCDesc *mGCDesc;
- bool mArrayOfVC, mDone;
-
- TADDR *mBuffer;
- CGCDescSeries *mCurrSeries;
-
- TADDR mLoaderAllocatorObjectHandle;
-
- int i, mCount;
-
- TADDR mCurr, mStop, mObject;
- size_t mObjSize;
- };
-
-
/* The Iterator used to walk the managed objects on the GC heap.
* The general usage pattern for this class is:
* for (ObjectIterator itr = gcheap.WalkHeap(); itr; ++itr)
return bLarge;
}
- /* Verifies the current object. Returns true if the current object is valid.
- * Returns false and fills 'buffer' with the reason the object is corrupted.
- * This is a deeper validation than Object::IsValid as it checks the card
- * table entires for the object in addition to the rest of the references.
- * This function does not throw exceptions.
- * Params:
- * buffer - out buffer that is filled if and only if this function returns
- * false.
- * size - the total size of the buffer
- * Returns:
- * True if the object is valid, false otherwise.
- */
- bool Verify(__out_ecount(size) char *buffer, size_t size) const;
-
- /* The same as Verify(char*, size_t), except it does not write out the failure
- * reason to a provided buffer.
- * See:
- * ObjectIterator::Verify(char *, size_t)
- */
- bool Verify() const;
-
/* Attempts to move to the next object (similar to ObjectIterator++), but
* attempts to recover from any heap corruption by skipping to the next
* segment. If Verify returns false, meaning it detected heap corruption
private:
ObjectIterator(const GCHeapDetails *heap, int numHeaps, TADDR start, TADDR stop);
- bool VerifyObjectMembers(__out_ecount(size) char *buffer, size_t size) const;
- void BuildError(__out_ecount(count) char *out, size_t count, const char *format, ...) const;
-
void AssertSanity() const;
/*
if (SUCCEEDED(Status) && bRefs)
{
- ExtOut("GC Refs:\n");
- TableOutput out(2, POINTERSIZE_HEX, AlignRight, 4);
- out.WriteRow("offset", "object");
- for (sos::RefIterator itr(TO_TADDR(p_Object)); itr; ++itr)
- out.WriteRow(Hex(itr.GetOffset()), ObjectPtr(*itr));
+ std::stringstream argsBuilder;
+ argsBuilder << std::hex << p_Object << " ";
+ return ExecuteCommand("dumpobjgcrefs", argsBuilder.str().c_str());
}
}
catch(const sos::Exception &e)
return ExecuteCommand("traverseheap", args);
}
-struct PrintRuntimeTypeArgs
-{
- DWORD_PTR mtOfRuntimeType;
- int handleFieldOffset;
- DacpAppDomainStoreData adstore;
-};
-
-void PrintRuntimeTypes(DWORD_PTR objAddr,size_t Size,DWORD_PTR methodTable,LPVOID token)
-{
- PrintRuntimeTypeArgs *pArgs = (PrintRuntimeTypeArgs *)token;
-
- if (pArgs->mtOfRuntimeType == NULL)
- {
- NameForMT_s(methodTable, g_mdName, mdNameLen);
-
- if (_wcscmp(g_mdName, W("System.RuntimeType")) == 0)
- {
- pArgs->mtOfRuntimeType = methodTable;
- pArgs->handleFieldOffset = GetObjFieldOffset(TO_CDADDR(objAddr), TO_CDADDR(methodTable), W("m_handle"));
- if (pArgs->handleFieldOffset <= 0)
- ExtOut("Error getting System.RuntimeType.m_handle offset\n");
-
- pArgs->adstore.Request(g_sos);
- }
- }
-
- if ((methodTable == pArgs->mtOfRuntimeType) && (pArgs->handleFieldOffset > 0))
- {
- // Get the method table and display the information.
- DWORD_PTR mtPtr;
- if (MOVE(mtPtr, objAddr + pArgs->handleFieldOffset) == S_OK)
- {
- DMLOut(DMLObject(objAddr));
-
- // Check if TypeDesc
- if ((mtPtr & RUNTIMETYPE_HANDLE_IS_TYPEDESC) != 0)
- {
- ExtOut(" %p\n", mtPtr & ~RUNTIMETYPE_HANDLE_IS_TYPEDESC);
- }
- else
- {
- CLRDATA_ADDRESS appDomain = GetAppDomainForMT(mtPtr);
- if (appDomain != NULL)
- {
- if (appDomain == pArgs->adstore.sharedDomain)
- ExtOut(" %" POINTERSIZE "s", "Shared");
-
- else if (appDomain == pArgs->adstore.systemDomain)
- ExtOut(" %" POINTERSIZE "s", "System");
- else
- DMLOut(" %s", DMLDomain(appDomain));
- }
- else
- {
- ExtOut(" %" POINTERSIZE "s", "?");
- }
-
- if (NameForMT_s(mtPtr, g_mdName, mdNameLen))
- {
- DMLOut(" %s %S\n", DMLMethodTable(mtPtr), g_mdName);
- }
- }
- }
- }
-}
-
-
DECLARE_API(DumpRuntimeTypes)
{
- INIT_API();
+ INIT_API_EXT();
MINIDUMP_NOT_SUPPORTED();
-
- BOOL dml = FALSE;
-
- CMDOption option[] =
- { // name, vptr, type, hasValue
- {"/d", &dml, COBOOL, FALSE},
- };
-
- if (!GetCMDOption(args, option, ARRAY_SIZE(option), NULL, 0, NULL))
- return Status;
-
- EnableDMLHolder dmlHolder(dml);
-
- ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s Type Name \n",
- "Address", "Domain", "MT");
- ExtOut("------------------------------------------------------------------------------\n");
-
- if (!g_snapshot.Build())
- {
- ExtOut("Unable to build snapshot of the garbage collector state\n");
- return E_FAIL;
- }
-
- PrintRuntimeTypeArgs pargs;
- ZeroMemory(&pargs, sizeof(PrintRuntimeTypeArgs));
-
- try
- {
- GCHeapsTraverse(PrintRuntimeTypes, (LPVOID)&pargs);
- }
- catch(const sos::Exception &e)
- {
- ExtOut("%s\n", e.what());
- return E_FAIL;
- }
-
- return Status;
+ return ExecuteCommand("dumpruntimetypes", args);
}
namespace sos
DECLARE_API(VerifyObj)
{
- INIT_API();
+ INIT_API_EXT();
MINIDUMP_NOT_SUPPORTED();
- TADDR taddrObj = 0;
- TADDR taddrMT;
- size_t objSize;
-
- BOOL bValid = FALSE;
- BOOL dml = FALSE;
-
- CMDOption option[] =
- { // name, vptr, type, hasValue
- {"/d", &dml, COBOOL, FALSE},
- };
- CMDValue arg[] =
- { // vptr, type
- {&taddrObj, COHEX}
- };
- size_t nArg;
- if (!GetCMDOption(args, option, ARRAY_SIZE(option), arg, ARRAY_SIZE(arg), &nArg))
- {
- return Status;
- }
-
- EnableDMLHolder dmlHolder(dml);
- BOOL bContainsPointers;
-
- if (FAILED(GetMTOfObject(taddrObj, &taddrMT)) ||
- !GetSizeEfficient(taddrObj, taddrMT, FALSE, objSize, bContainsPointers))
- {
- ExtOut("object %#p does not have valid method table\n", SOS_PTR(taddrObj));
- goto Exit;
- }
-
- // we need to build g_snapshot as it is later used in GetGeneration
- if (!g_snapshot.Build())
- {
- ExtOut("Unable to build snapshot of the garbage collector state\n");
- goto Exit;
- }
-
- try
- {
- GCHeapDetails *pheapDetails = g_snapshot.GetHeap(taddrObj);
- bValid = VerifyObject(*pheapDetails, taddrObj, taddrMT, objSize, TRUE);
- }
- catch(const sos::Exception &e)
- {
- ExtOut("%s\n", e.what());
- return E_FAIL;
- }
-
-Exit:
- if (bValid)
- {
- ExtOut("object %#p is a valid object\n", SOS_PTR(taddrObj));
- }
-
- return Status;
+ return ExecuteCommand("verifyobj", args);
}
DECLARE_API(ListNearObj)
void GetInfoFromName(DWORD_PTR ModuleAddr, const char* name, mdTypeDef* retMdTypeDef=NULL);
void GetInfoFromModule (DWORD_PTR ModuleAddr, ULONG token, DWORD_PTR *ret=NULL);
-
-typedef void (*VISITGCHEAPFUNC)(DWORD_PTR objAddr,size_t Size,DWORD_PTR methodTable,LPVOID token);
-BOOL GCHeapsTraverse(VISITGCHEAPFUNC pFunc, LPVOID token, BOOL verify=true);
-
/////////////////////////////////////////////////////////////////////////////////////////////////////////
struct strobjInfo
CLRDATA_ADDRESS GetAppDomainForMT(CLRDATA_ADDRESS mtPtr);
CLRDATA_ADDRESS GetAppDomain(CLRDATA_ADDRESS objPtr);
-BOOL VerifyObject(const GCHeapDetails &heap, const DacpHeapSegmentData &seg, DWORD_PTR objAddr, DWORD_PTR MTAddr, size_t objSize,
- BOOL bVerifyMember);
-BOOL VerifyObject(const GCHeapDetails &heap, DWORD_PTR objAddr, DWORD_PTR MTAddr, size_t objSize,
- BOOL bVerifyMember);
-
BOOL IsMTForFreeObj(DWORD_PTR pMT);
void DumpStackObjectsHelper (TADDR StackTop, TADDR StackBottom, BOOL verifyFields);
g_services->AddCommand("dumpmodule", new sosCommand("DumpModule"), "Displays information about a EE module structure at the specified address.");
g_services->AddCommand("dumpmt", new sosCommand("DumpMT"), "Displays information about a method table at the specified address.");
g_services->AddCommand("dumpobj", new sosCommand("DumpObj"), "Displays info about an object at the specified address.");
- g_services->AddCommand("dumpruntimetypes", new sosCommand("DumpRuntimeTypes"), "Finds all System.RuntimeType objects in the GC heap and prints the type name and MethodTable they refer too.");
+ g_services->AddManagedCommand("dumpruntimetypes", "Finds all System.RuntimeType objects in the GC heap and prints the type name and MethodTable they refer too.");
g_services->AddCommand("dumpsig", new sosCommand("DumpSig"), "Dumps the signature of a method or field specified by '<sigaddr> <moduleaddr>'.");
g_services->AddCommand("dumpsigelem", new sosCommand("DumpSigElem"), "Dumps a single element of a signature object.");
g_services->AddCommand("dumpstack", new sosCommand("DumpStack"), "Displays a native and managed stack trace.");
g_services->AddCommand("threadstate", new sosCommand("ThreadState"), "Pretty prints the meaning of a threads state.");
g_services->AddCommand("token2ee", new sosCommand("token2ee"), "Displays the MethodTable structure and MethodDesc structure for the specified token and module.");
g_services->AddManagedCommand("verifyheap", "Checks the GC heap for signs of corruption.");
- g_services->AddCommand("verifyobj", new sosCommand("VerifyObj"), "Checks the object that is passed as an argument for signs of corruption.");
+ g_services->AddManagedCommand("verifyobj", "Checks the object that is passed as an argument for signs of corruption.");
return true;
}