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 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 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 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 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:: 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:: 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:: 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 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:: 203 MightHaveChildren() { 204 return true; 205 } 206 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 * 223 lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEndCreator( 224 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { 225 return (valobj_sp ? new StdlibCoroutineHandleSyntheticFrontEnd(valobj_sp) 226 : nullptr); 227 } 228