Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit a06afd1

Browse files
committed
Add additional features to SOS DumpAsync command
1. Computing GC roots is a relatively slow operation, and doing it for every state machine object found in a large heap can be time consuming. Making it opt-in with -roots command-line flag. 2. Added -waiting command-line flag. DumpAsync will now retrieve the <>1__state field from the StateMachine, and if -waiting is specified, it'll filter down to state machines that have a state value >= 0, meaning the state machines are waiting at an await point. For example, given this program: ```C# using System.Threading.Tasks; class Program { static async Task Main() { await MethodA(0); await MethodA(int.MaxValue); } static async Task MethodA(int delay) => await MethodB(delay); static async Task MethodB(int delay) { await Task.Yield(); await Task.Delay(delay); } } ``` using `!DumpAsync` outputs: ``` Address MT Size Name #0 0000026848693438 00007ff88ea35e58 120 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<MethodB>d__2, test]] StateMachine: Program+<MethodB>d__2 (struct) MT Field Offset Type VT Attr Value Name 00007ff8e9bc4bc0 4000008 0 System.Int32 1 instance -2 <>1__state 00007ff8e9bd82f8 4000009 8 ...TaskMethodBuilder 1 instance 0000026848693490 <>t__builder 00007ff8e9bc4bc0 400000a 4 System.Int32 1 instance 0 delay 00007ff8e9bee4d0 400000b 10 ...able+YieldAwaiter 1 instance 0000026848693498 <>u__1 00007ff8e9bcead0 400000c 18 ...vices.TaskAwaiter 1 instance 00000268486934a0 <>u__2 Continuation: 00000268486934b0 (System.Object) #1 0000026848693e68 00007ff88ea36cc8 112 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<MethodA>d__1, test]] StateMachine: Program+<MethodA>d__1 (struct) MT Field Offset Type VT Attr Value Name 00007ff8e9bc4bc0 4000004 0 System.Int32 1 instance -2 <>1__state 00007ff8e9bd82f8 4000005 8 ...TaskMethodBuilder 1 instance 0000026848693ec0 <>t__builder 00007ff8e9bc4bc0 4000006 4 System.Int32 1 instance 0 delay 00007ff8e9bcead0 4000007 10 ...vices.TaskAwaiter 1 instance 0000026848693ec8 <>u__1 Continuation: 00000268486934b0 (System.Object) #2 0000026848693ed8 00007ff88ea37188 112 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<Main>d__0, test]] StateMachine: Program+<Main>d__0 (struct) MT Field Offset Type VT Attr Value Name 00007ff8e9bc4bc0 4000001 0 System.Int32 1 instance 1 <>1__state 00007ff8e9bd82f8 4000002 8 ...TaskMethodBuilder 1 instance 0000026848693f30 <>t__builder 00007ff8e9bcead0 4000003 10 ...vices.TaskAwaiter 1 instance 0000026848693f38 <>u__1 Continuation: 0000026848693f48 (System.Threading.Tasks.Task+SetOnInvokeMres) #3 0000026848695d30 00007ff88ea35e58 120 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<MethodB>d__2, test]] StateMachine: Program+<MethodB>d__2 (struct) MT Field Offset Type VT Attr Value Name 00007ff8e9bc4bc0 4000008 0 System.Int32 1 instance 1 <>1__state 00007ff8e9bd82f8 4000009 8 ...TaskMethodBuilder 1 instance 0000026848695d88 <>t__builder 00007ff8e9bc4bc0 400000a 4 System.Int32 1 instance 2147483647 delay 00007ff8e9bee4d0 400000b 10 ...able+YieldAwaiter 1 instance 0000026848695d90 <>u__1 00007ff8e9bcead0 400000c 18 ...vices.TaskAwaiter 1 instance 0000026848695d98 <>u__2 Continuation: 0000026848695dd0 (System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<MethodA>d__1, test]]) #4 0000026848695dd0 00007ff88ea36cc8 112 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<MethodA>d__1, test]] StateMachine: Program+<MethodA>d__1 (struct) MT Field Offset Type VT Attr Value Name 00007ff8e9bc4bc0 4000004 0 System.Int32 1 instance 0 <>1__state 00007ff8e9bd82f8 4000005 8 ...TaskMethodBuilder 1 instance 0000026848695e28 <>t__builder 00007ff8e9bc4bc0 4000006 4 System.Int32 1 instance 2147483647 delay 00007ff8e9bcead0 4000007 10 ...vices.TaskAwaiter 1 instance 0000026848695e30 <>u__1 Continuation: 0000026848693ed8 (System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<Main>d__0, test]]) Found 5 state machines. ``` while using `!DumpAsync -waiting` outputs only: ``` Address MT Size Name #0 0000026848693ed8 00007ff88ea37188 112 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<Main>d__0, test]] StateMachine: Program+<Main>d__0 (struct) MT Field Offset Type VT Attr Value Name 00007ff8e9bc4bc0 4000001 0 System.Int32 1 instance 1 <>1__state 00007ff8e9bd82f8 4000002 8 ...TaskMethodBuilder 1 instance 0000026848693f30 <>t__builder 00007ff8e9bcead0 4000003 10 ...vices.TaskAwaiter 1 instance 0000026848693f38 <>u__1 Continuation: 0000026848693f48 (System.Threading.Tasks.Task+SetOnInvokeMres) #1 0000026848695d30 00007ff88ea35e58 120 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<MethodB>d__2, test]] StateMachine: Program+<MethodB>d__2 (struct) MT Field Offset Type VT Attr Value Name 00007ff8e9bc4bc0 4000008 0 System.Int32 1 instance 1 <>1__state 00007ff8e9bd82f8 4000009 8 ...TaskMethodBuilder 1 instance 0000026848695d88 <>t__builder 00007ff8e9bc4bc0 400000a 4 System.Int32 1 instance 2147483647 delay 00007ff8e9bee4d0 400000b 10 ...able+YieldAwaiter 1 instance 0000026848695d90 <>u__1 00007ff8e9bcead0 400000c 18 ...vices.TaskAwaiter 1 instance 0000026848695d98 <>u__2 Continuation: 0000026848695dd0 (System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<MethodA>d__1, test]]) #2 0000026848695dd0 00007ff88ea36cc8 112 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<MethodA>d__1, test]] StateMachine: Program+<MethodA>d__1 (struct) MT Field Offset Type VT Attr Value Name 00007ff8e9bc4bc0 4000004 0 System.Int32 1 instance 0 <>1__state 00007ff8e9bd82f8 4000005 8 ...TaskMethodBuilder 1 instance 0000026848695e28 <>t__builder 00007ff8e9bc4bc0 4000006 4 System.Int32 1 instance 2147483647 delay 00007ff8e9bcead0 4000007 10 ...vices.TaskAwaiter 1 instance 0000026848695e30 <>u__1 Continuation: 0000026848693ed8 (System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<Main>d__0, test]]) Found 3 state machines. ``` skipping the two state machines that have a `<>1__state` field value of -2 (meaning it's completed). Note that this change has the somewhat unfortunate impact of taking a dependency on what's effectively an implementation detail of Roslyn, but the value the filtering provides is deemed to be worth it. This design is unlikely to change in the future, and as with other diagnostic/debugging features that rely on such details, it can be updated if Roslyn ever changes its scheme. In the meantime, the code will output a warning message if it can't find the state field. 3. If a state machine is found to have 0 roots but also to have a <>1__state value >= 0, that suggests it was dropped without having been completed, which is likely a sign of an application bug. The command now prints out an information message to highlight that state. For example, this program: ```C# using System; using System.Threading.Tasks; class Program { static void Main() { Task.Run(async () => await new TaskCompletionSource<bool>().Task); Console.ReadLine(); } } ``` when processed with `!DumpAsync -roots` results in: ``` Address MT Size Name #0 0000020787fb5b30 00007ff88ea1afe8 112 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Boolean, System.Private.CoreLib],[Program+<>c+<<Main>b__0_0>d, test]] StateMachine: Program+<>c+<<Main>b__0_0>d (struct) MT Field Offset Type VT Attr Value Name 00007ff8e9bc4bc0 4000003 0 System.Int32 1 instance 0 <>1__state 00007ff8e9bd0b88 4000004 8 ...Private.CoreLib]] 1 instance 0000020787fb5b88 <>t__builder 00007ff8e9bffd58 4000005 10 ...Private.CoreLib]] 1 instance 0000020787fb5b90 <>u__1 Continuation: 0000020787fb3fc8 (System.Threading.Tasks.UnwrapPromise`1[[System.Boolean, System.Private.CoreLib]]) GC roots: Incomplete state machine (<>1__state == 0) with 0 roots. Found 1 state machines. ```
1 parent a9c56e2 commit a06afd1

File tree

6 files changed

+133
-15
lines changed

6 files changed

+133
-15
lines changed

src/ToolBox/SOS/Strike/apollososdocs.txt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,9 @@ The arguments in detail:
344344

345345
COMMAND: dumpasync.
346346
!DumpAsync [-mt <MethodTable address>]
347-
[-type <partial type name>]]
347+
[-type <partial type name>]
348+
[-waiting]
349+
[-roots]]
348350

349351
!DumpAsync traverses the garbage collected heap, looking for objects representing
350352
async state machines as created when an async method's state is transferred to the
@@ -362,7 +364,7 @@ These details include:
362364

363365
For example:
364366

365-
0:011> !DumpAsync
367+
0:011> !DumpAsync -roots
366368
#0
367369
000001989f413de0 00007ff88c506ba8 112 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<MethodD>d__4, test]]
368370
StateMachine: Program+<MethodD>d__4 (struct)
@@ -396,6 +398,8 @@ The arguments in detail:
396398
-mt List only those state machine objects with the MethodTable given.
397399
-type List only those state machine objects whose type name is a
398400
substring match of the string provided.
401+
-waiting List only those state machines that are currently at an await point.
402+
-roots Include GC root information for each state machine object.
399403

400404
\\
401405

src/ToolBox/SOS/Strike/sosdocs.txt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,9 @@ The arguments in detail:
342342

343343
COMMAND: dumpasync.
344344
!DumpAsync [-mt <MethodTable address>]
345-
[-type <partial type name>]]
345+
[-type <partial type name>]
346+
[-waiting]
347+
[-roots]]
346348

347349
!DumpAsync traverses the garbage collected heap, looking for objects representing
348350
async state machines as created when an async method's state is transferred to the
@@ -360,7 +362,7 @@ These details include:
360362

361363
For example:
362364

363-
0:011> !DumpAsync
365+
0:011> !DumpAsync -roots
364366
#0
365367
000001989f413de0 00007ff88c506ba8 112 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<MethodD>d__4, test]]
366368
StateMachine: Program+<MethodD>d__4 (struct)
@@ -394,6 +396,8 @@ The arguments in detail:
394396
-mt List only those state machine objects with the MethodTable given.
395397
-type List only those state machine objects whose type name is a
396398
substring match of the string provided.
399+
-waiting List only those state machines that are currently at an await point.
400+
-roots Include GC root information for each state machine object.
397401

398402
\\
399403

src/ToolBox/SOS/Strike/sosdocsunix.txt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,9 @@ The arguments in detail:
203203

204204
COMMAND: dumpasync.
205205
!DumpAsync [-mt <MethodTable address>]
206-
[-type <partial type name>]]
206+
[-type <partial type name>]
207+
[-waiting]
208+
[-roots]]
207209

208210
!DumpAsync traverses the garbage collected heap, looking for objects representing
209211
async state machines as created when an async method's state is transferred to the
@@ -221,7 +223,7 @@ These details include:
221223

222224
For example:
223225

224-
(lldb) dumpasync
226+
(lldb) dumpasync -roots
225227
#0
226228
000001989f413de0 00007ff88c506ba8 112 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Program+<MethodD>d__4, test]]
227229
StateMachine: Program+<MethodD>d__4 (struct)
@@ -255,6 +257,8 @@ The arguments in detail:
255257
-mt List only those state machine objects with the MethodTable given.
256258
-type List only those state machine objects whose type name is a
257259
substring match of the string provided.
260+
-waiting List only those state machines that are currently at an await point.
261+
-roots Include GC root information for each state machine object.
258262

259263
\\
260264

src/ToolBox/SOS/Strike/strike.cpp

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4123,18 +4123,20 @@ DECLARE_API(DumpAsync)
41234123
TADDR mt = NULL;
41244124
ArrayHolder<char> ansiType = NULL;
41254125
ArrayHolder<WCHAR> type = NULL;
4126-
BOOL dml = FALSE;
4126+
BOOL dml = FALSE, waiting = FALSE, roots = FALSE;
41274127
CMDOption option[] =
41284128
{ // name, vptr, type, hasValue
41294129
{ "-mt", &mt, COHEX, TRUE }, // dump state machines only with a given MethodTable
41304130
{ "-type", &ansiType, COSTRING, TRUE }, // dump state machines only that contain the specified type substring
4131+
{ "-waiting", &waiting, COBOOL, FALSE }, // dump state machines only when they're in a waiting state
4132+
{ "-roots", &roots, COBOOL, FALSE }, // gather GC root information
41314133
#ifndef FEATURE_PAL
4132-
{ "/d", &dml, COBOOL, FALSE }, // Debugger Markup Language
4134+
{ "/d", &dml, COBOOL, FALSE }, // Debugger Markup Language
41334135
#endif
41344136
};
41354137
if (!GetCMDOption(args, option, _countof(option), NULL, 0, &nArg))
41364138
{
4137-
sos::Throw<sos::Exception>("Usage: DumpAsync [-mt MethodTableAddr] [-type TypeName] [-waiting]");
4139+
sos::Throw<sos::Exception>("Usage: DumpAsync [-mt MethodTableAddr] [-type TypeName] [-waiting] [-roots]");
41384140
}
41394141
if (nArg != 0)
41404142
{
@@ -4164,6 +4166,7 @@ DECLARE_API(DumpAsync)
41644166
ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %8s %s\n", "Address", "MT", "Size", "Name");
41654167

41664168
// Walk each heap object looking for async state machine objects.
4169+
BOOL missingStateFieldWarning = FALSE;
41674170
int numStateMachines = 0;
41684171
for (sos::ObjectIterator itr = gcheap.WalkHeap(); !IsInterrupt() && itr != NULL; ++itr)
41694172
{
@@ -4196,7 +4199,7 @@ DECLARE_API(DumpAsync)
41964199
// Get the async state machine object's StateMachine field. If we can't, it's not
41974200
// an async state machine we can handle.
41984201
DacpFieldDescData stateMachineField;
4199-
int stateMachineFieldOffset = GetObjFieldOffset(TO_CDADDR(itr->GetAddress()), itr->GetMT(), W("StateMachine"), TRUE, &stateMachineField);
4202+
int stateMachineFieldOffset = GetObjFieldOffset(TO_CDADDR(itr->GetAddress()), itr->GetMT(), W("StateMachine"), TRUE, FALSE, &stateMachineField);
42004203
if (stateMachineFieldOffset <= 0)
42014204
{
42024205
continue;
@@ -4224,6 +4227,36 @@ DECLARE_API(DumpAsync)
42244227
stateMachineMT = objData.MethodTable; // update from Canon to actual type
42254228
}
42264229

4230+
// Get the current state value of the state machine. If the user has requested to filter down
4231+
// to only those state machines that are currently at an await, compare it against the expected
4232+
// waiting values. This value can also be used in later analysis.
4233+
int stateValue = -2;
4234+
DacpFieldDescData stateField;
4235+
int stateFieldOffset = bStateMachineIsValueType ?
4236+
GetValueFieldOffset(stateMachineMT, W("<>1__state"), &stateField) :
4237+
GetObjFieldOffset(stateMachineAddr, stateMachineMT, W("<>1__state"), TRUE, bStateMachineIsValueType, &stateField);
4238+
if (stateFieldOffset < 0 || (!bStateMachineIsValueType && stateFieldOffset == 0))
4239+
{
4240+
missingStateFieldWarning = TRUE;
4241+
if (waiting)
4242+
{
4243+
// waiting was specified and we couldn't find the field to satisfy the query,
4244+
// so skip this object.
4245+
continue;
4246+
}
4247+
}
4248+
else
4249+
{
4250+
MOVE(stateValue, stateMachineAddr + stateFieldOffset);
4251+
if (waiting && stateValue < 0)
4252+
{
4253+
// 0+ values correspond to the await in linear sequence in the method, so a non-negative
4254+
// value indicates the state machine is at an await. Since we're filtering for waiting,
4255+
// anything else should be skipped.
4256+
continue;
4257+
}
4258+
}
4259+
42274260
// We now have a state machine that's passed all of our criteria. Print out its details.
42284261

42294262
// Print out top level description of the state machine object.
@@ -4261,16 +4294,28 @@ DECLARE_API(DumpAsync)
42614294

42624295
// Finally, output gcroots, as they can serve as call stacks, and also help to highlight
42634296
// state machines that aren't being kept alive.
4264-
ExtOut("GC roots:\n");
4265-
IncrementIndent();
4266-
GCRootImpl gcroot;
4267-
gcroot.PrintRootsForObject(*itr, FALSE, FALSE);
4268-
DecrementIndent();
4297+
if (roots)
4298+
{
4299+
ExtOut("GC roots:\n");
4300+
IncrementIndent();
4301+
GCRootImpl gcroot;
4302+
int numRoots = gcroot.PrintRootsForObject(*itr, FALSE, FALSE);
4303+
DecrementIndent();
4304+
4305+
if (stateValue >= 0 && numRoots == 0)
4306+
{
4307+
ExtOut("Incomplete state machine (<>1__state == %d) with 0 roots.\n", stateValue);
4308+
}
4309+
}
42694310

42704311
ExtOut("\n");
42714312
}
42724313

42734314
ExtOut("\nFound %d state machines.\n", numStateMachines);
4315+
if (missingStateFieldWarning)
4316+
{
4317+
ExtOut("Warning: Could not find a state machine's <>1__state field.\n");
4318+
}
42744319
return S_OK;
42754320
}
42764321
catch (const sos::Exception &e)

src/ToolBox/SOS/Strike/util.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1838,6 +1838,66 @@ int GetObjFieldOffset(CLRDATA_ADDRESS cdaObj, CLRDATA_ADDRESS cdaMT, __in_z LPCW
18381838
#undef EXITPOINT
18391839
}
18401840

1841+
1842+
// Return value: -1 = error
1843+
// -2 = not found
1844+
// >= 0 = offset to field from cdaValue
1845+
int GetValueFieldOffset(CLRDATA_ADDRESS cdaMT, __in_z LPCWSTR wszFieldName, DacpFieldDescData* pDacpFieldDescData)
1846+
{
1847+
#define EXITPOINT(EXPR) do { if(!(EXPR)) { return -1; } } while (0)
1848+
1849+
const int NOT_FOUND = -2;
1850+
DacpMethodTableData dmtd;
1851+
DacpMethodTableFieldData vMethodTableFields;
1852+
DacpFieldDescData vFieldDesc;
1853+
DacpModuleData module;
1854+
static DWORD numInstanceFields = 0; // Static due to recursion visiting parents
1855+
numInstanceFields = 0;
1856+
1857+
EXITPOINT(vMethodTableFields.Request(g_sos, cdaMT) == S_OK);
1858+
1859+
EXITPOINT(dmtd.Request(g_sos, cdaMT) == S_OK);
1860+
EXITPOINT(module.Request(g_sos, dmtd.Module) == S_OK);
1861+
if (dmtd.ParentMethodTable)
1862+
{
1863+
DWORD retVal = GetValueFieldOffset(dmtd.ParentMethodTable, wszFieldName, pDacpFieldDescData);
1864+
if (retVal != NOT_FOUND)
1865+
{
1866+
// Return in case of error or success. Fall through for field-not-found.
1867+
return retVal;
1868+
}
1869+
}
1870+
1871+
CLRDATA_ADDRESS dwAddr = vMethodTableFields.FirstField;
1872+
ToRelease<IMetaDataImport> pImport = MDImportForModule(&module);
1873+
1874+
while (numInstanceFields < vMethodTableFields.wNumInstanceFields)
1875+
{
1876+
EXITPOINT(vFieldDesc.Request(g_sos, dwAddr) == S_OK);
1877+
1878+
if (!vFieldDesc.bIsStatic)
1879+
{
1880+
NameForToken_s(TokenFromRid(vFieldDesc.mb, mdtFieldDef), pImport, g_mdName, mdNameLen, false);
1881+
if (_wcscmp(wszFieldName, g_mdName) == 0)
1882+
{
1883+
if (pDacpFieldDescData != NULL)
1884+
{
1885+
*pDacpFieldDescData = vFieldDesc;
1886+
}
1887+
return vFieldDesc.dwOffset;
1888+
}
1889+
numInstanceFields++;
1890+
}
1891+
1892+
dwAddr = vFieldDesc.NextField;
1893+
}
1894+
1895+
// Field name not found...
1896+
return NOT_FOUND;
1897+
1898+
#undef EXITPOINT
1899+
}
1900+
18411901
// Returns an AppDomain address if AssemblyPtr is loaded into that domain only. Otherwise
18421902
// returns NULL
18431903
CLRDATA_ADDRESS IsInOneDomainOnly(CLRDATA_ADDRESS AssemblyPtr)

src/ToolBox/SOS/Strike/util.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1388,6 +1388,7 @@ void DisplayFields (CLRDATA_ADDRESS cdaMT, DacpMethodTableData *pMTD, DacpMethod
13881388
DWORD_PTR dwStartAddr = 0, BOOL bFirst=TRUE, BOOL bValueClass=FALSE);
13891389
int GetObjFieldOffset(CLRDATA_ADDRESS cdaObj, __in_z LPCWSTR wszFieldName, BOOL bFirst=TRUE);
13901390
int GetObjFieldOffset(CLRDATA_ADDRESS cdaObj, CLRDATA_ADDRESS cdaMT, __in_z LPCWSTR wszFieldName, BOOL bFirst=TRUE, DacpFieldDescData* pDacpFieldDescData=NULL);
1391+
int GetValueFieldOffset(CLRDATA_ADDRESS cdaMT, __in_z LPCWSTR wszFieldName, DacpFieldDescData* pDacpFieldDescData);
13911392

13921393
BOOL IsValidToken(DWORD_PTR ModuleAddr, mdTypeDef mb);
13931394
void NameForToken_s(DacpModuleData *pModule, mdTypeDef mb, __out_ecount (capacity_mdName) WCHAR *mdName, size_t capacity_mdName,

0 commit comments

Comments
 (0)