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