llvm-project/lldb/source/Symbol/SymbolFileOnDemand.cpp
jeffreytan81 24feaab838
Fix statistics dump to report per-target (#113723)
"statistics dump" currently report the statistics of all targets in
debugger instead of current target. This is wrong because there is a
"statistics dump --all-targets" option that supposed to include
everything.

This PR fixes the issue by only report statistics for current target
instead of all. It also includes the change to reset statistics debug
info/symbol table parsing/indexing time during debugger destroy. This is
required so that we report current statistics if we plan to reuse
lldb/lldb-dap across debug sessions

---------

Co-authored-by: jeffreytan81 <jeffreytan@fb.com>
2024-11-17 20:36:54 -08:00

594 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) {
if (!m_debug_info_enabled) {
LLDB_LOG(GetLog(), "[{0}] {1} is skipped", GetSymbolFileName(),
__FUNCTION__);
return;
}
return m_sym_file_impl->DumpClangAST(s);
}
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();
}