xref: /freebsd/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/Coroutines.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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