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
11#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
12#include "lldb/Symbol/Function.h"
13#include "lldb/Symbol/VariableList.h"
14
15using namespace lldb;
16using namespace lldb_private;
17using namespace lldb_private::formatters;
18
19static lldb::addr_t GetCoroFramePtrFromHandle(ValueObjectSP valobj_sp) {
20 if (!valobj_sp)
21 return LLDB_INVALID_ADDRESS;
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)
26 return LLDB_INVALID_ADDRESS;
27 ValueObjectSP ptr_sp(valobj_sp->GetChildAtIndex(idx: 0));
28 if (!ptr_sp)
29 return LLDB_INVALID_ADDRESS;
30 if (!ptr_sp->GetCompilerType().IsPointerType())
31 return LLDB_INVALID_ADDRESS;
32
33 auto [frame_ptr_addr, addr_type] = ptr_sp->GetPointerValue();
34 if (!frame_ptr_addr || frame_ptr_addr == LLDB_INVALID_ADDRESS)
35 return LLDB_INVALID_ADDRESS;
36 lldbassert(addr_type == AddressType::eAddressTypeLoad);
37 if (addr_type != AddressType::eAddressTypeLoad)
38 return LLDB_INVALID_ADDRESS;
39
40 return frame_ptr_addr;
41}
42
43static Function *ExtractDestroyFunction(lldb::TargetSP target_sp,
44 lldb::addr_t frame_ptr_addr) {
45 lldb::ProcessSP process_sp = target_sp->GetProcessSP();
46 auto ptr_size = process_sp->GetAddressByteSize();
47
48 Status error;
49 auto destroy_func_ptr_addr = frame_ptr_addr + ptr_size;
50 lldb::addr_t destroy_func_addr =
51 process_sp->ReadPointerFromMemory(vm_addr: destroy_func_ptr_addr, error);
52 if (error.Fail())
53 return nullptr;
54
55 Address destroy_func_address;
56 if (!target_sp->ResolveLoadAddress(load_addr: destroy_func_addr, so_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.
64static CompilerType InferArtificialCoroType(Function *destroy_func,
65 ConstString var_name) {
66 if (!destroy_func)
67 return {};
68
69 Block &block = destroy_func->GetBlock(can_create: true);
70 auto variable_list = block.GetBlockVariableList(can_create: true);
71
72 auto var = variable_list->FindVariable(name: 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
84bool lldb_private::formatters::StdlibCoroutineHandleSummaryProvider(
85 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
86 lldb::addr_t frame_ptr_addr =
87 GetCoroFramePtrFromHandle(valobj_sp: valobj.GetNonSyntheticValue());
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(format: "coro frame = 0x%" PRIx64, frame_ptr_addr);
95 }
96
97 return true;
98}
99
100lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd::
101 StdlibCoroutineHandleSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
102 : SyntheticChildrenFrontEnd(*valobj_sp) {
103 if (valobj_sp)
104 Update();
105}
106
107lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd::
108 ~StdlibCoroutineHandleSyntheticFrontEnd() = default;
109
110llvm::Expected<uint32_t> lldb_private::formatters::
111 StdlibCoroutineHandleSyntheticFrontEnd::CalculateNumChildren() {
112 return m_children.size();
113}
114
115lldb::ValueObjectSP lldb_private::formatters::
116 StdlibCoroutineHandleSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) {
117 return idx < m_children.size() ? m_children[idx] : lldb::ValueObjectSP();
118}
119
120lldb::ChildCacheState
121lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd::Update() {
122 m_children.clear();
123
124 ValueObjectSP valobj_sp = m_backend.GetNonSyntheticValue();
125 if (!valobj_sp)
126 return lldb::ChildCacheState::eRefetch;
127
128 lldb::addr_t frame_ptr_addr = GetCoroFramePtrFromHandle(valobj_sp);
129 if (frame_ptr_addr == 0 || frame_ptr_addr == LLDB_INVALID_ADDRESS)
130 return lldb::ChildCacheState::eRefetch;
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)
138 return lldb::ChildCacheState::eRefetch;
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(type: lldb::eBasicTypeVoid);
145 CompilerType promise_type;
146 if (CompilerType template_arg =
147 valobj_sp->GetCompilerType().GetTypeTemplateArgument(idx: 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, var_name: ConstString("__promise"))) {
154 promise_type = inferred_type;
155 }
156 }
157 }
158 CompilerType coro_frame_type =
159 InferArtificialCoroType(destroy_func, var_name: 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=*/type_quals: 0);
168 CompilerType coro_func_ptr_type = coro_func_type.GetPointerType();
169 ValueObjectSP resume_ptr_sp = CreateValueObjectFromAddress(
170 name: "resume", address: frame_ptr_addr + 0 * ptr_size, exe_ctx, type: coro_func_ptr_type);
171 assert(resume_ptr_sp);
172 m_children.push_back(x: std::move(resume_ptr_sp));
173 ValueObjectSP destroy_ptr_sp = CreateValueObjectFromAddress(
174 name: "destroy", address: frame_ptr_addr + 1 * ptr_size, exe_ctx, type: coro_func_ptr_type);
175 assert(destroy_ptr_sp);
176 m_children.push_back(x: 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.
184 ValueObjectSP promise_ptr_sp = CreateValueObjectFromAddress(
185 name: "promise", address: frame_ptr_addr + 2 * ptr_size, exe_ctx,
186 type: promise_type.GetPointerType(), /*do_deref=*/false);
187 m_children.push_back(x: std::move(promise_ptr_sp));
188 ValueObjectSP coroframe_ptr_sp = CreateValueObjectFromAddress(
189 name: "coro_frame", address: frame_ptr_addr, exe_ctx, type: coro_frame_type.GetPointerType(),
190 /*do_deref=*/false);
191 m_children.push_back(x: std::move(coroframe_ptr_sp));
192
193 return lldb::ChildCacheState::eRefetch;
194}
195
196llvm::Expected<size_t>
197StdlibCoroutineHandleSyntheticFrontEnd::GetIndexOfChildWithName(
198 ConstString name) {
199 for (const auto &[idx, child_sp] : llvm::enumerate(First&: m_children)) {
200 if (child_sp->GetName() == name)
201 return idx;
202 }
203
204 return llvm::createStringError(Fmt: "Type has no child named '%s'",
205 Vals: name.AsCString());
206}
207
208SyntheticChildrenFrontEnd *
209lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEndCreator(
210 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
211 return (valobj_sp ? new StdlibCoroutineHandleSyntheticFrontEnd(valobj_sp)
212 : nullptr);
213}
214

source code of lldb/source/Plugins/Language/CPlusPlus/Coroutines.cpp