214 lines · cpp
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-exception6//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 15using namespace lldb;16using namespace lldb_private;17using namespace lldb_private::formatters;18 19static 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->GetNumChildrenIgnoringErrors() != 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 auto [frame_ptr_addr, addr_type] = ptr_sp->GetPointerValue();34 if (!frame_ptr_addr || frame_ptr_addr == LLDB_INVALID_ADDRESS)35 return LLDB_INVALID_ADDRESS;36 lldbassert(addr_type == AddressType::eAddressTypeLoad);37 if (addr_type != AddressType::eAddressTypeLoad)38 return LLDB_INVALID_ADDRESS;39 40 return frame_ptr_addr;41}42 43static Function *ExtractDestroyFunction(lldb::TargetSP target_sp,44 lldb::addr_t frame_ptr_addr) {45 lldb::ProcessSP process_sp = target_sp->GetProcessSP();46 auto ptr_size = process_sp->GetAddressByteSize();47 48 Status error;49 auto destroy_func_ptr_addr = frame_ptr_addr + ptr_size;50 lldb::addr_t destroy_func_addr =51 process_sp->ReadPointerFromMemory(destroy_func_ptr_addr, error);52 if (error.Fail())53 return nullptr;54 55 Address destroy_func_address;56 if (!target_sp->ResolveLoadAddress(destroy_func_addr, destroy_func_address))57 return nullptr;58 59 return destroy_func_address.CalculateSymbolContextFunction();60}61 62// clang generates aritifical `__promise` and `__coro_frame` variables inside63// the destroy function. Look for those variables and extract their type.64static CompilerType InferArtificialCoroType(Function *destroy_func,65 ConstString var_name) {66 if (!destroy_func)67 return {};68 69 Block &block = destroy_func->GetBlock(true);70 auto variable_list = block.GetBlockVariableList(true);71 72 auto var = variable_list->FindVariable(var_name);73 if (!var)74 return {};75 if (!var->IsArtificial())76 return {};77 78 Type *promise_type = var->GetType();79 if (!promise_type)80 return {};81 return promise_type->GetForwardCompilerType();82}83 84bool lldb_private::formatters::StdlibCoroutineHandleSummaryProvider(85 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {86 lldb::addr_t frame_ptr_addr =87 GetCoroFramePtrFromHandle(valobj.GetNonSyntheticValue());88 if (frame_ptr_addr == LLDB_INVALID_ADDRESS)89 return false;90 91 if (frame_ptr_addr == 0) {92 stream << "nullptr";93 } else {94 stream.Printf("coro frame = 0x%" PRIx64, frame_ptr_addr);95 }96 97 return true;98}99 100lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd::101 StdlibCoroutineHandleSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)102 : SyntheticChildrenFrontEnd(*valobj_sp) {103 if (valobj_sp)104 Update();105}106 107lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd::108 ~StdlibCoroutineHandleSyntheticFrontEnd() = default;109 110llvm::Expected<uint32_t> lldb_private::formatters::111 StdlibCoroutineHandleSyntheticFrontEnd::CalculateNumChildren() {112 return m_children.size();113}114 115lldb::ValueObjectSP lldb_private::formatters::116 StdlibCoroutineHandleSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) {117 return idx < m_children.size() ? m_children[idx] : lldb::ValueObjectSP();118}119 120lldb::ChildCacheState121lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd::Update() {122 m_children.clear();123 124 ValueObjectSP valobj_sp = m_backend.GetNonSyntheticValue();125 if (!valobj_sp)126 return lldb::ChildCacheState::eRefetch;127 128 lldb::addr_t frame_ptr_addr = GetCoroFramePtrFromHandle(valobj_sp);129 if (frame_ptr_addr == 0 || frame_ptr_addr == LLDB_INVALID_ADDRESS)130 return lldb::ChildCacheState::eRefetch;131 132 lldb::TargetSP target_sp = m_backend.GetTargetSP();133 auto &exe_ctx = m_backend.GetExecutionContextRef();134 lldb::ProcessSP process_sp = target_sp->GetProcessSP();135 auto ptr_size = process_sp->GetAddressByteSize();136 auto ast_ctx = valobj_sp->GetCompilerType().GetTypeSystem<TypeSystemClang>();137 if (!ast_ctx)138 return lldb::ChildCacheState::eRefetch;139 140 // Determine the coroutine frame type and the promise type. Fall back141 // to `void`, since even the pointer itself might be useful, even if the142 // type inference failed.143 Function *destroy_func = ExtractDestroyFunction(target_sp, frame_ptr_addr);144 CompilerType void_type = ast_ctx->GetBasicType(lldb::eBasicTypeVoid);145 CompilerType promise_type;146 if (CompilerType template_arg =147 valobj_sp->GetCompilerType().GetTypeTemplateArgument(0))148 promise_type = std::move(template_arg);149 if (promise_type.IsVoidType()) {150 // Try to infer the promise_type if it was type-erased151 if (destroy_func) {152 if (CompilerType inferred_type =153 InferArtificialCoroType(destroy_func, ConstString("__promise"))) {154 promise_type = inferred_type;155 }156 }157 }158 CompilerType coro_frame_type =159 InferArtificialCoroType(destroy_func, ConstString("__coro_frame"));160 if (!coro_frame_type)161 coro_frame_type = void_type;162 163 // Create the `resume` and `destroy` children.164 std::array<CompilerType, 1> args{coro_frame_type};165 CompilerType coro_func_type = ast_ctx->CreateFunctionType(166 /*result_type=*/void_type, args,167 /*is_variadic=*/false, /*qualifiers=*/0);168 CompilerType coro_func_ptr_type = coro_func_type.GetPointerType();169 ValueObjectSP resume_ptr_sp = CreateValueObjectFromAddress(170 "resume", frame_ptr_addr + 0 * ptr_size, exe_ctx, coro_func_ptr_type);171 assert(resume_ptr_sp);172 m_children.push_back(std::move(resume_ptr_sp));173 ValueObjectSP destroy_ptr_sp = CreateValueObjectFromAddress(174 "destroy", frame_ptr_addr + 1 * ptr_size, exe_ctx, coro_func_ptr_type);175 assert(destroy_ptr_sp);176 m_children.push_back(std::move(destroy_ptr_sp));177 178 // Add promise and coro_frame179 // Add the `promise` and `coro_frame` member. We intentionally add them as180 // pointer types instead of a value type, and don't automatically dereference181 // those pointers. We do so to avoid potential very deep recursion in case182 // there is a cycle formed between `std::coroutine_handle`s and their183 // promises.184 ValueObjectSP promise_ptr_sp = CreateValueObjectFromAddress(185 "promise", frame_ptr_addr + 2 * ptr_size, exe_ctx,186 promise_type.GetPointerType(), /*do_deref=*/false);187 m_children.push_back(std::move(promise_ptr_sp));188 ValueObjectSP coroframe_ptr_sp = CreateValueObjectFromAddress(189 "coro_frame", frame_ptr_addr, exe_ctx, coro_frame_type.GetPointerType(),190 /*do_deref=*/false);191 m_children.push_back(std::move(coroframe_ptr_sp));192 193 return lldb::ChildCacheState::eRefetch;194}195 196llvm::Expected<size_t>197StdlibCoroutineHandleSyntheticFrontEnd::GetIndexOfChildWithName(198 ConstString name) {199 for (const auto &[idx, child_sp] : llvm::enumerate(m_children)) {200 if (child_sp->GetName() == name)201 return idx;202 }203 204 return llvm::createStringError("Type has no child named '%s'",205 name.AsCString());206}207 208SyntheticChildrenFrontEnd *209lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEndCreator(210 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {211 return (valobj_sp ? new StdlibCoroutineHandleSyntheticFrontEnd(valobj_sp)212 : nullptr);213}214