diff --git a/clang-tools-extra/clangd/support/MemoryTree.h b/clang-tools-extra/clangd/support/MemoryTree.h --- a/clang-tools-extra/clangd/support/MemoryTree.h +++ b/clang-tools-extra/clangd/support/MemoryTree.h @@ -65,6 +65,10 @@ /// Returns total number of bytes used by this node only. size_t self() const { return Size; } + /// Records total memory usage of each node under "memory_usage" metric. + /// Labels are edges on the path joined with ".", starting with \p RootName. + void record(std::string RootName) const; + private: /// Adds a child with an edge labeled as \p Name. Multiple calls to this /// function returns the same node. diff --git a/clang-tools-extra/clangd/support/MemoryTree.cpp b/clang-tools-extra/clangd/support/MemoryTree.cpp --- a/clang-tools-extra/clangd/support/MemoryTree.cpp +++ b/clang-tools-extra/clangd/support/MemoryTree.cpp @@ -1,4 +1,5 @@ #include "support/MemoryTree.h" +#include "Trace.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include @@ -6,6 +7,24 @@ namespace clang { namespace clangd { +namespace { + +size_t traverseTree(const MemoryTree &MT, std::string &ComponentName) { + size_t OriginalLen = ComponentName.size(); + if (!ComponentName.empty()) + ComponentName += '.'; + size_t Total = MT.self(); + for (const auto &Entry : MT.children()) { + ComponentName += Entry.first; + Total += traverseTree(Entry.getSecond(), ComponentName); + ComponentName.resize(OriginalLen + 1); + } + ComponentName.resize(OriginalLen); + trace::recordMemoryUsage(Total, ComponentName); + return Total; +} +} // namespace + MemoryTree &MemoryTree::createChild(llvm::StringRef Name) { auto &Child = Children.try_emplace(Name, DetailAlloc).first->getSecond(); return Child; @@ -22,5 +41,9 @@ Total += Entry.getSecond().total(); return Total; } + +void MemoryTree::record(std::string RootName) const { + traverseTree(*this, RootName); +} } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/support/Trace.h b/clang-tools-extra/clangd/support/Trace.h --- a/clang-tools-extra/clangd/support/Trace.h +++ b/clang-tools-extra/clangd/support/Trace.h @@ -68,6 +68,10 @@ const llvm::StringLiteral LabelName; }; +/// Convenient helper for collecting memory usage metrics from across multiple +/// components. Results are recorded under a metric named "memory_usage". +void recordMemoryUsage(double Value, llvm::StringRef ComponentName); + /// A consumer of trace events and measurements. The events are produced by /// Spans and trace::log, the measurements are produced by Metrics::record. /// Implementations of this interface must be thread-safe. diff --git a/clang-tools-extra/clangd/support/Trace.cpp b/clang-tools-extra/clangd/support/Trace.cpp --- a/clang-tools-extra/clangd/support/Trace.cpp +++ b/clang-tools-extra/clangd/support/Trace.cpp @@ -20,6 +20,7 @@ #include #include #include +#include namespace clang { namespace clangd { @@ -326,6 +327,13 @@ Context EventTracer::beginSpan(llvm::StringRef Name, llvm::json::Object *Args) { return Context::current().clone(); } + +void recordMemoryUsage(double Value, llvm::StringRef ComponentName) { + static constexpr Metric MemoryUsage("memory_usage", Metric::Value, + "component_name"); + + MemoryUsage.record(Value, ComponentName); +} } // namespace trace } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/unittests/support/MemoryTreeTests.cpp b/clang-tools-extra/clangd/unittests/support/MemoryTreeTests.cpp --- a/clang-tools-extra/clangd/unittests/support/MemoryTreeTests.cpp +++ b/clang-tools-extra/clangd/unittests/support/MemoryTreeTests.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "support/MemoryTree.h" +#include "support/TestTracer.h" #include "llvm/Support/Allocator.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -16,6 +17,7 @@ namespace clangd { namespace { using testing::Contains; +using testing::ElementsAre; using testing::IsEmpty; using testing::UnorderedElementsAre; @@ -73,6 +75,44 @@ EXPECT_THAT(Detail.children(), Contains(WithNameAndSize("leaf", 1))); } } + +TEST(MemoryTree, Record) { + trace::TestTracer Tracer; + constexpr llvm::StringLiteral MetricName = "memory_usage"; + auto AddNodes = [](MemoryTree Root) { + Root.child("leaf").addUsage(1); + + { + auto &Detail = Root.detail("detail"); + Detail.addUsage(1); + Detail.child("leaf").addUsage(1); + auto &Child = Detail.child("child"); + Child.addUsage(1); + Child.child("leaf").addUsage(1); + } + + { + auto &Child = Root.child("child"); + Child.addUsage(1); + Child.child("leaf").addUsage(1); + } + return Root; + }; + + llvm::BumpPtrAllocator Alloc; + AddNodes(MemoryTree(&Alloc)).record("root"); + EXPECT_THAT(Tracer.takeMetric(MetricName, "root"), ElementsAre(7)); + EXPECT_THAT(Tracer.takeMetric(MetricName, "root.leaf"), ElementsAre(1)); + EXPECT_THAT(Tracer.takeMetric(MetricName, "root.detail"), ElementsAre(4)); + EXPECT_THAT(Tracer.takeMetric(MetricName, "root.detail.leaf"), + ElementsAre(1)); + EXPECT_THAT(Tracer.takeMetric(MetricName, "root.detail.child"), + ElementsAre(2)); + EXPECT_THAT(Tracer.takeMetric(MetricName, "root.detail.child.leaf"), + ElementsAre(1)); + EXPECT_THAT(Tracer.takeMetric(MetricName, "root.child"), ElementsAre(2)); + EXPECT_THAT(Tracer.takeMetric(MetricName, "root.child.leaf"), ElementsAre(1)); +} } // namespace } // namespace clangd } // namespace clang