Skip to content

Commit

Permalink
[lldb] Add ConstString memory usage statistics
Browse files Browse the repository at this point in the history
Add statistics about the memory usage of the string pool. I'm
particularly interested in the memory used by the allocator, i.e. the
number of bytes actually used by the allocator it self as well as the
number of bytes allocated through the allocator.

Differential revision: https://reviews.llvm.org/D117914
  • Loading branch information
JDevlieghere committed Jan 24, 2022
1 parent 9407a70 commit cd8122b
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 0 deletions.
6 changes: 6 additions & 0 deletions lldb/include/lldb/Target/Statistics.h
Expand Up @@ -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"
Expand Down Expand Up @@ -110,6 +111,11 @@ struct ModuleStats {
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:
Expand Down
10 changes: 10 additions & 0 deletions lldb/include/lldb/Utility/ConstString.h
Expand Up @@ -408,6 +408,16 @@ class ConstString {
/// 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 <typename T, typename Enable> friend struct ::llvm::DenseMapInfo;
/// Only used by DenseMapInfo.
Expand Down
14 changes: 14 additions & 0 deletions lldb/source/Target/Statistics.cpp
Expand Up @@ -65,6 +65,14 @@ json::Value ModuleStats::ToJSON() const {
return module;
}

llvm::json::Value ConstStringStats::ToJSON() const {
json::Object obj;
obj.try_emplace<int64_t>("bytesTotal", stats.GetBytesTotal());
obj.try_emplace<int64_t>("bytesUsed", stats.GetBytesUsed());
obj.try_emplace<int64_t>("bytesUnused", stats.GetBytesUnused());
return obj;
}

json::Value TargetStats::ToJSON(Target &target) {
CollectStats(target);

Expand Down Expand Up @@ -212,9 +220,15 @@ llvm::json::Value DebuggerStats::ReportStatistics(Debugger &debugger,
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},
Expand Down
15 changes: 15 additions & 0 deletions lldb/source/Utility/ConstString.cpp
Expand Up @@ -171,6 +171,17 @@ class Pool {
return mem_size;
}

ConstString::MemoryStats GetMemoryStats() const {
ConstString::MemoryStats stats;
for (const auto &pool : m_string_pools) {
llvm::sys::SmartScopedReader<false> 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);
Expand Down Expand Up @@ -332,6 +343,10 @@ size_t ConstString::StaticMemorySize() {
return StringPool().MemorySize();
}

ConstString::MemoryStats ConstString::GetMemoryStats() {
return StringPool().GetMemoryStats();
}

void llvm::format_provider<ConstString>::format(const ConstString &CS,
llvm::raw_ostream &OS,
llvm::StringRef Options) {
Expand Down
45 changes: 45 additions & 0 deletions lldb/test/API/commands/statistics/basic/TestStats.py
Expand Up @@ -135,6 +135,7 @@ def test_default_no_run(self):
(lldb) statistics dump
{
"memory" : {...},
"modules" : [...],
"targets" : [
{
Expand All @@ -160,6 +161,7 @@ def test_default_no_run(self):
target = self.createTestTarget()
debug_stats = self.get_stats()
debug_stat_keys = [
'memory',
'modules',
'targets',
'totalSymbolTableParseTime',
Expand Down Expand Up @@ -197,6 +199,7 @@ def test_default_with_run(self):
(lldb) statistics dump
{
"memory" : {...},
"modules" : [...],
"targets" : [
{
Expand Down Expand Up @@ -227,6 +230,7 @@ def test_default_with_run(self):
lldb.SBFileSpec("main.c"))
debug_stats = self.get_stats()
debug_stat_keys = [
'memory',
'modules',
'targets',
'totalSymbolTableParseTime',
Expand Down Expand Up @@ -254,6 +258,44 @@ def test_default_with_run(self):
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:
Expand All @@ -269,6 +311,7 @@ def test_modules(self):
target = self.createTestTarget(file_path=exe)
debug_stats = self.get_stats()
debug_stat_keys = [
'memory',
'modules',
'targets',
'totalSymbolTableParseTime',
Expand Down Expand Up @@ -312,6 +355,7 @@ def test_breakpoints(self):
Output expected to be something like:
{
"memory" : {...},
"modules" : [...],
"targets" : [
{
Expand Down Expand Up @@ -355,6 +399,7 @@ def test_breakpoints(self):
self.runCmd("b a_function")
debug_stats = self.get_stats()
debug_stat_keys = [
'memory',
'modules',
'targets',
'totalSymbolTableParseTime',
Expand Down

0 comments on commit cd8122b

Please sign in to comment.