xref: /freebsd/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/Coroutines.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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 #include "lldb/Utility/LLDBLog.h"
15 #include "lldb/Utility/Log.h"
16 
17 using namespace lldb;
18 using namespace lldb_private;
19 using namespace lldb_private::formatters;
20 
GetCoroFramePtrFromHandle(ValueObjectSP valobj_sp)21 static lldb::addr_t GetCoroFramePtrFromHandle(ValueObjectSP valobj_sp) {
22   if (!valobj_sp)
23     return LLDB_INVALID_ADDRESS;
24 
25   // We expect a single pointer in the `coroutine_handle` class.
26   // We don't care about its name.
27   if (valobj_sp->GetNumChildrenIgnoringErrors() != 1)
28     return LLDB_INVALID_ADDRESS;
29   ValueObjectSP ptr_sp(valobj_sp->GetChildAtIndex(0));
30   if (!ptr_sp)
31     return LLDB_INVALID_ADDRESS;
32   if (!ptr_sp->GetCompilerType().IsPointerType())
33     return LLDB_INVALID_ADDRESS;
34 
35   AddressType addr_type;
36   lldb::addr_t frame_ptr_addr = ptr_sp->GetPointerValue(&addr_type);
37   if (!frame_ptr_addr || frame_ptr_addr == LLDB_INVALID_ADDRESS)
38     return LLDB_INVALID_ADDRESS;
39   lldbassert(addr_type == AddressType::eAddressTypeLoad);
40   if (addr_type != AddressType::eAddressTypeLoad)
41     return LLDB_INVALID_ADDRESS;
42 
43   return frame_ptr_addr;
44 }
45 
ExtractDestroyFunction(lldb::TargetSP target_sp,lldb::addr_t frame_ptr_addr)46 static Function *ExtractDestroyFunction(lldb::TargetSP target_sp,
47                                         lldb::addr_t frame_ptr_addr) {
48   lldb::ProcessSP process_sp = target_sp->GetProcessSP();
49   auto ptr_size = process_sp->GetAddressByteSize();
50 
51   Status error;
52   auto destroy_func_ptr_addr = frame_ptr_addr + ptr_size;
53   lldb::addr_t destroy_func_addr =
54       process_sp->ReadPointerFromMemory(destroy_func_ptr_addr, error);
55   if (error.Fail())
56     return nullptr;
57 
58   Address destroy_func_address;
59   if (!target_sp->ResolveLoadAddress(destroy_func_addr, destroy_func_address))
60     return nullptr;
61 
62   return destroy_func_address.CalculateSymbolContextFunction();
63 }
64 
InferPromiseType(Function & destroy_func)65 static CompilerType InferPromiseType(Function &destroy_func) {
66   Block &block = destroy_func.GetBlock(true);
67   auto variable_list = block.GetBlockVariableList(true);
68 
69   // clang generates an artificial `__promise` variable inside the
70   // `destroy` function. Look for it.
71   auto promise_var = variable_list->FindVariable(ConstString("__promise"));
72   if (!promise_var)
73     return {};
74   if (!promise_var->IsArtificial())
75     return {};
76 
77   Type *promise_type = promise_var->GetType();
78   if (!promise_type)
79     return {};
80   return promise_type->GetForwardCompilerType();
81 }
82 
StdlibCoroutineHandleSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)83 bool lldb_private::formatters::StdlibCoroutineHandleSummaryProvider(
84     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
85   lldb::addr_t frame_ptr_addr =
86       GetCoroFramePtrFromHandle(valobj.GetNonSyntheticValue());
87   if (frame_ptr_addr == LLDB_INVALID_ADDRESS)
88     return false;
89 
90   if (frame_ptr_addr == 0) {
91     stream << "nullptr";
92   } else {
93     stream.Printf("coro frame = 0x%" PRIx64, frame_ptr_addr);
94   }
95 
96   return true;
97 }
98 
99 lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd::
StdlibCoroutineHandleSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)100     StdlibCoroutineHandleSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
101     : SyntheticChildrenFrontEnd(*valobj_sp) {
102   if (valobj_sp)
103     Update();
104 }
105 
106 lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd::
107     ~StdlibCoroutineHandleSyntheticFrontEnd() = default;
108 
109 llvm::Expected<uint32_t> lldb_private::formatters::
CalculateNumChildren()110     StdlibCoroutineHandleSyntheticFrontEnd::CalculateNumChildren() {
111   if (!m_resume_ptr_sp || !m_destroy_ptr_sp)
112     return 0;
113 
114   return m_promise_ptr_sp ? 3 : 2;
115 }
116 
117 lldb::ValueObjectSP lldb_private::formatters::
GetChildAtIndex(uint32_t idx)118     StdlibCoroutineHandleSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) {
119   switch (idx) {
120   case 0:
121     return m_resume_ptr_sp;
122   case 1:
123     return m_destroy_ptr_sp;
124   case 2:
125     return m_promise_ptr_sp;
126   }
127   return lldb::ValueObjectSP();
128 }
129 
130 lldb::ChildCacheState
Update()131 lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd::Update() {
132   m_resume_ptr_sp.reset();
133   m_destroy_ptr_sp.reset();
134   m_promise_ptr_sp.reset();
135 
136   ValueObjectSP valobj_sp = m_backend.GetNonSyntheticValue();
137   if (!valobj_sp)
138     return lldb::ChildCacheState::eRefetch;
139 
140   lldb::addr_t frame_ptr_addr = GetCoroFramePtrFromHandle(valobj_sp);
141   if (frame_ptr_addr == 0 || frame_ptr_addr == LLDB_INVALID_ADDRESS)
142     return lldb::ChildCacheState::eRefetch;
143 
144   auto ts = valobj_sp->GetCompilerType().GetTypeSystem();
145   auto ast_ctx = ts.dyn_cast_or_null<TypeSystemClang>();
146   if (!ast_ctx)
147     return lldb::ChildCacheState::eRefetch;
148 
149   // Create the `resume` and `destroy` children.
150   lldb::TargetSP target_sp = m_backend.GetTargetSP();
151   auto &exe_ctx = m_backend.GetExecutionContextRef();
152   lldb::ProcessSP process_sp = target_sp->GetProcessSP();
153   auto ptr_size = process_sp->GetAddressByteSize();
154   CompilerType void_type = ast_ctx->GetBasicType(lldb::eBasicTypeVoid);
155   CompilerType coro_func_type = ast_ctx->CreateFunctionType(
156       /*result_type=*/void_type, /*args=*/&void_type, /*num_args=*/1,
157       /*is_variadic=*/false, /*qualifiers=*/0);
158   CompilerType coro_func_ptr_type = coro_func_type.GetPointerType();
159   m_resume_ptr_sp = CreateValueObjectFromAddress(
160       "resume", frame_ptr_addr + 0 * ptr_size, exe_ctx, coro_func_ptr_type);
161   lldbassert(m_resume_ptr_sp);
162   m_destroy_ptr_sp = CreateValueObjectFromAddress(
163       "destroy", frame_ptr_addr + 1 * ptr_size, exe_ctx, coro_func_ptr_type);
164   lldbassert(m_destroy_ptr_sp);
165 
166   // Get the `promise_type` from the template argument
167   CompilerType promise_type(
168       valobj_sp->GetCompilerType().GetTypeTemplateArgument(0));
169   if (!promise_type)
170     return lldb::ChildCacheState::eRefetch;
171 
172   // Try to infer the promise_type if it was type-erased
173   if (promise_type.IsVoidType()) {
174     if (Function *destroy_func =
175             ExtractDestroyFunction(target_sp, frame_ptr_addr)) {
176       if (CompilerType inferred_type = InferPromiseType(*destroy_func)) {
177         promise_type = inferred_type;
178       }
179     }
180   }
181 
182   // If we don't know the promise type, we don't display the `promise` member.
183   // `CreateValueObjectFromAddress` below would fail for `void` types.
184   if (promise_type.IsVoidType()) {
185     return lldb::ChildCacheState::eRefetch;
186   }
187 
188   // Add the `promise` member. We intentionally add `promise` as a pointer type
189   // instead of a value type, and don't automatically dereference this pointer.
190   // We do so to avoid potential very deep recursion in case there is a cycle
191   // formed between `std::coroutine_handle`s and their promises.
192   lldb::ValueObjectSP promise = CreateValueObjectFromAddress(
193       "promise", frame_ptr_addr + 2 * ptr_size, exe_ctx, promise_type);
194   Status error;
195   lldb::ValueObjectSP promisePtr = promise->AddressOf(error);
196   if (error.Success())
197     m_promise_ptr_sp = promisePtr->Clone(ConstString("promise"));
198 
199   return lldb::ChildCacheState::eRefetch;
200 }
201 
202 bool lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd::
MightHaveChildren()203     MightHaveChildren() {
204   return true;
205 }
206 
GetIndexOfChildWithName(ConstString name)207 size_t StdlibCoroutineHandleSyntheticFrontEnd::GetIndexOfChildWithName(
208     ConstString name) {
209   if (!m_resume_ptr_sp || !m_destroy_ptr_sp)
210     return UINT32_MAX;
211 
212   if (name == ConstString("resume"))
213     return 0;
214   if (name == ConstString("destroy"))
215     return 1;
216   if (name == ConstString("promise_ptr") && m_promise_ptr_sp)
217     return 2;
218 
219   return UINT32_MAX;
220 }
221 
222 SyntheticChildrenFrontEnd *
StdlibCoroutineHandleSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)223 lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEndCreator(
224     CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
225   return (valobj_sp ? new StdlibCoroutineHandleSyntheticFrontEnd(valobj_sp)
226                     : nullptr);
227 }
228