[clang-tools-extra] f9317f7 - [clangd] Introduce MemoryTrees

Kadir Cetinkaya via cfe-commits cfe-commits at lists.llvm.org
Mon Oct 12 06:27:18 PDT 2020


Author: Kadir Cetinkaya
Date: 2020-10-12T15:25:29+02:00
New Revision: f9317f7bf6bdac10d6f8a1c106ef8d489da7efbf

URL: https://github.com/llvm/llvm-project/commit/f9317f7bf6bdac10d6f8a1c106ef8d489da7efbf
DIFF: https://github.com/llvm/llvm-project/commit/f9317f7bf6bdac10d6f8a1c106ef8d489da7efbf.diff

LOG: [clangd] Introduce MemoryTrees

A structure that can be used to represent memory usage of a nested
set of systems.

Differential Revision: https://reviews.llvm.org/D88411

Added: 
    clang-tools-extra/clangd/support/MemoryTree.cpp
    clang-tools-extra/clangd/support/MemoryTree.h
    clang-tools-extra/clangd/unittests/support/MemoryTreeTests.cpp

Modified: 
    clang-tools-extra/clangd/support/CMakeLists.txt
    clang-tools-extra/clangd/unittests/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clangd/support/CMakeLists.txt b/clang-tools-extra/clangd/support/CMakeLists.txt
index ce08f7d58cd0..e3412447142c 100644
--- a/clang-tools-extra/clangd/support/CMakeLists.txt
+++ b/clang-tools-extra/clangd/support/CMakeLists.txt
@@ -21,6 +21,7 @@ add_clang_library(clangdSupport
   Context.cpp
   Logger.cpp
   Markup.cpp
+  MemoryTree.cpp
   Shutdown.cpp
   Threading.cpp
   ThreadsafeFS.cpp

diff  --git a/clang-tools-extra/clangd/support/MemoryTree.cpp b/clang-tools-extra/clangd/support/MemoryTree.cpp
new file mode 100644
index 000000000000..d7acf7b3d8c0
--- /dev/null
+++ b/clang-tools-extra/clangd/support/MemoryTree.cpp
@@ -0,0 +1,26 @@
+#include "support/MemoryTree.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include <cstddef>
+
+namespace clang {
+namespace clangd {
+
+MemoryTree &MemoryTree::createChild(llvm::StringRef Name) {
+  auto &Child = Children.try_emplace(Name, DetailAlloc).first->getSecond();
+  return Child;
+}
+
+const llvm::DenseMap<llvm::StringRef, MemoryTree> &
+MemoryTree::children() const {
+  return Children;
+}
+
+size_t MemoryTree::total() const {
+  size_t Total = Size;
+  for (const auto &Entry : Children)
+    Total += Entry.getSecond().total();
+  return Total;
+}
+} // namespace clangd
+} // namespace clang

diff  --git a/clang-tools-extra/clangd/support/MemoryTree.h b/clang-tools-extra/clangd/support/MemoryTree.h
new file mode 100644
index 000000000000..9cb7be5e04fd
--- /dev/null
+++ b/clang-tools-extra/clangd/support/MemoryTree.h
@@ -0,0 +1,86 @@
+//===--- MemoryTree.h - A special tree for components and sizes -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_MEMORYTREE_H_
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_MEMORYTREE_H_
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/StringSaver.h"
+#include <cstddef>
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace clangd {
+
+/// A tree that can be used to represent memory usage of nested components while
+/// preserving the hierarchy.
+/// Edges have associated names. An edge that might not be interesting to all
+/// traversers or costly to copy (e.g. file names) can be marked as "detail".
+/// Tree construction allows chosing between a detailed and brief mode, in brief
+/// mode all "detail" edges are ignored and tree is constructed without any
+/// string copies.
+struct MemoryTree {
+public:
+  /// If Alloc is nullptr, tree is in brief mode and will ignore detail edges.
+  MemoryTree(llvm::BumpPtrAllocator *DetailAlloc = nullptr)
+      : DetailAlloc(DetailAlloc) {}
+
+  /// No copy of the \p Name.
+  /// Note that returned pointers are invalidated with subsequent calls to
+  /// child/detail.
+  MemoryTree &child(llvm::StringLiteral Name) { return createChild(Name); }
+
+  MemoryTree(const MemoryTree &) = delete;
+  MemoryTree &operator=(const MemoryTree &) = delete;
+
+  MemoryTree(MemoryTree &&) = default;
+  MemoryTree &operator=(MemoryTree &&) = default;
+
+  /// Makes a copy of the \p Name in detailed mode, returns current node
+  /// otherwise.
+  /// Note that returned pointers are invalidated with subsequent calls to
+  /// child/detail.
+  MemoryTree &detail(llvm::StringRef Name) {
+    return DetailAlloc ? createChild(Name.copy(*DetailAlloc)) : *this;
+  }
+
+  /// Increases size of current node by \p Increment.
+  void addUsage(size_t Increment) { Size += Increment; }
+
+  /// Returns edges to direct children of this node.
+  const llvm::DenseMap<llvm::StringRef, MemoryTree> &children() const;
+
+  /// Returns total number of bytes used by this sub-tree. Performs a traversal.
+  size_t total() const;
+
+  /// Returns total number of bytes used by this node only.
+  size_t self() const { return Size; }
+
+private:
+  /// Adds a child with an edge labeled as \p Name. Multiple calls to this
+  /// function returns the same node.
+  MemoryTree &createChild(llvm::StringRef Name);
+
+  /// Allocator to use for detailed edge names.
+  llvm::BumpPtrAllocator *DetailAlloc = nullptr;
+
+  /// Bytes owned by this component specifically.
+  size_t Size = 0;
+
+  /// Edges from current node to its children. Keys are the labels for edges.
+  llvm::DenseMap<llvm::StringRef, MemoryTree> Children;
+};
+
+} // namespace clangd
+} // namespace clang
+
+#endif

