-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Add debug information for runtime async methods #120303
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 3 commits
b7bb968
d5d0864
d7277fb
6addd0e
6a5bf19
262260a
4e2a829
820afa9
3621eae
f597424
6bb6cee
f580e3f
55b9522
c9c64be
db77446
c808390
3ec5160
bb12c77
bf3364b
b49f38f
d1bf49f
579714e
c056793
9679f91
7a34475
e839409
d8ad0c1
9fdd8a7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -431,4 +431,30 @@ class ICorDebugInfo | |
// Source information about the IL instruction in the inlinee | ||
SourceTypes Source; | ||
}; | ||
|
||
struct AsyncContinuationVarInfo | ||
{ | ||
// IL number of variable (or one of the special IL numbers, like UNKNOWN_ILNUM) | ||
uint32_t VarNumber; | ||
// Offset in continuation's data where this variable is stored | ||
uint32_t Offset; | ||
|
||
}; | ||
|
||
struct AsyncSuspensionPoint | ||
{ | ||
// IL offset in the root method that resulted in the creation of this suspension point. | ||
uint32_t RootILOffset; | ||
// Index of inline tree node containing the IL offset (0 for root) | ||
uint32_t Inlinee; | ||
// IL offset that resulted in the creation of the suspension point. | ||
uint32_t ILOffset; | ||
VSadov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
// Count of AsyncContinuationVarInfo in array of locals | ||
uint32_t NumContinuationVars; | ||
noahfalk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}; | ||
|
||
struct AsyncInfo | ||
{ | ||
// Number of suspension points in the method. | ||
uint32_t NumSuspensionPoints; | ||
VSadov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -760,6 +760,8 @@ PhaseStatus AsyncTransformation::Run() | |
|
||
m_comp->fgInvalidateDfsTree(); | ||
|
||
ReportDebugInfo(); | ||
|
||
return PhaseStatus::MODIFIED_EVERYTHING; | ||
} | ||
|
||
|
@@ -818,6 +820,8 @@ void AsyncTransformation::Transform( | |
BasicBlock* resumeBB = CreateResumption(block, *remainder, call, callDefInfo, stateNum, layout); | ||
|
||
m_resumptionBBs.push_back(resumeBB); | ||
|
||
CreateDebugInfoForSuspensionPoint(call, layout); | ||
} | ||
|
||
//------------------------------------------------------------------------ | ||
|
@@ -2202,6 +2206,76 @@ GenTreeStoreInd* AsyncTransformation::StoreAtOffset(GenTree* base, unsigned offs | |
return store; | ||
} | ||
|
||
//------------------------------------------------------------------------ | ||
// AsyncTransformation::CreateDebugInfoForSuspensionPoint: | ||
// Create debug info for the specific suspension point we just created. | ||
// | ||
// Parameters: | ||
// asyncCall - Call node resulting in the suspension point | ||
// stateNum - State number that was assigned to the suspension point | ||
// layout - Layout of continuation | ||
// | ||
void AsyncTransformation::CreateDebugInfoForSuspensionPoint(GenTreeCall* asyncCall, const ContinuationLayout& layout) | ||
{ | ||
if (!m_comp->opts.compDbgInfo) | ||
{ | ||
return; | ||
} | ||
|
||
uint32_t numLocals = 0; | ||
for (const LiveLocalInfo& local : layout.Locals) | ||
{ | ||
unsigned ilVarNum = m_comp->compMap2ILvarNum(local.LclNum); | ||
if (ilVarNum == ICorDebugInfo::UNKNOWN_ILNUM) | ||
{ | ||
continue; | ||
} | ||
|
||
ICorDebugInfo::AsyncContinuationVarInfo varInf; | ||
varInf.VarNumber = ilVarNum; | ||
varInf.Offset = local.DataOffset; | ||
m_dbgContinuationVars.push_back(varInf); | ||
numLocals++; | ||
} | ||
|
||
ICorDebugInfo::AsyncSuspensionPoint suspensionPoint; | ||
const DebugInfo& di = asyncCall->GetAsyncInfo().DebugInfo; | ||
suspensionPoint.RootILOffset = di.GetRoot().GetLocation().GetOffset(); | ||
suspensionPoint.Inlinee = di.GetInlineContext()->GetOrdinal(); | ||
suspensionPoint.ILOffset = di.GetLocation().GetOffset(); | ||
suspensionPoint.NumContinuationVars = numLocals; | ||
|
||
m_dbgSuspensionPoints.push_back(suspensionPoint); | ||
} | ||
|
||
//------------------------------------------------------------------------ | ||
// AsyncTransformation::ReportDebugInfo: | ||
// Report debug info back to EE. | ||
// | ||
void AsyncTransformation::ReportDebugInfo() | ||
{ | ||
if (!m_comp->opts.compDbgInfo) | ||
|
||
{ | ||
return; | ||
} | ||
|
||
ICorDebugInfo::AsyncInfo asyncInfo; | ||
asyncInfo.NumSuspensionPoints = static_cast<uint32_t>(m_dbgSuspensionPoints.size()); | ||
|
||
ICorDebugInfo::AsyncSuspensionPoint* hostSuspensionPoints = | ||
static_cast<ICorDebugInfo::AsyncSuspensionPoint*>(m_comp->info.compCompHnd->allocateArray( | ||
m_dbgSuspensionPoints.size() * sizeof(ICorDebugInfo::AsyncSuspensionPoint))); | ||
std::copy(m_dbgSuspensionPoints.begin(), m_dbgSuspensionPoints.end(), hostSuspensionPoints); | ||
|
||
ICorDebugInfo::AsyncContinuationVarInfo* hostVars = | ||
static_cast<ICorDebugInfo::AsyncContinuationVarInfo*>(m_comp->info.compCompHnd->allocateArray( | ||
m_dbgContinuationVars.size() * sizeof(ICorDebugInfo::AsyncContinuationVarInfo))); | ||
std::copy(m_dbgContinuationVars.begin(), m_dbgContinuationVars.end(), hostVars); | ||
|
||
m_comp->info.compCompHnd->reportAsyncDebugInfo(&asyncInfo, hostSuspensionPoints, hostVars, | ||
static_cast<uint32_t>(m_dbgContinuationVars.size())); | ||
} | ||
|
||
//------------------------------------------------------------------------ | ||
// AsyncTransformation::GetDataArrayVar: | ||
// Create a new local to hold the data array of the continuation object. This | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Has there been any discussion about the managed return value feature? I'm fine if its out of scope for this PR and maybe it doesn't even land in .NET 11, but didn't want it to be a surprise if it came up later. The feature works for synchronous code but I don't think it is supported for async V1 code. Its another example where the debugging experience currently gets worse when users switch their code from sync to async. I'm not aware of anything that prevents it from working - its just work that has never been done.
At the JIT-EE level managed return value consists of reporting the variable home for the return value of an IL call, even if that return value is only pushed on the IL stack and never assigned an explicit IL local. In async code the natural extension would be reporting the return value storage location of a call that had to be awaited.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if the debugger breaks on the join point of the synchronous/resumption paths, then it can read the return value from the same place as normal. That join point will be the native offset I am working on encoding for the suspension points. So we may already have enough information available after that.