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/ExpressionParser/Clang/ClangASTImporter.h" 12 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" 13 #include "lldb/Symbol/Function.h" 14 #include "lldb/Symbol/VariableList.h" 15 16 using namespace lldb; 17 using namespace lldb_private; 18 using namespace lldb_private::formatters; 19 20 static ValueObjectSP GetCoroFramePtrFromHandle(ValueObject &valobj) { 21 ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); 22 if (!valobj_sp) 23 return nullptr; 24 25 // We expect a single pointer in the `coroutine_handle` class. 26 // We don't care about its name. 27 if (valobj_sp->GetNumChildren() != 1) 28 return nullptr; 29 ValueObjectSP ptr_sp(valobj_sp->GetChildAtIndex(0, true)); 30 if (!ptr_sp) 31 return nullptr; 32 if (!ptr_sp->GetCompilerType().IsPointerType()) 33 return nullptr; 34 35 return ptr_sp; 36 } 37 38 static Function *ExtractDestroyFunction(ValueObjectSP &frame_ptr_sp) { 39 lldb::TargetSP target_sp = frame_ptr_sp->GetTargetSP(); 40 lldb::ProcessSP process_sp = frame_ptr_sp->GetProcessSP(); 41 auto ptr_size = process_sp->GetAddressByteSize(); 42 43 AddressType addr_type; 44 lldb::addr_t frame_ptr_addr = frame_ptr_sp->GetPointerValue(&addr_type); 45 if (!frame_ptr_addr || frame_ptr_addr == LLDB_INVALID_ADDRESS) 46 return nullptr; 47 lldbassert(addr_type == AddressType::eAddressTypeLoad); 48 49 Status error; 50 // The destroy pointer is the 2nd pointer inside the compiler-generated 51 // `pair<resumePtr,destroyPtr>`. 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 Function *destroy_func = 63 destroy_func_address.CalculateSymbolContextFunction(); 64 if (!destroy_func) 65 return nullptr; 66 67 return destroy_func; 68 } 69 70 static CompilerType InferPromiseType(Function &destroy_func) { 71 Block &block = destroy_func.GetBlock(true); 72 auto variable_list = block.GetBlockVariableList(true); 73 74 // clang generates an artificial `__promise` variable inside the 75 // `destroy` function. Look for it. 76 auto promise_var = variable_list->FindVariable(ConstString("__promise")); 77 if (!promise_var) 78 return {}; 79 if (!promise_var->IsArtificial()) 80 return {}; 81 82 Type *promise_type = promise_var->GetType(); 83 if (!promise_type) 84 return {}; 85 return promise_type->GetForwardCompilerType(); 86 } 87 88 static CompilerType GetCoroutineFrameType(TypeSystemClang &ast_ctx, 89 CompilerType promise_type) { 90 CompilerType void_type = ast_ctx.GetBasicType(lldb::eBasicTypeVoid); 91 CompilerType coro_func_type = ast_ctx.CreateFunctionType( 92 /*result_type=*/void_type, /*args=*/&void_type, /*num_args=*/1, 93 /*is_variadic=*/false, /*qualifiers=*/0); 94 CompilerType coro_abi_type; 95 if (promise_type.IsVoidType()) { 96 coro_abi_type = ast_ctx.CreateStructForIdentifier( 97 ConstString(), {{"resume", coro_func_type.GetPointerType()}, 98 {"destroy", coro_func_type.GetPointerType()}}); 99 } else { 100 coro_abi_type = ast_ctx.CreateStructForIdentifier( 101 ConstString(), {{"resume", coro_func_type.GetPointerType()}, 102 {"destroy", coro_func_type.GetPointerType()}, 103 {"promise", promise_type}}); 104 } 105 return coro_abi_type; 106 } 107 108 bool lldb_private::formatters::StdlibCoroutineHandleSummaryProvider( 109 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 110 ValueObjectSP ptr_sp(GetCoroFramePtrFromHandle(valobj)); 111 if (!ptr_sp) 112 return false; 113 114 if (!ptr_sp->GetValueAsUnsigned(0)) { 115 stream << "nullptr"; 116 } else { 117 stream.Printf("coro frame = 0x%" PRIx64, ptr_sp->GetValueAsUnsigned(0)); 118 } 119 return true; 120 } 121 122 lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd:: 123 StdlibCoroutineHandleSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) 124 : SyntheticChildrenFrontEnd(*valobj_sp), 125 m_ast_importer(std::make_unique<ClangASTImporter>()) { 126 if (valobj_sp) 127 Update(); 128 } 129 130 lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd:: 131 ~StdlibCoroutineHandleSyntheticFrontEnd() = default; 132 133 size_t lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd:: 134 CalculateNumChildren() { 135 if (!m_frame_ptr_sp) 136 return 0; 137 138 return m_frame_ptr_sp->GetNumChildren(); 139 } 140 141 lldb::ValueObjectSP lldb_private::formatters:: 142 StdlibCoroutineHandleSyntheticFrontEnd::GetChildAtIndex(size_t idx) { 143 if (!m_frame_ptr_sp) 144 return lldb::ValueObjectSP(); 145 146 return m_frame_ptr_sp->GetChildAtIndex(idx, true); 147 } 148 149 bool lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd:: 150 Update() { 151 m_frame_ptr_sp.reset(); 152 153 ValueObjectSP valobj_sp = m_backend.GetSP(); 154 if (!valobj_sp) 155 return false; 156 157 ValueObjectSP ptr_sp(GetCoroFramePtrFromHandle(m_backend)); 158 if (!ptr_sp) 159 return false; 160 161 // Get the `promise_type` from the template argument 162 CompilerType promise_type( 163 valobj_sp->GetCompilerType().GetTypeTemplateArgument(0)); 164 if (!promise_type) 165 return false; 166 167 // Try to infer the promise_type if it was type-erased 168 auto ts = valobj_sp->GetCompilerType().GetTypeSystem(); 169 auto ast_ctx = ts.dyn_cast_or_null<TypeSystemClang>(); 170 if (!ast_ctx) 171 return false; 172 if (promise_type.IsVoidType()) { 173 if (Function *destroy_func = ExtractDestroyFunction(ptr_sp)) { 174 if (CompilerType inferred_type = InferPromiseType(*destroy_func)) { 175 // Copy the type over to the correct `TypeSystemClang` instance 176 promise_type = m_ast_importer->CopyType(*ast_ctx, inferred_type); 177 } 178 } 179 } 180 181 // Build the coroutine frame type 182 CompilerType coro_frame_type = GetCoroutineFrameType(*ast_ctx, promise_type); 183 184 m_frame_ptr_sp = ptr_sp->Cast(coro_frame_type.GetPointerType()); 185 186 return false; 187 } 188 189 bool lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd:: 190 MightHaveChildren() { 191 return true; 192 } 193 194 size_t StdlibCoroutineHandleSyntheticFrontEnd::GetIndexOfChildWithName( 195 ConstString name) { 196 if (!m_frame_ptr_sp) 197 return UINT32_MAX; 198 199 return m_frame_ptr_sp->GetIndexOfChildWithName(name); 200 } 201 202 SyntheticChildrenFrontEnd * 203 lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEndCreator( 204 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { 205 return (valobj_sp ? new StdlibCoroutineHandleSyntheticFrontEnd(valobj_sp) 206 : nullptr); 207 } 208