Skip to content

Commit 8eef12d

Browse files
committed
[debuginfo][coro] Emit debug info labels for coroutine resume points
With this commit, we add `DILabel` debug infos (lowering to DW_TAG_label in DWARF) to the various resume points of a coroutine. Those labels use the naming convention `__coro_resume_<N>` where `<N>` is the suspension point id. That way, debugging scripts can figure out the exact line / column at which a coroutine was suspended by looking up current `__coro_index` value inside the coroutines frame, and then searching for the corresponding `__coro_resume_<N>` inside the coroutine's resume function. While this is an artificial compiler-generated label, I did not apply the DW_AT_artificial tag to it. The DWARFv5 standard only allows that tag on type and variable definitions, but not on labels. In gdb, those line numebers can then be looked up using the command `info line -function my_coroutine -label __coro_resume_1`. LLDB unfortunately does not parse DW_TAG_label debug information, yet. As such, this debug information is currently not useful in LLDB.
1 parent 73c4929 commit 8eef12d

File tree

3 files changed

+47
-12
lines changed

3 files changed

+47
-12
lines changed

clang/docs/DebuggingCoroutines.rst

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -311,17 +311,25 @@ variables in the C++ source.
311311
Get the suspended points
312312
========================
313313

314-
An important requirement for debugging coroutines is to understand suspended
315-
points, which are where the coroutine is currently suspended and awaiting.
314+
An important requirement for debugging coroutines is to understand at which
315+
point a coroutine is currently suspended and awaiting.
316316

317-
For simple cases like the above, inspecting the value of the `__coro_index`
318-
variable in the coroutine frame works well.
317+
The value of the `__coro_index` inside a couroutine frame indicates the current
318+
suspension point. To map this id back to a source code location, you can lookup
319+
the source location of a special, compiler-generated label `__coro_suspend_<N>`.
319320

320-
However, it is not quite so simple in really complex situations. In these
321-
cases, it is necessary to use the coroutine libraries to insert the
322-
line-number.
321+
::
323322

324-
For example:
323+
(gdb) info line -function coro_task -label __coro_resume_2
324+
Line 45 of "llvm-example.cpp" starts at address 0x1b1b <_ZL9coro_taski.resume+555> and ends at 0x1b46 <_ZL9coro_taski.resume+598>.
325+
Line 45 of "llvm-example.cpp" starts at address 0x201b <_ZL9coro_taski.destroy+555> and ends at 0x2046 <_ZL9coro_taski.destroy+598>.
326+
Line 45 of "llvm-example.cpp" starts at address 0x253b <_ZL9coro_taski.cleanup+555> and ends at 0x2566 <_ZL9coro_taski.cleanup+598>.
327+
328+
Older versions of LLVM/clang might not yet emit those labels, though. For
329+
simple cases, you might still be able to guess the suspension point correctly.
330+
331+
Alternatively, you might also want to modify your coroutine library to store
332+
the line number of the current suspension point in the promise:
325333

326334
.. code-block:: c++
327335

