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