diff  --git a/clang-tools-extra/clangd/unittests/CMakeLists.txt b/clang-tools-extra/clangd/unittests/CMakeLists.txt
index a84fd0b71ca5..de8eaca6059f 100644
--- a/clang-tools-extra/clangd/unittests/CMakeLists.txt
+++ b/clang-tools-extra/clangd/unittests/CMakeLists.txt
@@ -99,6 +99,7 @@ add_unittest(ClangdUnitTests ClangdTests
   support/ContextTests.cpp
   support/FunctionTests.cpp
   support/MarkupTests.cpp
+  support/MemoryTreeTests.cpp
   support/ThreadingTests.cpp
   support/TestTracer.cpp
   support/TraceTests.cpp

diff  --git a/clang-tools-extra/clangd/unittests/support/MemoryTreeTests.cpp b/clang-tools-extra/clangd/unittests/support/MemoryTreeTests.cpp
new file mode 100644
index 000000000000..72095f04fc68
--- /dev/null
+++ b/clang-tools-extra/clangd/unittests/support/MemoryTreeTests.cpp
@@ -0,0 +1,78 @@
+//===-- MemoryTreeTests.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "support/MemoryTree.h"
+#include "llvm/Support/Allocator.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <ostream>
+
+namespace clang {
+namespace clangd {
+namespace {
+using testing::Contains;
+using testing::IsEmpty;
+using testing::UnorderedElementsAre;
+
+MATCHER_P2(WithNameAndSize, Name, Size, "") {
+  return arg.first == Name &&
+         arg.getSecond().total() == static_cast<size_t>(Size);
+}
+
+TEST(MemoryTree, Basics) {
+  MemoryTree MT;
+  EXPECT_EQ(MT.total(), 0U);
+  EXPECT_THAT(MT.children(), IsEmpty());
+
+  MT.addUsage(42);
+  EXPECT_EQ(MT.total(), 42U);
+  EXPECT_THAT(MT.children(), IsEmpty());
+
+  MT.child("leaf").addUsage(1);
+  EXPECT_EQ(MT.total(), 43U);
+  EXPECT_THAT(MT.children(), UnorderedElementsAre(WithNameAndSize("leaf", 1)));
+
+  // child should be idempotent.
+  MT.child("leaf").addUsage(1);
+  EXPECT_EQ(MT.total(), 44U);
+  EXPECT_THAT(MT.children(), UnorderedElementsAre(WithNameAndSize("leaf", 2)));
+}
+
+TEST(MemoryTree, DetailedNodesWithoutDetails) {
+  MemoryTree MT;
+  MT.detail("should_be_ignored").addUsage(2);
+  EXPECT_THAT(MT.children(), IsEmpty());
+  EXPECT_EQ(MT.total(), 2U);
+
+  // Make sure children from details are merged.
+  MT.detail("first_detail").child("leaf").addUsage(1);
+  MT.detail("second_detail").child("leaf").addUsage(1);
+  EXPECT_THAT(MT.children(), Contains(WithNameAndSize("leaf", 2)));
+}
+
+TEST(MemoryTree, DetailedNodesWithDetails) {
+  llvm::BumpPtrAllocator Alloc;
+  MemoryTree MT(&Alloc);
+
+  {
+    auto &Detail = MT.detail("first_detail");
+    Detail.child("leaf").addUsage(1);
+    EXPECT_THAT(MT.children(), Contains(WithNameAndSize("first_detail", 1)));
+    EXPECT_THAT(Detail.children(), Contains(WithNameAndSize("leaf", 1)));
+  }
+
+  {
+    auto &Detail = MT.detail("second_detail");
+    Detail.child("leaf").addUsage(1);
+    EXPECT_THAT(MT.children(), Contains(WithNameAndSize("second_detail", 1)));
+    EXPECT_THAT(Detail.children(), Contains(WithNameAndSize("leaf", 1)));
+  }
+}
+} // namespace
+} // namespace clangd
+} // namespace clang


        


More information about the cfe-commits mailing list