llvm/lib/Transforms/Coroutines/CoroFrame.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -689,9 +689,9 @@ static DIType *solveDIType(DIBuilder &Builder, Type *Ty,
689689
static void buildFrameDebugInfo(Function &F, coro::Shape &Shape,
690690
FrameDataInfo &FrameData) {
691691
DISubprogram *DIS = F.getSubprogram();
692-
// If there is no DISubprogram for F, it implies the Function are not compiled
693-
// with debug info. So we also don't need to generate debug info for the frame
694-
// neither.
692+
// If there is no DISubprogram for F, it implies the function is compiled
693+
// without debug info. So we also don't generate debug info for the frame,
694+
// either.
695695
if (!DIS || !DIS->getUnit() ||
696696
!dwarf::isCPlusPlus(
697697
(dwarf::SourceLanguage)DIS->getUnit()->getSourceLanguage()) ||

llvm/lib/Transforms/Coroutines/CoroSplit.cpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include "llvm/IR/CFG.h"
4343
#include "llvm/IR/CallingConv.h"
4444
#include "llvm/IR/Constants.h"
45+
#include "llvm/IR/DIBuilder.h"
4546
#include "llvm/IR/DataLayout.h"
4647
#include "llvm/IR/DebugInfo.h"
4748
#include "llvm/IR/DerivedTypes.h"
@@ -302,7 +303,7 @@ static void replaceFallthroughCoroEnd(AnyCoroEndInst *End,
302303
}
303304

304305
// Mark a coroutine as done, which implies that the coroutine is finished and
305-
// never get resumed.
306+
// never gets resumed.
306307
//
307308
// In resume-switched ABI, the done state is represented by storing zero in
308309
// ResumeFnAddr.
@@ -1492,6 +1493,18 @@ struct SwitchCoroutineSplitter {
14921493
static void createResumeEntryBlock(Function &F, coro::Shape &Shape) {
14931494
LLVMContext &C = F.getContext();
14941495

1496+
DIBuilder DBuilder(*F.getParent(), /*AllowUnresolved*/ false);
1497+
DISubprogram *DIS = F.getSubprogram();
1498+
// If there is no DISubprogram for F, it implies the function is compiled
1499+
// without debug info. So we also don't generate debug info for the
1500+
// suspension points, either.
1501+
bool AddDebugLabels =
1502+
(DIS && DIS->getUnit() &&
1503+
(DIS->getUnit()->getEmissionKind() ==
1504+
DICompileUnit::DebugEmissionKind::FullDebug ||
1505+
DIS->getUnit()->getEmissionKind() ==
1506+
DICompileUnit::DebugEmissionKind::LineTablesOnly));
1507+
14951508
// resume.entry:
14961509
// %index.addr = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32
14971510
// 0, i32 2 % index = load i32, i32* %index.addr switch i32 %index, label
@@ -1514,6 +1527,7 @@ struct SwitchCoroutineSplitter {
15141527
Builder.CreateSwitch(Index, UnreachBB, Shape.CoroSuspends.size());
15151528
Shape.SwitchLowering.ResumeSwitch = Switch;
15161529

1530+
// Split all coro.suspend calls
15171531
size_t SuspendIndex = 0;
15181532
for (auto *AnyS : Shape.CoroSuspends) {
15191533
auto *S = cast<CoroSuspendInst>(AnyS);
@@ -1552,6 +1566,7 @@ struct SwitchCoroutineSplitter {
15521566
// br label %resume.0.landing
15531567
//
15541568
// resume.0: ; <--- jump from the switch in the resume.entry
1569+
// XXX: label
15551570
// %0 = tail call i8 @llvm.coro.suspend(token none, i1 false)
15561571
// br label %resume.0.landing
15571572
//
@@ -1574,11 +1589,23 @@ struct SwitchCoroutineSplitter {
15741589
PN->addIncoming(Builder.getInt8(-1), SuspendBB);
15751590
PN->addIncoming(S, ResumeBB);
15761591

1592+
if (AddDebugLabels) {
1593+
if (DebugLoc SuspendLoc = S->getDebugLoc()) {
1594+
std::string LabelName =
1595+
("__coro_resume_" + Twine(SuspendIndex)).str();
1596+
DILocation& DILoc = *SuspendLoc.get();
1597+
DILabel *ResumeLabel = DBuilder.createLabel(
1598+
DIS, LabelName, DILoc.getFile(), SuspendLoc.getLine());
1599+
DBuilder.insertLabel(ResumeLabel, &DILoc, ResumeBB->begin());
1600+
}
1601+
}
1602+
15771603
++SuspendIndex;
15781604
}
15791605

15801606
Builder.SetInsertPoint(UnreachBB);
15811607
Builder.CreateUnreachable();
1608+
DBuilder.finalize();
15821609

15831610
Shape.SwitchLowering.ResumeEntryBlock = NewEntry;
15841611
}

0 commit comments

Comments
 (0)