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->GetNumChildrenIgnoringErrors() != 1)
27 ValueObjectSP ptr_sp(valobj_sp->GetChildAtIndex(0));
28 if (!ptr_sp)
30 if (!ptr_sp->GetCompilerType().IsPointerType())
32
33 auto [frame_ptr_addr, addr_type] = ptr_sp->GetPointerValue();
34 if (!frame_ptr_addr || frame_ptr_addr == LLDB_INVALID_ADDRESS)
37 if (addr_type != AddressType::eAddressTypeLoad)
39
40 return frame_ptr_addr;
41}
42
44 lldb::addr_t frame_ptr_addr) {
45 lldb::ProcessSP process_sp = target_sp->GetProcessSP();
46 auto ptr_size = process_sp->GetAddressByteSize();
47
49 auto destroy_func_ptr_addr = frame_ptr_addr + ptr_size;
50 lldb::addr_t destroy_func_addr =
51 process_sp->ReadPointerFromMemory(destroy_func_ptr_addr, error);
52 if (error.Fail())
53 return nullptr;
54
55 Address destroy_func_address;
56 if (!target_sp->ResolveLoadAddress(destroy_func_addr, destroy_func_address))
57 return nullptr;
58
59 return destroy_func_address.CalculateSymbolContextFunction();
60}
61
62// clang generates aritifical `__promise` and `__coro_frame` variables inside
63// the destroy function. Look for those variables and extract their type.
65 ConstString var_name) {
66 if (!destroy_func)
67 return {};
68
69 Block &block = destroy_func->GetBlock(true);
70 auto variable_list = block.GetBlockVariableList(true);
71
72 auto var = variable_list->FindVariable(var_name);
73 if (!var)
74 return {};
75 if (!var->IsArtificial())
76 return {};
77
78 Type *promise_type = var->GetType();
79 if (!promise_type)
80 return {};
81 return promise_type->GetForwardCompilerType();
82}
83
85 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
86 lldb::addr_t frame_ptr_addr =
88 if (frame_ptr_addr == LLDB_INVALID_ADDRESS)
89 return false;
90
91 if (frame_ptr_addr == 0) {
92 stream << "nullptr";
93 } else {
94 stream.Printf("coro frame = 0x%" PRIx64, frame_ptr_addr);
95 }
96
97 return true;
98}
99
106
109
114
119
122 m_children.clear();
123
124 ValueObjectSP valobj_sp = m_backend.GetNonSyntheticValue();
125 if (!valobj_sp)
127
128 lldb::addr_t frame_ptr_addr = GetCoroFramePtrFromHandle(valobj_sp);
129 if (frame_ptr_addr == 0 || frame_ptr_addr == LLDB_INVALID_ADDRESS)
131
132 lldb::TargetSP target_sp = m_backend.GetTargetSP();
133 auto &exe_ctx = m_backend.GetExecutionContextRef();
134 lldb::ProcessSP process_sp = target_sp->GetProcessSP();
135 auto ptr_size = process_sp->GetAddressByteSize();
136 auto ast_ctx = valobj_sp->GetCompilerType().GetTypeSystem<TypeSystemClang>();
137 if (!ast_ctx)
139
140 // Determine the coroutine frame type and the promise type. Fall back
141 // to `void`, since even the pointer itself might be useful, even if the
142 // type inference failed.
143 Function *destroy_func = ExtractDestroyFunction(target_sp, frame_ptr_addr);
144 CompilerType void_type = ast_ctx->GetBasicType(lldb::eBasicTypeVoid);
145 CompilerType promise_type;
146 if (CompilerType template_arg =
147 valobj_sp->GetCompilerType().GetTypeTemplateArgument(0))
148 promise_type = std::move(template_arg);
149 if (promise_type.IsVoidType()) {
150 // Try to infer the promise_type if it was type-erased
151 if (destroy_func) {
152 if (CompilerType inferred_type =
153 InferArtificialCoroType(destroy_func, ConstString("__promise"))) {
154 promise_type = inferred_type;
155 }
156 }
157 }
158 CompilerType coro_frame_type =
159 InferArtificialCoroType(destroy_func, ConstString("__coro_frame"));
160 if (!coro_frame_type)
161 coro_frame_type = void_type;
162
163 // Create the `resume` and `destroy` children.
164 std::array<CompilerType, 1> args{coro_frame_type};
165 CompilerType coro_func_type = ast_ctx->CreateFunctionType(
166 /*result_type=*/void_type, args,
167 /*is_variadic=*/false, /*qualifiers=*/0);
168 CompilerType coro_func_ptr_type = coro_func_type.GetPointerType();
170 "resume", frame_ptr_addr + 0 * ptr_size, exe_ctx, coro_func_ptr_type);
171 assert(resume_ptr_sp);
172 m_children.push_back(std::move(resume_ptr_sp));
174 "destroy", frame_ptr_addr + 1 * ptr_size, exe_ctx, coro_func_ptr_type);
175 assert(destroy_ptr_sp);
176 m_children.push_back(std::move(destroy_ptr_sp));
177
178 // Add promise and coro_frame
179 // Add the `promise` and `coro_frame` member. We intentionally add them as
180 // pointer types instead of a value type, and don't automatically dereference
181 // those pointers. We do so to avoid potential very deep recursion in case
182 // there is a cycle formed between `std::coroutine_handle`s and their
183 // promises.
185 "promise", frame_ptr_addr + 2 * ptr_size, exe_ctx,
186 promise_type.GetPointerType(), /*do_deref=*/false);
187 m_children.push_back(std::move(promise_ptr_sp));
189 "coro_frame", frame_ptr_addr, exe_ctx, coro_frame_type.GetPointerType(),
190 /*do_deref=*/false);
191 m_children.push_back(std::move(coroframe_ptr_sp));
192
194}
195
196llvm::Expected<size_t>
198 ConstString name) {
199 for (const auto &[idx, child_sp] : llvm::enumerate(m_children)) {
200 if (child_sp->GetName() == name)
201 return idx;
202 }
203
204 return llvm::createStringError("Type has no child named '%s'",
205 name.AsCString());
206}
207
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: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:392
Generic representation of a type in a programming language.
CompilerType GetTypeTemplateArgument(size_t idx, bool expand_pack=false) const
CompilerType GetPointerType() const
Return a new CompilerType that is a pointer to this type.
A uniqued constant string class.
Definition ConstString.h:40
const char * AsCString(const char *value_if_empty=nullptr) const
Get the string value as a C string.
A class that describes a function.
Definition Function.h:400
Block & GetBlock(bool can_create)
Get accessor for the block list.
Definition Function.cpp:382
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:134
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
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