Skip to content

Commit 162a4b6

Browse files
authored
Merge pull request #764 from microsoft/dev/lifengl/fixSosToolForValueTask
Fix the dumpasync common to handle general ValueTasks.
2 parents 966ac96 + e2b3641 commit 162a4b6

File tree

1 file changed

+62
-3
lines changed

1 file changed

+62
-3
lines changed

src/SosThreadingTools/DumpAsyncCommand.cs

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,19 +56,78 @@ private static void GetAllStateMachines(DebuggerContext context, ClrHeap heap, L
5656
break;
5757
}
5858

59-
asyncBuilder = asyncBuilder.TryGetValueClassField("m_builder");
59+
ClrValueType? nextAsyncBuilder = asyncBuilder.TryGetValueClassField("m_builder");
60+
if (nextAsyncBuilder == null)
61+
{
62+
asyncBuilder = asyncBuilder.TryGetValueClassField("_methodBuilder");
63+
}
64+
else
65+
{
66+
asyncBuilder = nextAsyncBuilder;
67+
}
6068
}
6169
}
6270
else
6371
{
6472
// CLR debugger may not be able to access t__builder, when NGEN assemblies are being used, and the type of the field could be lost.
6573
// Our workaround is to pick up the first Task object referenced by the state machine, which seems to be correct.
6674
// That function works with the raw data structure (like how GC scans the object, so it doesn't depend on symbols.
75+
//
76+
// However, one problem of that is we can pick up tasks from other reference fields of the same structure. So, we go through fields which we have symbols
77+
// and remember references encounted, and we skip them when we go through GC references.
78+
// Note: we can do better by going through other value structures, and extract references from them here, which we can consider when we have a real scenario.
79+
var previousReferences = new Dictionary<ulong, int>();
80+
if (stateMachine.Type?.GetFieldByName("<>t__builder") != null)
81+
{
82+
foreach (ClrInstanceField field in stateMachine.Type.Fields)
83+
{
84+
if (string.Equals(field.Name, "<>t__builder", StringComparison.Ordinal))
85+
{
86+
break;
87+
}
88+
89+
if (field.IsObjectReference)
90+
{
91+
ClrObject referencedValue = field.ReadObject(stateMachine.Address, interior: false);
92+
if (!referencedValue.IsNull)
93+
{
94+
if (previousReferences.TryGetValue(referencedValue.Address, out int refCount))
95+
{
96+
previousReferences[referencedValue.Address] = refCount + 1;
97+
}
98+
else
99+
{
100+
previousReferences[referencedValue.Address] = 1;
101+
}
102+
}
103+
}
104+
}
105+
}
106+
67107
foreach (ClrObject referencedObject in stateMachine.EnumerateReferences(true))
68108
{
69-
if (!referencedObject.IsNull && referencedObject.Type is object)
109+
if (!referencedObject.IsNull)
70110
{
71-
if (string.Equals(referencedObject.Type.Name, "System.Threading.Tasks.Task", StringComparison.Ordinal) || string.Equals(referencedObject.Type.BaseType?.Name, "System.Threading.Tasks.Task", StringComparison.Ordinal))
111+
if (previousReferences.TryGetValue(referencedObject.Address, out int refCount) && refCount > 0)
112+
{
113+
if (refCount == 1)
114+
{
115+
previousReferences.Remove(referencedObject.Address);
116+
}
117+
else
118+
{
119+
previousReferences[referencedObject.Address] = refCount - 1;
120+
}
121+
122+
continue;
123+
}
124+
else if (previousReferences.Count > 0)
125+
{
126+
continue;
127+
}
128+
129+
if (referencedObject.Type is object &&
130+
(string.Equals(referencedObject.Type.Name, "System.Threading.Tasks.Task", StringComparison.Ordinal) || string.Equals(referencedObject.Type.BaseType?.Name, "System.Threading.Tasks.Task", StringComparison.Ordinal)))
72131
{
73132
taskField = referencedObject;
74133
break;

0 commit comments

Comments
 (0)