Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix statistics dump to report per-target #113723

Merged
merged 4 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions lldb/include/lldb/API/SBDebugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,11 @@ class LLDB_API SBDebugger {

SBTypeSynthetic GetSyntheticForType(SBTypeNameSpecifier);

/// Clear collected statistics for targets belonging to this debugger. This
/// includes clearing symbol table and debug info parsing/index time for all
/// modules, breakpoint resolve time and target statistics.
void ResetStatistics();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a header doc comment for this to document what this will do:

  • clear all stats for all modules in all targets
  • clear all target breakpoint stats?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments added. I do not think we need to clear any breakpoint stats because they are not reused across debug sessions by "reuse lldb-dap"


#ifndef SWIG
/// Run the command interpreter.
///
Expand Down
5 changes: 5 additions & 0 deletions lldb/include/lldb/API/SBTarget.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ class LLDB_API SBTarget {
/// A SBStructuredData with the statistics collected.
lldb::SBStructuredData GetStatistics(SBStatisticsOptions options);

/// Reset the statistics collected for this target.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Document this a bit better with more explanation

/// This includes clearing symbol table and debug info parsing/index time for
/// all modules, breakpoint resolve time and target statistics.
void ResetStatistics();

/// Return the platform object associated with the target.
///
/// After return, the platform object should be checked for
Expand Down
2 changes: 2 additions & 0 deletions lldb/include/lldb/Breakpoint/Breakpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,8 @@ class Breakpoint : public std::enable_shared_from_this<Breakpoint>,
/// Get statistics associated with this breakpoint in JSON format.
llvm::json::Value GetStatistics();

void ResetStatistics();

/// Get the time it took to resolve all locations in this breakpoint.
StatsDuration::Duration GetResolveTime() const { return m_resolve_time; }

Expand Down
2 changes: 2 additions & 0 deletions lldb/include/lldb/Core/Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -880,6 +880,8 @@ class Module : public std::enable_shared_from_this<Module>,
/// ElapsedTime RAII object.
StatsDuration &GetSymtabIndexTime() { return m_symtab_index_time; }

void ResetStatistics();

/// \class LookupInfo Module.h "lldb/Core/Module.h"
/// A class that encapsulates name lookup information.
///
Expand Down
3 changes: 3 additions & 0 deletions lldb/include/lldb/Symbol/SymbolFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,9 @@ class SymbolFile : public PluginInterface {
/// hasn't been indexed yet, or a valid duration if it has.
virtual StatsDuration::Duration GetDebugInfoIndexTime() { return {}; }

/// Reset the statistics for the symbol file.
virtual void ResetStatistics() {}

/// Get the additional modules that this symbol file uses to parse debug info.
///
/// Some debug info is stored in stand alone object files that are represented
Expand Down
2 changes: 2 additions & 0 deletions lldb/include/lldb/Symbol/SymbolFileOnDemand.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ class SymbolFileOnDemand : public lldb_private::SymbolFile {
lldb_private::StatsDuration::Duration GetDebugInfoParseTime() override;
lldb_private::StatsDuration::Duration GetDebugInfoIndexTime() override;

void ResetStatistics() override;

uint32_t GetAbilities() override;

Symtab *GetSymtab() override { return m_sym_file_impl->GetSymtab(); }
Expand Down
17 changes: 17 additions & 0 deletions lldb/include/lldb/Target/Statistics.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ class StatsDuration {
}
operator Duration() const { return get(); }

void reset() { value.store(0, std::memory_order_relaxed); }

StatsDuration &operator+=(Duration dur) {
value.fetch_add(std::chrono::duration_cast<InternalDuration>(dur).count(),
std::memory_order_relaxed);
Expand Down Expand Up @@ -201,6 +203,8 @@ class SummaryStatistics {

llvm::json::Value ToJSON() const;

void Reset() { m_total_time.reset(); }

/// Basic RAII class to increment the summary count when the call is complete.
class SummaryInvocation {
public:
Expand Down Expand Up @@ -250,6 +254,8 @@ class SummaryStatisticsCache {

llvm::json::Value ToJSON();

void Reset();

private:
llvm::StringMap<SummaryStatisticsSP> m_summary_stats_map;
std::mutex m_map_mutex;
Expand All @@ -271,6 +277,7 @@ class TargetStats {
StatsDuration &GetCreateTime() { return m_create_time; }
StatsSuccessFail &GetExpressionStats() { return m_expr_eval; }
StatsSuccessFail &GetFrameVariableStats() { return m_frame_var; }
void Reset(Target &target);

protected:
StatsDuration m_create_time;
Expand Down Expand Up @@ -311,6 +318,16 @@ class DebuggerStats {
ReportStatistics(Debugger &debugger, Target *target,
const lldb_private::StatisticsOptions &options);

/// Reset metrics associated with one or all targets in a debugger.
///
/// \param debugger
/// The debugger to reset the target list from if \a target is NULL.
///
/// \param target
/// The target to reset statistics for, or if null, reset statistics
/// for all targets
static void ResetStatistics(Debugger &debugger, Target *target);

protected:
// Collecting stats can be set to true to collect stats that are expensive
// to collect. By default all stats that are cheap to collect are enabled.
Expand Down
2 changes: 2 additions & 0 deletions lldb/include/lldb/Target/Target.h
Original file line number Diff line number Diff line change
Expand Up @@ -1624,6 +1624,8 @@ class Target : public std::enable_shared_from_this<Target>,
llvm::json::Value
ReportStatistics(const lldb_private::StatisticsOptions &options);

void ResetStatistics();

TargetStats &GetStatistics() { return m_stats; }

protected:
Expand Down
6 changes: 6 additions & 0 deletions lldb/source/API/SBDebugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1667,6 +1667,12 @@ SBTypeSynthetic SBDebugger::GetSyntheticForType(SBTypeNameSpecifier type_name) {
DataVisualization::GetSyntheticForType(type_name.GetSP()));
}

void SBDebugger::ResetStatistics() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add a void SBTarget::ResetStatistics() too that calls with the right target?

LLDB_INSTRUMENT_VA(this);
if (m_opaque_sp)
DebuggerStats::ResetStatistics(*m_opaque_sp, nullptr);
}

static llvm::ArrayRef<const char *> GetCategoryArray(const char **categories) {
if (categories == nullptr)
return {};
Expand Down
7 changes: 7 additions & 0 deletions lldb/source/API/SBTarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,13 @@ SBStructuredData SBTarget::GetStatistics(SBStatisticsOptions options) {
return data;
}

void SBTarget::ResetStatistics() {
LLDB_INSTRUMENT_VA(this);
TargetSP target_sp(GetSP());
if (target_sp)
DebuggerStats::ResetStatistics(target_sp->GetDebugger(), target_sp.get());
}

void SBTarget::SetCollectingStats(bool v) {
LLDB_INSTRUMENT_VA(this, v);

Expand Down
2 changes: 2 additions & 0 deletions lldb/source/Breakpoint/Breakpoint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1138,3 +1138,5 @@ json::Value Breakpoint::GetStatistics() {
}
return json::Value(std::move(bp));
}

void Breakpoint::ResetStatistics() { m_resolve_time.reset(); }
8 changes: 8 additions & 0 deletions lldb/source/Core/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1600,6 +1600,14 @@ bool Module::MergeArchitecture(const ArchSpec &arch_spec) {
return SetArchitecture(merged_arch);
}

void Module::ResetStatistics() {
m_symtab_parse_time.reset();
m_symtab_index_time.reset();
SymbolFile *sym_file = GetSymbolFile();
if (sym_file)
sym_file->ResetStatistics();
}

llvm::VersionTuple Module::GetVersion() {
if (ObjectFile *obj_file = GetObjectFile())
return obj_file->GetVersion();
Expand Down
2 changes: 2 additions & 0 deletions lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ class DWARFIndex {

StatsDuration::Duration GetIndexTime() { return m_index_time; }

void ResetStatistics() { m_index_time.reset(); }

protected:
Module &m_module;
StatsDuration m_index_time;
Expand Down
6 changes: 6 additions & 0 deletions lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4464,6 +4464,12 @@ StatsDuration::Duration SymbolFileDWARF::GetDebugInfoIndexTime() {
return {};
}

void SymbolFileDWARF::ResetStatistics() {
m_parse_time.reset();
if (m_index)
return m_index->ResetStatistics();
}

Status SymbolFileDWARF::CalculateFrameVariableError(StackFrame &frame) {
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
CompileUnit *cu = frame.GetSymbolContext(eSymbolContextCompUnit).comp_unit;
Expand Down
2 changes: 2 additions & 0 deletions lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,8 @@ class SymbolFileDWARF : public SymbolFileCommon {

StatsDuration &GetDebugInfoParseTimeRef() { return m_parse_time; }

void ResetStatistics() override;

virtual lldb::offset_t
GetVendorDWARFOpcodeSize(const DataExtractor &data,
const lldb::offset_t data_offset,
Expand Down
6 changes: 6 additions & 0 deletions lldb/source/Symbol/SymbolFileOnDemand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,12 @@ StatsDuration::Duration SymbolFileOnDemand::GetDebugInfoIndexTime() {
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;
Expand Down
53 changes: 51 additions & 2 deletions lldb/source/Target/Statistics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,24 @@ TargetStats::ToJSON(Target &target,
return target_metrics_json;
}

void TargetStats::Reset(Target &target) {
m_launch_or_attach_time.reset();
m_first_private_stop_time.reset();
m_first_public_stop_time.reset();
// Report both the normal breakpoint list and the internal breakpoint list.
for (int i = 0; i < 2; ++i) {
BreakpointList &breakpoints = target.GetBreakpointList(i == 1);
std::unique_lock<std::recursive_mutex> lock;
breakpoints.GetListMutex(lock);
size_t num_breakpoints = breakpoints.GetSize();
for (size_t i = 0; i < num_breakpoints; i++) {
Breakpoint *bp = breakpoints.GetBreakpointAtIndex(i).get();
bp->ResetStatistics();
}
}
target.GetSummaryStatisticsCache().Reset();
}

void TargetStats::SetLaunchOrAttachTime() {
m_launch_or_attach_time = StatsClock::now();
m_first_private_stop_time = std::nullopt;
Expand Down Expand Up @@ -236,6 +254,28 @@ void TargetStats::IncreaseSourceRealpathCompatibleCount(uint32_t count) {

bool DebuggerStats::g_collecting_stats = false;

void DebuggerStats::ResetStatistics(Debugger &debugger, Target *target) {
std::lock_guard<std::recursive_mutex> guard(
Module::GetAllocationModuleCollectionMutex());
const uint64_t num_modules = target != nullptr
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to lock the Module::GetAllocationModuleCollectionMutex() when using the Module static calls for the global module list.

? target->GetImages().GetSize()
: Module::GetNumberAllocatedModules();
for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) {
Module *module = target != nullptr
? target->GetImages().GetModuleAtIndex(image_idx).get()
: Module::GetAllocatedModuleAtIndex(image_idx);
if (module == nullptr)
continue;
module->ResetStatistics();
}
if (target)
target->ResetStatistics();
else {
for (const auto &target : debugger.GetTargetList().Targets())
target->ResetStatistics();
}
}

llvm::json::Value DebuggerStats::ReportStatistics(
Debugger &debugger, Target *target,
const lldb_private::StatisticsOptions &options) {
Expand All @@ -261,14 +301,18 @@ llvm::json::Value DebuggerStats::ReportStatistics(
std::vector<ModuleStats> modules;
std::lock_guard<std::recursive_mutex> guard(
Module::GetAllocationModuleCollectionMutex());
const uint64_t num_modules = Module::GetNumberAllocatedModules();
const uint64_t num_modules = target != nullptr
? target->GetImages().GetSize()
: Module::GetNumberAllocatedModules();
uint32_t num_debug_info_enabled_modules = 0;
uint32_t num_modules_has_debug_info = 0;
uint32_t num_modules_with_variable_errors = 0;
uint32_t num_modules_with_incomplete_types = 0;
uint32_t num_stripped_modules = 0;
for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) {
Module *module = Module::GetAllocatedModuleAtIndex(image_idx);
Module *module = target != nullptr
? target->GetImages().GetModuleAtIndex(image_idx).get()
: Module::GetAllocatedModuleAtIndex(image_idx);
ModuleStats module_stat;
module_stat.symtab_parse_time = module->GetSymtabParseTime().get().count();
module_stat.symtab_index_time = module->GetSymtabIndexTime().get().count();
Expand Down Expand Up @@ -440,3 +484,8 @@ json::Value SummaryStatisticsCache::ToJSON() {

return json_summary_stats;
}

void SummaryStatisticsCache::Reset() {
for (const auto &summary_stat : m_summary_stats_map)
summary_stat.second->Reset();
}
2 changes: 2 additions & 0 deletions lldb/source/Target/Target.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5066,3 +5066,5 @@ llvm::json::Value
Target::ReportStatistics(const lldb_private::StatisticsOptions &options) {
return m_stats.ToJSON(*this, options);
}

void Target::ResetStatistics() { m_stats.Reset(*this); }
40 changes: 38 additions & 2 deletions lldb/test/API/commands/statistics/basic/TestStats.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import lldb
import json
import os
import re

import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
Expand Down Expand Up @@ -540,7 +541,7 @@ def test_no_dsym_binary_has_symfile_identifiers_in_stats(self):
# in the stats.
self.runCmd("b main.cpp:7")

debug_stats = self.get_stats()
debug_stats = self.get_stats("--all-targets")

exe_stats = self.find_module_in_metrics(exe, debug_stats)
# If we don't have a dSYM file, there should not be a key/value pair in
Expand Down Expand Up @@ -986,3 +987,38 @@ def test_summary_statistics_providers_vec(self):
# We may hit the std::vector C++ provider, or a summary provider string
if "c++" in summary_provider_str:
self.assertIn("std::vector", summary_provider_str)

@skipIfWindows
def test_multiple_targets(self):
"""
Test statistics dump only reports the stats from current target and
"statistics dump --all-targets" includes all target stats.
"""
da = {"CXX_SOURCES": "main.cpp", "EXE": self.getBuildArtifact("a.out")}
self.build(dictionary=da)
self.addTearDownCleanup(dictionary=da)

db = {"CXX_SOURCES": "second.cpp", "EXE": self.getBuildArtifact("second.out")}
self.build(dictionary=db)
self.addTearDownCleanup(dictionary=db)

main_exe = self.getBuildArtifact("a.out")
second_exe = self.getBuildArtifact("second.out")

(target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
self, "// break here", lldb.SBFileSpec("main.cpp"), None, "a.out"
)
debugger_stats1 = self.get_stats()
self.assertIsNotNone(self.find_module_in_metrics(main_exe, debugger_stats1))
self.assertIsNone(self.find_module_in_metrics(second_exe, debugger_stats1))

(target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
self, "// break here", lldb.SBFileSpec("second.cpp"), None, "second.out"
)
debugger_stats2 = self.get_stats()
self.assertIsNone(self.find_module_in_metrics(main_exe, debugger_stats2))
self.assertIsNotNone(self.find_module_in_metrics(second_exe, debugger_stats2))

all_targets_stats = self.get_stats("--all-targets")
self.assertIsNotNone(self.find_module_in_metrics(main_exe, all_targets_stats))
self.assertIsNotNone(self.find_module_in_metrics(second_exe, all_targets_stats))
5 changes: 5 additions & 0 deletions lldb/test/API/commands/statistics/basic/second.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Test that the lldb command `statistics` works.

int main(void) {
return 0; // break here
}
Loading