diff --git a/lldb/include/lldb/Target/Statistics.h b/lldb/include/lldb/Target/Statistics.h --- a/lldb/include/lldb/Target/Statistics.h +++ b/lldb/include/lldb/Target/Statistics.h @@ -9,6 +9,7 @@ #ifndef LLDB_TARGET_STATISTICS_H #define LLDB_TARGET_STATISTICS_H +#include "lldb/Utility/ConstString.h" #include "lldb/Utility/Stream.h" #include "lldb/lldb-forward.h" #include "llvm/Support/JSON.h" @@ -110,6 +111,11 @@ bool debug_info_index_saved_to_cache = false; }; +struct ConstStringStats { + llvm::json::Value ToJSON() const; + ConstString::MemoryStats stats = ConstString::GetMemoryStats(); +}; + /// A class that represents statistics for a since lldb_private::Target. class TargetStats { public: diff --git a/lldb/include/lldb/Utility/ConstString.h b/lldb/include/lldb/Utility/ConstString.h --- a/lldb/include/lldb/Utility/ConstString.h +++ b/lldb/include/lldb/Utility/ConstString.h @@ -408,6 +408,16 @@ /// in memory. static size_t StaticMemorySize(); + struct MemoryStats { + size_t GetBytesTotal() const { return bytes_total; } + size_t GetBytesUsed() const { return bytes_used; } + size_t GetBytesUnused() const { return bytes_total - bytes_used; } + size_t bytes_total = 0; + size_t bytes_used = 0; + }; + + static MemoryStats GetMemoryStats(); + protected: template friend struct ::llvm::DenseMapInfo; /// Only used by DenseMapInfo. diff --git a/lldb/source/Target/Statistics.cpp b/lldb/source/Target/Statistics.cpp --- a/lldb/source/Target/Statistics.cpp +++ b/lldb/source/Target/Statistics.cpp @@ -65,6 +65,14 @@ return module; } +llvm::json::Value ConstStringStats::ToJSON() const { + json::Object obj; + obj.try_emplace("bytesTotal", stats.GetBytesTotal()); + obj.try_emplace("bytesUsed", stats.GetBytesUsed()); + obj.try_emplace("bytesUnused", stats.GetBytesUnused()); + return obj; +} + json::Value TargetStats::ToJSON(Target &target) { CollectStats(target); @@ -212,9 +220,15 @@ json_modules.emplace_back(module_stat.ToJSON()); } + ConstStringStats const_string_stats; + json::Object json_memory{ + {"strings", const_string_stats.ToJSON()}, + }; + json::Object global_stats{ {"targets", std::move(json_targets)}, {"modules", std::move(json_modules)}, + {"memory", std::move(json_memory)}, {"totalSymbolTableParseTime", symtab_parse_time}, {"totalSymbolTableIndexTime", symtab_index_time}, {"totalSymbolTablesLoadedFromCache", symtabs_loaded}, diff --git a/lldb/source/Utility/ConstString.cpp b/lldb/source/Utility/ConstString.cpp --- a/lldb/source/Utility/ConstString.cpp +++ b/lldb/source/Utility/ConstString.cpp @@ -171,6 +171,17 @@ return mem_size; } + ConstString::MemoryStats GetMemoryStats() const { + ConstString::MemoryStats stats; + for (const auto &pool : m_string_pools) { + llvm::sys::SmartScopedReader rlock(pool.m_mutex); + const Allocator &alloc = pool.m_string_map.getAllocator(); + stats.bytes_total += alloc.getTotalMemory(); + stats.bytes_used += alloc.getBytesAllocated(); + } + return stats; + } + protected: uint8_t hash(const llvm::StringRef &s) const { uint32_t h = llvm::djbHash(s); @@ -332,6 +343,10 @@ return StringPool().MemorySize(); } +ConstString::MemoryStats ConstString::GetMemoryStats() { + return StringPool().GetMemoryStats(); +} + void llvm::format_provider::format(const ConstString &CS, llvm::raw_ostream &OS, llvm::StringRef Options) { diff --git a/lldb/test/API/commands/statistics/basic/TestStats.py b/lldb/test/API/commands/statistics/basic/TestStats.py --- a/lldb/test/API/commands/statistics/basic/TestStats.py +++ b/lldb/test/API/commands/statistics/basic/TestStats.py @@ -135,6 +135,7 @@ (lldb) statistics dump { + "memory" : {...}, "modules" : [...], "targets" : [ { @@ -160,6 +161,7 @@ target = self.createTestTarget() debug_stats = self.get_stats() debug_stat_keys = [ + 'memory', 'modules', 'targets', 'totalSymbolTableParseTime', @@ -197,6 +199,7 @@ (lldb) statistics dump { + "memory" : {...}, "modules" : [...], "targets" : [ { @@ -227,6 +230,7 @@ lldb.SBFileSpec("main.c")) debug_stats = self.get_stats() debug_stat_keys = [ + 'memory', 'modules', 'targets', 'totalSymbolTableParseTime', @@ -254,6 +258,44 @@ self.assertGreater(stats['launchOrAttachTime'], 0.0) self.assertGreater(stats['targetCreateTime'], 0.0) + def test_memory(self): + """ + Test "statistics dump" and the memory information. + """ + exe = self.getBuildArtifact("a.out") + target = self.createTestTarget(file_path=exe) + debug_stats = self.get_stats() + debug_stat_keys = [ + 'memory', + 'modules', + 'targets', + 'totalSymbolTableParseTime', + 'totalSymbolTableIndexTime', + 'totalSymbolTablesLoadedFromCache', + 'totalSymbolTablesSavedToCache', + 'totalDebugInfoParseTime', + 'totalDebugInfoIndexTime', + 'totalDebugInfoIndexLoadedFromCache', + 'totalDebugInfoIndexSavedToCache', + 'totalDebugInfoByteSize' + ] + self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None) + + memory = debug_stats['memory'] + memory_keys= [ + 'strings', + ] + self.verify_keys(memory, '"memory"', memory_keys, None) + + strings = memory['strings'] + strings_keys= [ + 'bytesTotal', + 'bytesUsed', + 'bytesUnused', + ] + self.verify_keys(strings, '"strings"', strings_keys, None) + + def find_module_in_metrics(self, path, stats): modules = stats['modules'] for module in modules: @@ -269,6 +311,7 @@ target = self.createTestTarget(file_path=exe) debug_stats = self.get_stats() debug_stat_keys = [ + 'memory', 'modules', 'targets', 'totalSymbolTableParseTime', @@ -312,6 +355,7 @@ Output expected to be something like: { + "memory" : {...}, "modules" : [...], "targets" : [ { @@ -355,6 +399,7 @@ self.runCmd("b a_function") debug_stats = self.get_stats() debug_stat_keys = [ + 'memory', 'modules', 'targets', 'totalSymbolTableParseTime',