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 @@ -9,6 +9,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_MEMORYTREE_H_ #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_MEMORYTREE_H_ +#include "Trace.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" @@ -80,6 +81,11 @@ llvm::DenseMap Children; }; +/// Records total memory usage of each node under \p Out. Labels are edges on +/// the path joined with ".", starting with \p RootName. +void record(const MemoryTree &MT, std::string RootName, + const trace::Metric &Out); + } // namespace clangd } // namespace clang 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,25 @@ namespace clang { namespace clangd { +namespace { + +size_t traverseTree(const MemoryTree &MT, std::string &ComponentName, + const trace::Metric &Out) { + 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, Out); + ComponentName.resize(OriginalLen + 1); + } + ComponentName.resize(OriginalLen); + Out.record(Total, ComponentName); + return Total; +} +} // namespace + MemoryTree &MemoryTree::createChild(llvm::StringRef Name) { auto &Child = Children.try_emplace(Name, DetailAlloc).first->getSecond(); return Child; @@ -22,5 +42,10 @@ Total += Entry.getSecond().total(); return Total; } + +void record(const MemoryTree &MT, std::string RootName, + const trace::Metric &Out) { + traverseTree(MT, RootName, Out); +} } // 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,8 @@ //===----------------------------------------------------------------------===// #include "support/MemoryTree.h" +#include "support/TestTracer.h" +#include "support/Trace.h" #include "llvm/Support/Allocator.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -16,6 +18,7 @@ namespace clangd { namespace { using testing::Contains; +using testing::ElementsAre; using testing::IsEmpty; using testing::UnorderedElementsAre; @@ -73,6 +76,46 @@ EXPECT_THAT(Detail.children(), Contains(WithNameAndSize("leaf", 1))); } } + +TEST(MemoryTree, Record) { + trace::TestTracer Tracer; + static constexpr llvm::StringLiteral MetricName = "memory_usage"; + static constexpr trace::Metric OutMetric(MetricName, trace::Metric::Value, + "component_name"); + 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; + record(AddNodes(MemoryTree(&Alloc)), "root", OutMetric); + 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