llvm-project/lldb/source/Symbol/SymbolFileOnDemand.cpp
Michael Buch 0f7e10b027
[lldb] Add filter option to AST dump command (#142164)
Depends on https://github.com/llvm/llvm-project/pull/142163

This patch makes the `-ast-dump-filter` Clang option available to the
`target modules dump ast` command. This allows us to selectively dump
parts of the AST by name.

The AST can quickly grow way too large to skim on the console. This will
aid in debugging AST related issues.

Example:
```
(lldb) target modules dump ast --filter func
Dumping clang ast for 48 modules.
Dumping func:
FunctionDecl 0xc4b785008 <<invalid sloc>> <invalid sloc> func 'void (int)' extern
|-ParmVarDecl 0xc4b7853d8 <<invalid sloc>> <invalid sloc> x 'int'
`-AsmLabelAttr 0xc4b785358 <<invalid sloc>> Implicit "_Z4funcIiEvT_"

Dumping func<int>:
FunctionDecl 0xc4b7850b8 <<invalid sloc>> <invalid sloc> func<int> 'void (int)' implicit_instantiation extern
|-TemplateArgument type 'int'
| `-BuiltinType 0xc4b85b110 'int'
`-ParmVarDecl 0xc4b7853d8 <<invalid sloc>> <invalid sloc> x 'int'
```

The majority of this patch is adjust the `Dump` API. The main change in
behaviour is in `TypeSystemClang::Dump`, where we now use the
`ASTPrinter` for dumping the `TranslationUnitDecl`. This is where the
`-ast-dump-filter` functionality lives in Clang.
2025-06-02 10:55:04 +01:00

595 lines
20 KiB
C++

//===-- SymbolFileOnDemand.cpp ---------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "lldb/Symbol/SymbolFileOnDemand.h"
#include "lldb/Core/Module.h"
#include "lldb/Symbol/SymbolFile.h"
#include <memory>
#include <optional>
using namespace lldb;
using namespace lldb_private;
char SymbolFileOnDemand::ID;
SymbolFileOnDemand::SymbolFileOnDemand(
std::unique_ptr<SymbolFile> &&symbol_file)
: m_sym_file_impl(std::move(symbol_file)) {}
SymbolFileOnDemand::~SymbolFileOnDemand() = default;
uint32_t SymbolFileOnDemand::CalculateAbilities() {
// Explicitly allow ability checking to pass though.
// This should be a cheap operation.
return m_sym_file_impl->CalculateAbilities();
}
std::recursive_mutex &SymbolFileOnDemand::GetModuleMutex() const {
return m_sym_file_impl->GetModuleMutex();
}
void SymbolFileOnDemand::InitializeObject() {
if (!m_debug_info_enabled) {
LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(),
__FUNCTION__);
return;
}
return m_sym_file_impl->InitializeObject();
}
lldb::LanguageType SymbolFileOnDemand::ParseLanguage(CompileUnit &comp_unit) {
if (!m_debug_info_enabled) {
Log *log = GetLog();
LLDB_LOG(log, "[{0}] {1} is skipped", GetSymbolFileName(), __FUNCTION__);
if (log) {
lldb::LanguageType langType = m_sym_file_impl->ParseLanguage(comp_unit);
if (langType != eLanguageTypeUnknown)
LLDB_LOG(log, "Language {0} would return if hydrated.", langType);
}
return eLanguageTypeUnknown;
}
return m_sym_file_impl->ParseLanguage(comp_unit);
}
XcodeSDK SymbolFileOnDemand::ParseXcodeSDK(CompileUnit &comp_unit) {
if (!m_debug_info_enabled) {
Log *log = GetLog();
LLDB_LOG(log, "[{0}] {1} is skipped", GetSymbolFileName(), __FUNCTION__);
XcodeSDK defaultValue{};
if (log) {
XcodeSDK sdk = m_sym_file_impl->ParseXcodeSDK(comp_unit);
if (!(sdk == defaultValue))
LLDB_LOG(log, "SDK {0} would return if hydrated.", sdk.GetString());
}
return defaultValue;
}
return m_sym_file_impl->ParseXcodeSDK(comp_unit);
}
size_t SymbolFileOnDemand::ParseFunctions(CompileUnit &comp_unit) {
if (!m_debug_info_enabled) {
LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(),
__FUNCTION__);
return 0;
}
return m_sym_file_impl->ParseFunctions(comp_unit);
}
bool SymbolFileOnDemand::ParseLineTable(CompileUnit &comp_unit) {
if (!m_debug_info_enabled) {
LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(),
__FUNCTION__);
return false;
}
return m_sym_file_impl->ParseLineTable(comp_unit);
}
bool SymbolFileOnDemand::ParseDebugMacros(CompileUnit &comp_unit) {
if (!m_debug_info_enabled) {
LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(),
__FUNCTION__);
return false;
}
return m_sym_file_impl->ParseDebugMacros(comp_unit);
}
bool SymbolFileOnDemand::ForEachExternalModule(
CompileUnit &comp_unit,
llvm::DenseSet<lldb_private::SymbolFile *> &visited_symbol_files,
llvm::function_ref<bool(Module &)> lambda) {
if (!m_debug_info_enabled) {
LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(),
__FUNCTION__);
// Return false to not early exit.
return false;
}
return m_sym_file_impl->ForEachExternalModule(comp_unit, visited_symbol_files,
lambda);
}
bool SymbolFileOnDemand::ParseSupportFiles(CompileUnit &comp_unit,
SupportFileList &support_files) {
LLDB_LOG(GetLog(),
"[{0}] {1} is not skipped: explicitly allowed to support breakpoint",
GetSymbolFileName(), __FUNCTION__);
// Explicitly allow this API through to support source line breakpoint.
return m_sym_file_impl->ParseSupportFiles(comp_unit, support_files);
}
bool SymbolFileOnDemand::ParseIsOptimized(CompileUnit &comp_unit) {
if (!m_debug_info_enabled) {
Log *log = GetLog();
LLDB_LOG(log, "[{0}] {1} is skipped", GetSymbolFileName(), __FUNCTION__);
if (log) {
bool optimized = m_sym_file_impl->ParseIsOptimized(comp_unit);
if (optimized) {
LLDB_LOG(log, "Would return optimized if hydrated.");
}
}
return false;
}
return m_sym_file_impl->ParseIsOptimized(comp_unit);
}
size_t SymbolFileOnDemand::ParseTypes(CompileUnit &comp_unit) {
if (!m_debug_info_enabled) {
LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(),
__FUNCTION__);
return 0;
}
return m_sym_file_impl->ParseTypes(comp_unit);
}
bool SymbolFileOnDemand::ParseImportedModules(
const lldb_private::SymbolContext &sc,
std::vector<SourceModule> &imported_modules) {
if (!m_debug_info_enabled) {
Log *log = GetLog();
LLDB_LOG(log, "[{0}] {1} is skipped", GetSymbolFileName(), __FUNCTION__);
if (log) {
std::vector<SourceModule> tmp_imported_modules;
bool succeed =
m_sym_file_impl->ParseImportedModules(sc, tmp_imported_modules);
if (succeed)
LLDB_LOG(log, "{0} imported modules would be parsed if hydrated.",
tmp_imported_modules.size());
}
return false;
}
return m_sym_file_impl->ParseImportedModules(sc, imported_modules);
}
size_t SymbolFileOnDemand::ParseBlocksRecursive(Function &func) {
if (!m_debug_info_enabled) {
LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(),
__FUNCTION__);
return 0;
}
return m_sym_file_impl->ParseBlocksRecursive(func);
}
size_t SymbolFileOnDemand::ParseVariablesForContext(const SymbolContext &sc) {
if (!m_debug_info_enabled) {
LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(),
__FUNCTION__);
return 0;
}
return m_sym_file_impl->ParseVariablesForContext(sc);
}
Type *SymbolFileOnDemand::ResolveTypeUID(lldb::user_id_t type_uid) {
if (!m_debug_info_enabled) {
Log *log = GetLog();
LLDB_LOG(log, "[{0}] {1} is skipped", GetSymbolFileName(), __FUNCTION__);
if (log) {
Type *resolved_type = m_sym_file_impl->ResolveTypeUID(type_uid);
if (resolved_type)
LLDB_LOG(log, "Type would be parsed for {0} if hydrated.", type_uid);
}
return nullptr;
}
return m_sym_file_impl->ResolveTypeUID(type_uid);
}
std::optional<SymbolFile::ArrayInfo>
SymbolFileOnDemand::GetDynamicArrayInfoForUID(
lldb::user_id_t type_uid, const lldb_private::ExecutionContext *exe_ctx) {
if (!m_debug_info_enabled) {
LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(),
__FUNCTION__);
return std::nullopt;
}
return m_sym_file_impl->GetDynamicArrayInfoForUID(type_uid, exe_ctx);
}
bool SymbolFileOnDemand::CompleteType(CompilerType &compiler_type) {
if (!m_debug_info_enabled) {
LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(),
__FUNCTION__);
return false;
}
return m_sym_file_impl->CompleteType(compiler_type);
}
CompilerDecl SymbolFileOnDemand::GetDeclForUID(lldb::user_id_t type_uid) {
if (!m_debug_info_enabled) {
Log *log = GetLog();
LLDB_LOG(log, "[{0}] {1} is skipped", GetSymbolFileName(), __FUNCTION__);
if (log) {
CompilerDecl parsed_decl = m_sym_file_impl->GetDeclForUID(type_uid);
if (parsed_decl != CompilerDecl()) {
LLDB_LOG(log, "CompilerDecl {0} would be parsed for {1} if hydrated.",
parsed_decl.GetName(), type_uid);
}
}
return CompilerDecl();
}
return m_sym_file_impl->GetDeclForUID(type_uid);
}
CompilerDeclContext
SymbolFileOnDemand::GetDeclContextForUID(lldb::user_id_t type_uid) {
if (!m_debug_info_enabled) {
LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(),
__FUNCTION__);
return CompilerDeclContext();
}
return m_sym_file_impl->GetDeclContextForUID(type_uid);
}
CompilerDeclContext
SymbolFileOnDemand::GetDeclContextContainingUID(lldb::user_id_t type_uid) {
if (!m_debug_info_enabled) {
LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(),
__FUNCTION__);
return CompilerDeclContext();
}
return m_sym_file_impl->GetDeclContextContainingUID(type_uid);
}
void SymbolFileOnDemand::ParseDeclsForContext(CompilerDeclContext decl_ctx) {
if (!m_debug_info_enabled) {
LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(),
__FUNCTION__);
return;
}
return m_sym_file_impl->ParseDeclsForContext(decl_ctx);
}
uint32_t
SymbolFileOnDemand::ResolveSymbolContext(const Address &so_addr,
SymbolContextItem resolve_scope,
SymbolContext &sc) {
if (!m_debug_info_enabled) {
LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(),
__FUNCTION__);
return 0;
}
return m_sym_file_impl->ResolveSymbolContext(so_addr, resolve_scope, sc);
}
Status SymbolFileOnDemand::CalculateFrameVariableError(StackFrame &frame) {
if (!m_debug_info_enabled) {
LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(),
__FUNCTION__);
return Status();
}
return m_sym_file_impl->CalculateFrameVariableError(frame);
}
uint32_t SymbolFileOnDemand::ResolveSymbolContext(
const SourceLocationSpec &src_location_spec,
SymbolContextItem resolve_scope, SymbolContextList &sc_list) {
if (!m_debug_info_enabled) {
LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(),
__FUNCTION__);
return 0;
}
return m_sym_file_impl->ResolveSymbolContext(src_location_spec, resolve_scope,
sc_list);
}
void SymbolFileOnDemand::Dump(lldb_private::Stream &s) {
if (!m_debug_info_enabled) {
LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(),
__FUNCTION__);
return;
}
return m_sym_file_impl->Dump(s);
}
void SymbolFileOnDemand::DumpClangAST(lldb_private::Stream &s,
llvm::StringRef filter) {
if (!m_debug_info_enabled) {
LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(),
__FUNCTION__);
return;
}
return m_sym_file_impl->DumpClangAST(s, filter);
}
void SymbolFileOnDemand::FindGlobalVariables(const RegularExpression &regex,
uint32_t max_matches,
VariableList &variables) {
if (!m_debug_info_enabled) {
LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(),
__FUNCTION__);
return;
}
return m_sym_file_impl->FindGlobalVariables(regex, max_matches, variables);
}
void SymbolFileOnDemand::FindGlobalVariables(
ConstString name, const CompilerDeclContext &parent_decl_ctx,
uint32_t max_matches, VariableList &variables) {
if (!m_debug_info_enabled) {
Log *log = GetLog();
Symtab *symtab = GetSymtab();
if (!symtab) {
LLDB_LOG(log, "[{0}] {1} is skipped - fail to get symtab",
GetSymbolFileName(), __FUNCTION__);
return;
}
Symbol *sym = symtab->FindFirstSymbolWithNameAndType(
name, eSymbolTypeData, Symtab::eDebugAny, Symtab::eVisibilityAny);
if (!sym) {
LLDB_LOG(log, "[{0}] {1} is skipped - fail to find match in symtab",
GetSymbolFileName(), __FUNCTION__);
return;
}
LLDB_LOG(log, "[{0}] {1} is NOT skipped - found match in symtab",
GetSymbolFileName(), __FUNCTION__);
// Found match in symbol table hydrate debug info and
// allow the FindGlobalVariables to go through.
SetLoadDebugInfoEnabled();
}
return m_sym_file_impl->FindGlobalVariables(name, parent_decl_ctx,
max_matches, variables);
}
void SymbolFileOnDemand::FindFunctions(const RegularExpression &regex,
bool include_inlines,
SymbolContextList &sc_list) {
if (!m_debug_info_enabled) {
Log *log = GetLog();
Symtab *symtab = GetSymtab();
if (!symtab) {
LLDB_LOG(log, "[{0}] {1} is skipped - fail to get symtab",
GetSymbolFileName(), __FUNCTION__);
return;
}
std::vector<uint32_t> symbol_indexes;
symtab->AppendSymbolIndexesMatchingRegExAndType(
regex, eSymbolTypeAny, Symtab::eDebugAny, Symtab::eVisibilityAny,
symbol_indexes);
if (symbol_indexes.empty()) {
LLDB_LOG(log, "[{0}] {1} is skipped - fail to find match in symtab",
GetSymbolFileName(), __FUNCTION__);
return;
}
LLDB_LOG(log, "[{0}] {1} is NOT skipped - found match in symtab",
GetSymbolFileName(), __FUNCTION__);
// Found match in symbol table hydrate debug info and
// allow the FindFucntions to go through.
SetLoadDebugInfoEnabled();
}
return m_sym_file_impl->FindFunctions(regex, include_inlines, sc_list);
}
void SymbolFileOnDemand::FindFunctions(
const Module::LookupInfo &lookup_info,
const CompilerDeclContext &parent_decl_ctx, bool include_inlines,
SymbolContextList &sc_list) {
ConstString name = lookup_info.GetLookupName();
FunctionNameType name_type_mask = lookup_info.GetNameTypeMask();
if (!m_debug_info_enabled) {
Log *log = GetLog();
Symtab *symtab = GetSymtab();
if (!symtab) {
LLDB_LOG(log, "[{0}] {1}({2}) is skipped - fail to get symtab",
GetSymbolFileName(), __FUNCTION__, name);
return;
}
SymbolContextList sc_list_helper;
symtab->FindFunctionSymbols(name, name_type_mask, sc_list_helper);
if (sc_list_helper.GetSize() == 0) {
LLDB_LOG(log, "[{0}] {1}({2}) is skipped - fail to find match in symtab",
GetSymbolFileName(), __FUNCTION__, name);
return;
}
LLDB_LOG(log, "[{0}] {1}({2}) is NOT skipped - found match in symtab",
GetSymbolFileName(), __FUNCTION__, name);
// Found match in symbol table hydrate debug info and
// allow the FindFucntions to go through.
SetLoadDebugInfoEnabled();
}
return m_sym_file_impl->FindFunctions(lookup_info, parent_decl_ctx,
include_inlines, sc_list);
}
void SymbolFileOnDemand::GetMangledNamesForFunction(
const std::string &scope_qualified_name,
std::vector<ConstString> &mangled_names) {
if (!m_debug_info_enabled) {
Log *log = GetLog();
LLDB_LOG(log, "[{0}] {1}({2}) is skipped", GetSymbolFileName(),
__FUNCTION__, scope_qualified_name);
return;
}
return m_sym_file_impl->GetMangledNamesForFunction(scope_qualified_name,
mangled_names);
}
void SymbolFileOnDemand::FindTypes(const TypeQuery &match,
TypeResults &results) {
if (!m_debug_info_enabled) {
LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(),
__FUNCTION__);
return;
}
return m_sym_file_impl->FindTypes(match, results);
}
void SymbolFileOnDemand::GetTypes(SymbolContextScope *sc_scope,
TypeClass type_mask, TypeList &type_list) {
if (!m_debug_info_enabled) {
LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(),
__FUNCTION__);
return;
}
return m_sym_file_impl->GetTypes(sc_scope, type_mask, type_list);
}
llvm::Expected<lldb::TypeSystemSP>
SymbolFileOnDemand::GetTypeSystemForLanguage(LanguageType language) {
if (!m_debug_info_enabled) {
Log *log = GetLog();
LLDB_LOG(log, "[{0}] {1} is skipped for language type {2}",
GetSymbolFileName(), __FUNCTION__, language);
return llvm::createStringError(
"GetTypeSystemForLanguage is skipped by SymbolFileOnDemand");
}
return m_sym_file_impl->GetTypeSystemForLanguage(language);
}
CompilerDeclContext
SymbolFileOnDemand::FindNamespace(ConstString name,
const CompilerDeclContext &parent_decl_ctx,
bool only_root_namespaces) {
if (!m_debug_info_enabled) {
LLDB_LOG(GetLog(), "[{0}] {1}({2}) is skipped", GetSymbolFileName(),
__FUNCTION__, name);
return SymbolFile::FindNamespace(name, parent_decl_ctx,
only_root_namespaces);
}
return m_sym_file_impl->FindNamespace(name, parent_decl_ctx,
only_root_namespaces);
}
std::vector<std::unique_ptr<lldb_private::CallEdge>>
SymbolFileOnDemand::ParseCallEdgesInFunction(UserID func_id) {
if (!m_debug_info_enabled) {
Log *log = GetLog();
LLDB_LOG(log, "[{0}] {1} is skipped", GetSymbolFileName(), __FUNCTION__);
if (log) {
std::vector<std::unique_ptr<lldb_private::CallEdge>> call_edges =
m_sym_file_impl->ParseCallEdgesInFunction(func_id);
if (call_edges.size() > 0) {
LLDB_LOG(log, "{0} call edges would be parsed for {1} if hydrated.",
call_edges.size(), func_id.GetID());
}
}
return {};
}
return m_sym_file_impl->ParseCallEdgesInFunction(func_id);
}
lldb::UnwindPlanSP
SymbolFileOnDemand::GetUnwindPlan(const Address &address,
const RegisterInfoResolver &resolver) {
if (!m_debug_info_enabled) {
LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(),
__FUNCTION__);
return nullptr;
}
return m_sym_file_impl->GetUnwindPlan(address, resolver);
}
llvm::Expected<lldb::addr_t>
SymbolFileOnDemand::GetParameterStackSize(Symbol &symbol) {
if (!m_debug_info_enabled) {
Log *log = GetLog();
LLDB_LOG(log, "[{0}] {1} is skipped", GetSymbolFileName(), __FUNCTION__);
if (log) {
llvm::Expected<lldb::addr_t> stack_size =
m_sym_file_impl->GetParameterStackSize(symbol);
if (stack_size) {
LLDB_LOG(log, "{0} stack size would return for symbol {1} if hydrated.",
*stack_size, symbol.GetName());
}
}
return SymbolFile::GetParameterStackSize(symbol);
}
return m_sym_file_impl->GetParameterStackSize(symbol);
}
void SymbolFileOnDemand::PreloadSymbols() {
m_preload_symbols = true;
if (!m_debug_info_enabled) {
LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(),
__FUNCTION__);
return;
}
return m_sym_file_impl->PreloadSymbols();
}
uint64_t SymbolFileOnDemand::GetDebugInfoSize(bool load_all_debug_info) {
// Always return the real debug info size.
LLDB_LOG(GetLog(), "[{0}] {1} is not skipped", GetSymbolFileName(),
__FUNCTION__);
return m_sym_file_impl->GetDebugInfoSize(load_all_debug_info);
}
StatsDuration::Duration SymbolFileOnDemand::GetDebugInfoParseTime() {
// Always return the real parse time.
LLDB_LOG(GetLog(), "[{0}] {1} is not skipped", GetSymbolFileName(),
__FUNCTION__);
return m_sym_file_impl->GetDebugInfoParseTime();
}
StatsDuration::Duration SymbolFileOnDemand::GetDebugInfoIndexTime() {
// Always return the real index time.
LLDB_LOG(GetLog(), "[{0}] {1} is not skipped", GetSymbolFileName(),
__FUNCTION__);
return m_sym_file_impl->GetDebugInfoIndexTime();
}
void SymbolFileOnDemand::ResetStatistics() {
LLDB_LOG(GetLog(), "[{0}] {1} is not skipped", GetSymbolFileName(),
__FUNCTION__);
return m_sym_file_impl->ResetStatistics();
}
void SymbolFileOnDemand::SetLoadDebugInfoEnabled() {
if (m_debug_info_enabled)
return;
LLDB_LOG(GetLog(), "[{0}] Hydrate debug info", GetSymbolFileName());
m_debug_info_enabled = true;
InitializeObject();
if (m_preload_symbols)
PreloadSymbols();
}
uint32_t SymbolFileOnDemand::GetNumCompileUnits() {
LLDB_LOG(GetLog(), "[{0}] {1} is not skipped to support breakpoint hydration",
GetSymbolFileName(), __FUNCTION__);
return m_sym_file_impl->GetNumCompileUnits();
}
CompUnitSP SymbolFileOnDemand::GetCompileUnitAtIndex(uint32_t idx) {
LLDB_LOG(GetLog(), "[{0}] {1} is not skipped to support breakpoint hydration",
GetSymbolFileName(), __FUNCTION__);
return m_sym_file_impl->GetCompileUnitAtIndex(idx);
}
uint32_t SymbolFileOnDemand::GetAbilities() {
if (!m_debug_info_enabled) {
LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(),
__FUNCTION__);
return 0;
}
return m_sym_file_impl->GetAbilities();
}