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#include "llvm/Support/ErrorExtras.h"
15
16using namespace lldb;
17using namespace lldb_private;
18using namespace lldb_private::formatters;
19
21 if (!valobj_sp)
23
24 // We expect a single pointer in the `coroutine_handle` class.
25 // We don't care about its name.
26 if (valobj_sp->GetNumChildrenIgnoringErrors() != 1)
28 ValueObjectSP ptr_sp(valobj_sp->GetChildAtIndex(0));
29 if (!ptr_sp)
31 if (!ptr_sp->GetCompilerType().IsPointerType())
33
34 auto [frame_ptr_addr, addr_type] = ptr_sp->GetPointerValue();
35 if (!frame_ptr_addr || frame_ptr_addr == LLDB_INVALID_ADDRESS)
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
49 auto destroy_func_ptr_addr = frame_ptr_addr + ptr_size;
50 llvm::Expected<lldb::addr_t> destroy_func_addr =
51 process_sp->ReadPointerFromMemory(destroy_func_ptr_addr);
52 if (!destroy_func_addr) {
53 llvm::consumeError(destroy_func_addr.takeError());
54 return nullptr;
55 }
56
57 Address destroy_func_address;
58 if (!target_sp->ResolveLoadAddress(*destroy_func_addr, destroy_func_address))
59 return nullptr;
60
61 return destroy_func_address.CalculateSymbolContextFunction();
62}
63
64// clang generates aritifical `__promise` and `__coro_frame` variables inside
65// the destroy function. Look for those variables and extract their type.
67 ConstString var_name) {
68 if (!destroy_func)
69 return {};
70
71 Block &block = destroy_func->GetBlock(true);
72 auto variable_list = block.GetBlockVariableList(true);
73
74 auto var = variable_list->FindVariable(var_name);
75 if (!var)
76 return {};
77 if (!var->IsArtificial())
78 return {};
79
80 Type *promise_type = var->GetType();
81 if (!promise_type)
82 return {};
83 return promise_type->GetForwardCompilerType();
84}
85
87 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
88 lldb::addr_t frame_ptr_addr =
90 if (frame_ptr_addr == LLDB_INVALID_ADDRESS)
91 return false;
92
93 if (frame_ptr_addr == 0) {
94 stream << "nullptr";
95 } else {
96 stream.Printf("coro frame = 0x%" PRIx64, frame_ptr_addr);
97 }
98
99 return true;
100}
101
108
111
116
121
124 m_children.clear();
125
126 ValueObjectSP valobj_sp = m_backend.GetNonSyntheticValue();
127 if (!valobj_sp)
129
130 lldb::addr_t frame_ptr_addr = GetCoroFramePtrFromHandle(valobj_sp);
131 if (frame_ptr_addr == 0 || frame_ptr_addr == LLDB_INVALID_ADDRESS)
133
134 lldb::TargetSP target_sp = m_backend.GetTargetSP();
135 auto &exe_ctx = m_backend.GetExecutionContextRef();
136 lldb::ProcessSP process_sp = target_sp->GetProcessSP();
137 auto ptr_size = process_sp->GetAddressByteSize();
138 auto ast_ctx = valobj_sp->GetCompilerType().GetTypeSystem<TypeSystemClang>();
139 if (!ast_ctx)
141
142 // Determine the coroutine frame type and the promise type. Fall back
143 // to `void`, since even the pointer itself might be useful, even if the
144 // type inference failed.
145 Function *destroy_func = ExtractDestroyFunction(target_sp, frame_ptr_addr);
146 CompilerType void_type = ast_ctx->GetBasicType(lldb::eBasicTypeVoid);
147 CompilerType promise_type;
148 if (CompilerType template_arg =
149 valobj_sp->GetCompilerType().GetTypeTemplateArgument(0))
150 promise_type = std::move(template_arg);
151 if (promise_type.IsVoidType()) {
152 // Try to infer the promise_type if it was type-erased
153 if (destroy_func) {
154 if (CompilerType inferred_type =
155 InferArtificialCoroType(destroy_func, ConstString("__promise"))) {
156 promise_type = inferred_type;
157 }
158 }
159 }
160 CompilerType coro_frame_type =
161 InferArtificialCoroType(destroy_func, ConstString("__coro_frame"));
162 if (!coro_frame_type)
163 coro_frame_type = void_type;
164
165 // Create the `resume` and `destroy` children.
166 std::array<CompilerType, 1> args{coro_frame_type};
167 CompilerType coro_func_type = ast_ctx->CreateFunctionType(
168 /*result_type=*/void_type, args,
169 /*is_variadic=*/false, /*qualifiers=*/0);
170 CompilerType coro_func_ptr_type = coro_func_type.GetPointerType();
172 "resume", frame_ptr_addr + 0 * ptr_size, exe_ctx, coro_func_ptr_type);
173 assert(resume_ptr_sp);
174 m_children.push_back(std::move(resume_ptr_sp));
176 "destroy", frame_ptr_addr + 1 * ptr_size, exe_ctx, coro_func_ptr_type);
177 assert(destroy_ptr_sp);
178 m_children.push_back(std::move(destroy_ptr_sp));
179
180 // Add promise and coro_frame
181 // Add the `promise` and `coro_frame` member. We intentionally add them as
182 // pointer types instead of a value type, and don't automatically dereference
183 // those pointers. We do so to avoid potential very deep recursion in case
184 // there is a cycle formed between `std::coroutine_handle`s and their
185 // promises.
187 "promise", frame_ptr_addr + 2 * ptr_size, exe_ctx,
188 promise_type.GetPointerType(), /*do_deref=*/false);
189 m_children.push_back(std::move(promise_ptr_sp));
191 "coro_frame", frame_ptr_addr, exe_ctx, coro_frame_type.GetPointerType(),
192 /*do_deref=*/false);
193 m_children.push_back(std::move(coroframe_ptr_sp));
194
196}
197
198llvm::Expected<size_t>
200 ConstString name) {
201 for (const auto &[idx, child_sp] : llvm::enumerate(m_children)) {
202 if (child_sp->GetName() == name)
203 return idx;
204 }
205
206 return llvm::createStringErrorV("type has no child named '{0}'", name);
207}
208
212 return (valobj_sp ? new StdlibCoroutineHandleSyntheticFrontEnd(valobj_sp)
213 : nullptr);
214}
static CompilerType InferArtificialCoroType(Function *destroy_func, ConstString var_name)
static Function * ExtractDestroyFunction(lldb::TargetSP target_sp, lldb::addr_t frame_ptr_addr)
static lldb::addr_t GetCoroFramePtrFromHandle(ValueObjectSP valobj_sp)
#define lldbassert(x)
Definition LLDBAssert.h:16
A section + offset based address class.
Definition Address.h:62
Function * CalculateSymbolContextFunction() const
Definition Address.cpp:860
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:382
Generic representation of a type in a programming language.
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:377
Block & GetBlock(bool can_create)
Get accessor for the block list.
Definition Function.cpp:403
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
lldb::ValueObjectSP CreateChildValueObjectFromAddress(llvm::StringRef name, uint64_t address, const ExecutionContext &exe_ctx, CompilerType type, bool do_deref=true)
SyntheticChildrenFrontEnd(ValueObject &backend)
A TypeSystem implementation based on Clang.
CompilerType GetForwardCompilerType()
Definition Type.cpp:791
virtual lldb::ValueObjectSP GetNonSyntheticValue()
Synthetic children frontend for std::coroutine_handle<promise_type> from libc++, libstdc++ and MSVC S...
Definition Coroutines.h:31
llvm::Expected< size_t > GetIndexOfChildWithName(ConstString name) override
Determine the index of a named child.
lldb::ChildCacheState Update() override
This function is assumed to always succeed and if it fails, the front-end should know to deal with it...
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override
#define LLDB_INVALID_ADDRESS
SyntheticChildrenFrontEnd * StdlibCoroutineHandleSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP)
bool StdlibCoroutineHandleSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options)
Summary provider for std::coroutine_handle<T> from libc++, libstdc++ and MSVC STL.
A class that represents a running process on the host machine.
@ eAddressTypeLoad
Address is an address as in the current target inferior process.
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
std::shared_ptr< lldb_private::Process > ProcessSP
uint64_t addr_t
Definition lldb-types.h:80
std::shared_ptr< lldb_private::Target > TargetSP