LLDB mainline
Coroutines.cpp
Go to the documentation of this file.
1//===-- Coroutines.cpp ----------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "Coroutines.h"
10
14
15using namespace lldb;
16using namespace lldb_private;
17using namespace lldb_private::formatters;
18
20 if (!valobj_sp)
22
23 // We expect a single pointer in the `coroutine_handle` class.
24 // We don't care about its name.
25 if (valobj_sp->GetNumChildren() != 1)
27 ValueObjectSP ptr_sp(valobj_sp->GetChildAtIndex(0));
28 if (!ptr_sp)
30 if (!ptr_sp->GetCompilerType().IsPointerType())
32
33 AddressType addr_type;
34 lldb::addr_t frame_ptr_addr = ptr_sp->GetPointerValue(&addr_type);
35 if (!frame_ptr_addr || frame_ptr_addr == LLDB_INVALID_ADDRESS)
37 lldbassert(addr_type == AddressType::eAddressTypeLoad);
38 if (addr_type != AddressType::eAddressTypeLoad)
40
41 return frame_ptr_addr;
42}
43
45 lldb::addr_t frame_ptr_addr) {
46 lldb::ProcessSP process_sp = target_sp->GetProcessSP();
47 auto ptr_size = process_sp->GetAddressByteSize();
48
50 auto destroy_func_ptr_addr = frame_ptr_addr + ptr_size;
51 lldb::addr_t destroy_func_addr =
52 process_sp->ReadPointerFromMemory(destroy_func_ptr_addr, error);
53 if (error.Fail())
54 return nullptr;
55
56 Address destroy_func_address;
57 if (!target_sp->ResolveLoadAddress(destroy_func_addr, destroy_func_address))
58 return nullptr;
59
60 return destroy_func_address.CalculateSymbolContextFunction();
61}
62
63static CompilerType InferPromiseType(Function &destroy_func) {
64 Block &block = destroy_func.GetBlock(true);
65 auto variable_list = block.GetBlockVariableList(true);
66
67 // clang generates an artificial `__promise` variable inside the
68 // `destroy` function. Look for it.
69 auto promise_var = variable_list->FindVariable(ConstString("__promise"));
70 if (!promise_var)
71 return {};
72 if (!promise_var->IsArtificial())
73 return {};
74
75 Type *promise_type = promise_var->GetType();
76 if (!promise_type)
77 return {};
78 return promise_type->GetForwardCompilerType();
79}
80
82 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
83 lldb::addr_t frame_ptr_addr =
85 if (frame_ptr_addr == LLDB_INVALID_ADDRESS)
86 return false;
87
88 if (frame_ptr_addr == 0) {
89 stream << "nullptr";
90 } else {
91 stream.Printf("coro frame = 0x%" PRIx64, frame_ptr_addr);
92 }
93
94 return true;
95}
96
99 : SyntheticChildrenFrontEnd(*valobj_sp) {
100 if (valobj_sp)
101 Update();
102}
103
106
109 if (!m_resume_ptr_sp || !m_destroy_ptr_sp)
110 return 0;
111
112 return m_promise_ptr_sp ? 3 : 2;
113}
114
117 switch (idx) {
118 case 0:
119 return m_resume_ptr_sp;
120 case 1:
121 return m_destroy_ptr_sp;
122 case 2:
123 return m_promise_ptr_sp;
124 }
125 return lldb::ValueObjectSP();
126}
127
130 m_resume_ptr_sp.reset();
131 m_destroy_ptr_sp.reset();
132 m_promise_ptr_sp.reset();
133
134 ValueObjectSP valobj_sp = m_backend.GetNonSyntheticValue();
135 if (!valobj_sp)
137
138 lldb::addr_t frame_ptr_addr = GetCoroFramePtrFromHandle(valobj_sp);
139 if (frame_ptr_addr == 0 || frame_ptr_addr == LLDB_INVALID_ADDRESS)
141
142 auto ts = valobj_sp->GetCompilerType().GetTypeSystem();
143 auto ast_ctx = ts.dyn_cast_or_null<TypeSystemClang>();
144 if (!ast_ctx)
146
147 // Create the `resume` and `destroy` children.
148 lldb::TargetSP target_sp = m_backend.GetTargetSP();
149 auto &exe_ctx = m_backend.GetExecutionContextRef();
150 lldb::ProcessSP process_sp = target_sp->GetProcessSP();
151 auto ptr_size = process_sp->GetAddressByteSize();
152 CompilerType void_type = ast_ctx->GetBasicType(lldb::eBasicTypeVoid);
153 CompilerType coro_func_type = ast_ctx->CreateFunctionType(
154 /*result_type=*/void_type, /*args=*/&void_type, /*num_args=*/1,
155 /*is_variadic=*/false, /*qualifiers=*/0);
156 CompilerType coro_func_ptr_type = coro_func_type.GetPointerType();
157 m_resume_ptr_sp = CreateValueObjectFromAddress(
158 "resume", frame_ptr_addr + 0 * ptr_size, exe_ctx, coro_func_ptr_type);
159 lldbassert(m_resume_ptr_sp);
160 m_destroy_ptr_sp = CreateValueObjectFromAddress(
161 "destroy", frame_ptr_addr + 1 * ptr_size, exe_ctx, coro_func_ptr_type);
162 lldbassert(m_destroy_ptr_sp);
163
164 // Get the `promise_type` from the template argument
165 CompilerType promise_type(
166 valobj_sp->GetCompilerType().GetTypeTemplateArgument(0));
167 if (!promise_type)
169
170 // Try to infer the promise_type if it was type-erased
171 if (promise_type.IsVoidType()) {
172 if (Function *destroy_func =
173 ExtractDestroyFunction(target_sp, frame_ptr_addr)) {
174 if (CompilerType inferred_type = InferPromiseType(*destroy_func)) {
175 promise_type = inferred_type;
176 }
177 }
178 }
179
180 // If we don't know the promise type, we don't display the `promise` member.
181 // `CreateValueObjectFromAddress` below would fail for `void` types.
182 if (promise_type.IsVoidType()) {
184 }
185
186 // Add the `promise` member. We intentionally add `promise` as a pointer type
187 // instead of a value type, and don't automatically dereference this pointer.
188 // We do so to avoid potential very deep recursion in case there is a cycle
189 // formed between `std::coroutine_handle`s and their promises.
190 lldb::ValueObjectSP promise = CreateValueObjectFromAddress(
191 "promise", frame_ptr_addr + 2 * ptr_size, exe_ctx, promise_type);
193 lldb::ValueObjectSP promisePtr = promise->AddressOf(error);
194 if (error.Success())
195 m_promise_ptr_sp = promisePtr->Clone(ConstString("promise"));
196
198}
199
202 return true;
203}
204
206 ConstString name) {
208 return UINT32_MAX;
209
210 if (name == ConstString("resume"))
211 return 0;
212 if (name == ConstString("destroy"))
213 return 1;
214 if (name == ConstString("promise_ptr") && m_promise_ptr_sp)
215 return 2;
216
217 return UINT32_MAX;
218}
219
223 return (valobj_sp ? new StdlibCoroutineHandleSyntheticFrontEnd(valobj_sp)
224 : nullptr);
225}
static llvm::raw_ostream & error(Stream &strm)
static Function * ExtractDestroyFunction(lldb::TargetSP target_sp, lldb::addr_t frame_ptr_addr)
Definition: Coroutines.cpp:44
static lldb::addr_t GetCoroFramePtrFromHandle(ValueObjectSP valobj_sp)
Definition: Coroutines.cpp:19
static CompilerType InferPromiseType(Function &destroy_func)
Definition: Coroutines.cpp:63
#define lldbassert(x)
Definition: LLDBAssert.h:15
A section + offset based address class.
Definition: Address.h:62
Function * CalculateSymbolContextFunction() const
Definition: Address.cpp:871
A class that describes a single lexical block.
Definition: Block.h:41
lldb::VariableListSP GetBlockVariableList(bool can_create)
Get the variable list for this block only.
Definition: Block.cpp:399
Generic representation of a type in a programming language.
Definition: CompilerType.h:36
CompilerType GetPointerType() const
Return a new CompilerType that is a pointer to this type.
A uniqued constant string class.
Definition: ConstString.h:40
A class that describes a function.
Definition: Function.h:399
Block & GetBlock(bool can_create)
Get accessor for the block list.
Definition: Function.cpp:370
An error handling class.
Definition: Status.h:44
A stream class that can stream formatted output to a file.
Definition: Stream.h:28
size_t Printf(const char *format,...) __attribute__((format(printf
Output printf formatted output to the stream.
Definition: Stream.cpp:134
A TypeSystem implementation based on Clang.
CompilerType GetForwardCompilerType()
Definition: Type.cpp:751
virtual lldb::ValueObjectSP GetNonSyntheticValue()
Definition: ValueObject.h:576
Synthetic children frontend for std::coroutine_handle<promise_type> from libc++, libstdc++ and MSVC S...
Definition: Coroutines.h:31
StdlibCoroutineHandleSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
Definition: Coroutines.cpp:98
lldb::ValueObjectSP GetChildAtIndex(size_t idx) override
Definition: Coroutines.cpp:116
lldb::ChildCacheState Update() override
This function is assumed to always succeed and if it fails, the front-end should know to deal with it...
Definition: Coroutines.cpp:129
#define LLDB_INVALID_ADDRESS
Definition: lldb-defines.h:82
#define UINT32_MAX
Definition: lldb-defines.h:19
SyntheticChildrenFrontEnd * StdlibCoroutineHandleSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP)
Definition: Coroutines.cpp:221
bool StdlibCoroutineHandleSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options)
Summary provider for std::coroutine_handle<T> from libc++, libstdc++ and MSVC STL.
Definition: Coroutines.cpp:81
A class that represents a running process on the host machine.
Definition: SBAttachInfo.h:14
Definition: SBAddress.h:15
ChildCacheState
Specifies if children need to be re-computed after a call to SyntheticChildrenFrontEnd::Update.
@ eRefetch
Children need to be recomputed dynamically.
std::shared_ptr< lldb_private::ValueObject > ValueObjectSP
Definition: lldb-forward.h:472
std::shared_ptr< lldb_private::Process > ProcessSP
Definition: lldb-forward.h:381
uint64_t addr_t
Definition: lldb-types.h:79
std::shared_ptr< lldb_private::Target > TargetSP
Definition: lldb-forward.h:436