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
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
63// clang generates aritifical `__promise` and `__coro_frame` variables inside
64// the destroy function. Look for those variables and extract their type.
66 ConstString var_name) {
67 if (!destroy_func)
68 return {};
69
70 Block &block = destroy_func->GetBlock(true);
71 auto variable_list = block.GetBlockVariableList(true);
72
73 auto var = variable_list->FindVariable(var_name);
74 if (!var)
75 return {};
76 if (!var->IsArtificial())
77 return {};
78
79 Type *promise_type = var->GetType();
80 if (!promise_type)
81 return {};
82 return promise_type->GetForwardCompilerType();
83}
84
86 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
87 lldb::addr_t frame_ptr_addr =
89 if (frame_ptr_addr == LLDB_INVALID_ADDRESS)
90 return false;
91
92 if (frame_ptr_addr == 0) {
93 stream << "nullptr";
94 } else {
95 stream.Printf("coro frame = 0x%" PRIx64, frame_ptr_addr);
96 }
97
98 return true;
99}
100
107
110
115
120
123 m_children.clear();
124
125 ValueObjectSP valobj_sp = m_backend.GetNonSyntheticValue();
126 if (!valobj_sp)
128
129 lldb::addr_t frame_ptr_addr = GetCoroFramePtrFromHandle(valobj_sp);
130 if (frame_ptr_addr == 0 || frame_ptr_addr == LLDB_INVALID_ADDRESS)
132
133 lldb::TargetSP target_sp = m_backend.GetTargetSP();
134 auto &exe_ctx = m_backend.GetExecutionContextRef();
135 lldb::ProcessSP process_sp = target_sp->GetProcessSP();
136 auto ptr_size = process_sp->GetAddressByteSize();
137 auto ast_ctx = valobj_sp->GetCompilerType().GetTypeSystem<TypeSystemClang>();
138 if (!ast_ctx)
140
141 // Determine the coroutine frame type and the promise type. Fall back
142 // to `void`, since even the pointer itself might be useful, even if the
143 // type inference failed.
144 Function *destroy_func = ExtractDestroyFunction(target_sp, frame_ptr_addr);
145 CompilerType void_type = ast_ctx->GetBasicType(lldb::eBasicTypeVoid);
146 CompilerType promise_type;
147 if (CompilerType template_arg =
148 valobj_sp->GetCompilerType().GetTypeTemplateArgument(0))
149 promise_type = std::move(template_arg);
150 if (promise_type.IsVoidType()) {
151 // Try to infer the promise_type if it was type-erased
152 if (destroy_func) {
153 if (CompilerType inferred_type =
154 InferArtificialCoroType(destroy_func, ConstString("__promise"))) {
155 promise_type = inferred_type;
156 }
157 }
158 }
159 CompilerType coro_frame_type =
160 InferArtificialCoroType(destroy_func, ConstString("__coro_frame"));
161 if (!coro_frame_type)
162 coro_frame_type = void_type;
163
164 // Create the `resume` and `destroy` children.
165 std::array<CompilerType, 1> args{coro_frame_type};
166 CompilerType coro_func_type = ast_ctx->CreateFunctionType(
167 /*result_type=*/void_type, args,
168 /*is_variadic=*/false, /*qualifiers=*/0);
169 CompilerType coro_func_ptr_type = coro_func_type.GetPointerType();
171 "resume", frame_ptr_addr + 0 * ptr_size, exe_ctx, coro_func_ptr_type);
172 assert(resume_ptr_sp);
173 m_children.push_back(std::move(resume_ptr_sp));
175 "destroy", frame_ptr_addr + 1 * ptr_size, exe_ctx, coro_func_ptr_type);
176 assert(destroy_ptr_sp);
177 m_children.push_back(std::move(destroy_ptr_sp));
178
179 // Add promise and coro_frame
180 // Add the `promise` and `coro_frame` member. We intentionally add them as
181 // pointer types instead of a value type, and don't automatically dereference
182 // those pointers. We do so to avoid potential very deep recursion in case
183 // there is a cycle formed between `std::coroutine_handle`s and their
184 // promises.
186 "promise", frame_ptr_addr + 2 * ptr_size, exe_ctx,
187 promise_type.GetPointerType(), /*do_deref=*/false);
188 m_children.push_back(std::move(promise_ptr_sp));
190 "coro_frame", frame_ptr_addr, exe_ctx, coro_frame_type.GetPointerType(),
191 /*do_deref=*/false);
192 m_children.push_back(std::move(coroframe_ptr_sp));
193
195}
196
197llvm::Expected<size_t>
199 ConstString name) {
200 for (const auto &[idx, child_sp] : llvm::enumerate(m_children)) {
201 if (child_sp->GetName() == name)
202 return idx;
203 }
204
205 return llvm::createStringErrorV("type has no child named '{0}'", name);
206}
207
211 return (valobj_sp ? new StdlibCoroutineHandleSyntheticFrontEnd(valobj_sp)
212 : nullptr);
213}
static llvm::raw_ostream & error(Stream &strm)
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:859
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:392
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:400
Block & GetBlock(bool can_create)
Get accessor for the block list.
Definition Function.cpp:383
An error handling class.
Definition Status.h:118
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:132
SyntheticChildrenFrontEnd(ValueObject &backend)
lldb::ValueObjectSP CreateValueObjectFromAddress(llvm::StringRef name, uint64_t address, const ExecutionContext &exe_ctx, CompilerType type, bool do_deref=true)
A TypeSystem implementation based on Clang.
CompilerType GetForwardCompilerType()
Definition Type.cpp:782
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