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
15 using namespace lldb;
16 using namespace lldb_private;
17 using namespace lldb_private::formatters;
18
GetCoroFramePtrFromHandle(ValueObjectSP valobj_sp)19 static 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(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
ExtractDestroyFunction(lldb::TargetSP target_sp,lldb::addr_t frame_ptr_addr)43 static 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(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.
InferArtificialCoroType(Function * destroy_func,ConstString var_name)64 static CompilerType InferArtificialCoroType(Function *destroy_func,
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
StdlibCoroutineHandleSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)84 bool lldb_private::formatters::StdlibCoroutineHandleSummaryProvider(
85 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
86 lldb::addr_t frame_ptr_addr =
87 GetCoroFramePtrFromHandle(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("coro frame = 0x%" PRIx64, frame_ptr_addr);
95 }
96
97 return true;
98 }
99
100 lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd::
StdlibCoroutineHandleSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)101 StdlibCoroutineHandleSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
102 : SyntheticChildrenFrontEnd(*valobj_sp) {
103 if (valobj_sp)
104 Update();
105 }
106
107 lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd::
108 ~StdlibCoroutineHandleSyntheticFrontEnd() = default;
109
110 llvm::Expected<uint32_t> lldb_private::formatters::
CalculateNumChildren()111 StdlibCoroutineHandleSyntheticFrontEnd::CalculateNumChildren() {
112 return m_children.size();
113 }
114
115 lldb::ValueObjectSP lldb_private::formatters::
GetChildAtIndex(uint32_t idx)116 StdlibCoroutineHandleSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) {
117 return idx < m_children.size() ? m_children[idx] : lldb::ValueObjectSP();
118 }
119
120 lldb::ChildCacheState
Update()121 lldb_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(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();
169 ValueObjectSP resume_ptr_sp = CreateValueObjectFromAddress(
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));
173 ValueObjectSP destroy_ptr_sp = CreateValueObjectFromAddress(
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.
184 ValueObjectSP promise_ptr_sp = CreateValueObjectFromAddress(
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));
188 ValueObjectSP coroframe_ptr_sp = CreateValueObjectFromAddress(
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
193 return lldb::ChildCacheState::eRefetch;
194 }
195
196 llvm::Expected<size_t>
GetIndexOfChildWithName(ConstString name)197 StdlibCoroutineHandleSyntheticFrontEnd::GetIndexOfChildWithName(
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
208 SyntheticChildrenFrontEnd *
StdlibCoroutineHandleSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)209 lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEndCreator(
210 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
211 return (valobj_sp ? new StdlibCoroutineHandleSyntheticFrontEnd(valobj_sp)
212 : nullptr);
213 }
214