diff --git a/lldb/source/Plugins/SymbolFile/CTF/SymbolFileCTF.h b/lldb/source/Plugins/SymbolFile/CTF/SymbolFileCTF.h --- a/lldb/source/Plugins/SymbolFile/CTF/SymbolFileCTF.h +++ b/lldb/source/Plugins/SymbolFile/CTF/SymbolFileCTF.h @@ -93,7 +93,7 @@ return std::nullopt; } - bool CompleteType(CompilerType &compiler_type) override { return false; } + bool CompleteType(CompilerType &compiler_type) override; uint32_t ResolveSymbolContext(const lldb_private::Address &so_addr, lldb::SymbolContextItem resolve_scope, @@ -247,6 +247,11 @@ std::vector> m_ctf_types; + /// To complete types, we need a way to map (imcomplete) compiler types back + /// to parsed CTF types. + llvm::DenseMap + m_compiler_types; + llvm::DenseMap m_types; std::vector m_functions; diff --git a/lldb/source/Plugins/SymbolFile/CTF/SymbolFileCTF.cpp b/lldb/source/Plugins/SymbolFile/CTF/SymbolFileCTF.cpp --- a/lldb/source/Plugins/SymbolFile/CTF/SymbolFileCTF.cpp +++ b/lldb/source/Plugins/SymbolFile/CTF/SymbolFileCTF.cpp @@ -503,26 +503,59 @@ llvm::Expected SymbolFileCTF::CreateRecord(const CTFRecord &ctf_record) { const clang::TagTypeKind tag_kind = TranslateRecordKind(ctf_record.kind); - CompilerType record_type = m_ast->CreateRecordType(nullptr, OptionalClangModuleID(), eAccessPublic, ctf_record.name.data(), tag_kind, eLanguageTypeC); + m_compiler_types[record_type.GetOpaqueQualType()] = &ctf_record; + Declaration decl; + return MakeType(ctf_record.uid, ConstString(ctf_record.name), ctf_record.size, + nullptr, LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, + decl, record_type, lldb_private::Type::ResolveState::Forward); +} + +bool SymbolFileCTF::CompleteType(CompilerType &compiler_type) { + // Check if we have a CTF type for the given incomplete compiler type. + auto it = m_compiler_types.find(compiler_type.GetOpaqueQualType()); + if (it == m_compiler_types.end()) + return false; + + const CTFType *ctf_type = it->second; + assert(ctf_type && "m_compiler_types should only contain valid CTF types"); + + // We only support resolving record types. + assert(ctf_type->kind == CTFType::Kind::eStruct || + ctf_type->kind == CTFType::Kind::eUnion); - m_ast->StartTagDeclarationDefinition(record_type); - for (const CTFRecord::Field &field : ctf_record.fields) { - if (Type *field_type = ResolveTypeUID(field.type)) { - const uint32_t field_size = field_type->GetByteSize(nullptr).value_or(0); - TypeSystemClang::AddFieldToRecordType(record_type, field.name, - field_type->GetFullCompilerType(), - eAccessPublic, field_size); + // Cast to the appropriate CTF type. + const CTFRecord *ctf_record = static_cast(ctf_type); + + // If any of the fields are incomplete, we cannot complete the type. + for (const CTFRecord::Field &field : ctf_record->fields) { + if (!ResolveTypeUID(field.type)) { + LLDB_LOG(GetLog(LLDBLog::Symbols), + "Cannot complete type {0} because field {1} is incomplete", + ctf_type->uid, field.type); + return false; } } - m_ast->CompleteTagDeclarationDefinition(record_type); - Declaration decl; - return MakeType(ctf_record.uid, ConstString(ctf_record.name), ctf_record.size, - nullptr, LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, - decl, record_type, lldb_private::Type::ResolveState::Full); + // Complete the record type. + m_ast->StartTagDeclarationDefinition(compiler_type); + for (const CTFRecord::Field &field : ctf_record->fields) { + Type *field_type = ResolveTypeUID(field.type); + assert(field_type && "field must be complete"); + const uint32_t field_size = field_type->GetByteSize(nullptr).value_or(0); + TypeSystemClang::AddFieldToRecordType(compiler_type, field.name, + field_type->GetFullCompilerType(), + eAccessPublic, field_size); + } + m_ast->CompleteTagDeclarationDefinition(compiler_type); + + // Now that the compiler type is no longer incomplete we don't need to + // remember it anymore. + m_compiler_types.erase(compiler_type.GetOpaqueQualType()); + + return true; } llvm::Expected @@ -960,7 +993,6 @@ if (!ctf_type) return nullptr; - m_types[type_uid] = TypeSP(); Log *log = GetLog(LLDBLog::Symbols); llvm::Expected type_or_error = CreateType(ctf_type); diff --git a/lldb/test/API/macosx/ctf/TestCTF.py b/lldb/test/API/macosx/ctf/TestCTF.py --- a/lldb/test/API/macosx/ctf/TestCTF.py +++ b/lldb/test/API/macosx/ctf/TestCTF.py @@ -91,3 +91,5 @@ "}", ], ) + + self.expect("type lookup RecursiveStruct", substrs=["RecursiveStruct *n;"]) diff --git a/lldb/test/API/macosx/ctf/test.c b/lldb/test/API/macosx/ctf/test.c --- a/lldb/test/API/macosx/ctf/test.c +++ b/lldb/test/API/macosx/ctf/test.c @@ -36,9 +36,14 @@ int b; }; +struct RecursiveStruct { + struct RecursiveStruct *n; +}; + MyStructT foo; struct ForwardDecl *forward; struct LargeStruct bar; +struct RecursiveStruct ke; void populate(MyInt i) { foo.n.i = i; @@ -52,6 +57,7 @@ foo.f = NULL; forward = NULL; bar.b = i; + ke.n = NULL; } int main(int argc, char** argv) {