[compiler-rt] [ctxprof] Auto root detection: trie for stack samples (PR #133106)

Snehasish Kumar via llvm-commits llvm-commits at lists.llvm.org
Thu Mar 27 13:24:21 PDT 2025


================
@@ -0,0 +1,123 @@
+#include "../RootAutoDetector.h"
+#include "sanitizer_common/sanitizer_array_ref.h"
+#include "gtest/gtest.h"
+
+using namespace __ctx_profile;
+
+class MockCallsiteTree final : public PerThreadCallsiteTrie {
+  // Return the first multiple of 100.
+  uptr getFctStartAddr(uptr CallsiteAddress) const override {
+    return (CallsiteAddress / 100) * 100;
+  }
+};
+
+class Marker {
+  enum class Kind { End, Value, Split };
+  const uptr Value;
+  const Kind K;
+  Marker(uptr V, Kind S) : Value(V), K(S) {}
+
+public:
+  Marker(uptr V) : Marker(V, Kind::Value) {}
+
+  static Marker split(uptr V) { return Marker(V, Kind::Split); }
+  static Marker term() { return Marker(0, Kind::End); }
+
+  bool isSplit() const { return K == Kind::Split; }
+  bool isTerm() const { return K == Kind::End; }
+  bool isVal() const { return K == Kind::Value; }
+
+  bool operator==(const Marker &M) const {
+    return Value == M.Value && K == M.K;
+  }
+};
+
+void popAndCheck(ArrayRef<Marker> &Preorder, Marker M) {
+  ASSERT_FALSE(Preorder.empty());
+  ASSERT_EQ(Preorder[0], M);
+  Preorder = Preorder.drop_front();
+}
+
+void checkSameImpl(const Trie &T, ArrayRef<Marker> &Preorder) {
+  popAndCheck(Preorder, T.address());
+
+  if (T.children().size() == 0) {
+    popAndCheck(Preorder, Marker::term());
+    return;
+  }
+
+  if (T.children().size() > 1)
+    popAndCheck(Preorder, Marker::split(T.children().size()));
+
+  T.children().forEach([&](const auto &KVP) {
+    checkSameImpl(KVP.second, Preorder);
+    return true;
+  });
+}
+
+void checkSame(const PerThreadCallsiteTrie &RCT, ArrayRef<Marker> Preorder) {
+  checkSameImpl(RCT.start(), Preorder);
+  ASSERT_TRUE(Preorder.empty());
+}
+
+TEST(PerThreadCallsiteTrieTest, Insert) {
+  PerThreadCallsiteTrie R;
+  uptr Stack1[]{4, 3, 2, 1};
+  R.insertStack(StackTrace(Stack1, 4));
+  checkSame(R, ArrayRef<Marker>({0, 1, 2, 3, 4, Marker::term()}));
+
+  uptr Stack2[]{5, 4, 3, 2, 1};
+  R.insertStack(StackTrace(Stack2, 5));
+  checkSame(R, ArrayRef<Marker>({0, 1, 2, 3, 4, 5, Marker::term()}));
+
+  uptr Stack3[]{6, 3, 2, 1};
+  R.insertStack(StackTrace(Stack3, 4));
+  checkSame(R, ArrayRef<Marker>({0, 1, 2, 3, Marker::split(2), 4, 5,
+                                 Marker::term(), 6, Marker::term()}));
+
+  uptr Stack4[]{7, 2, 1};
+  R.insertStack(StackTrace(Stack4, 3));
+  checkSame(R, ArrayRef<Marker>({0, 1, 2, Marker::split(2), 7, Marker::term(),
+                                 3, Marker::split(2), 4, 5, Marker::term(), 6,
+                                 Marker::term()}));
+}
+
+TEST(PerThreadCallsiteTrieTest, DetectRoots) {
+  MockCallsiteTree T;
+
+  uptr Stack1[]{501, 302, 202, 102};
+  uptr Stack2[]{601, 402, 203, 102};
+  T.insertStack({Stack1, 4});
+  T.insertStack({Stack2, 4});
+
+  auto R = T.determineRoots();
+  EXPECT_EQ(R.size(), 2U);
+  EXPECT_TRUE(R.contains(300));
+  EXPECT_TRUE(R.contains(400));
+}
+
+TEST(PerThreadCallsiteTrieTest, DetectRootsNoBranches) {
+  MockCallsiteTree T;
+
+  uptr Stack1[]{501, 302, 202, 102};
+  T.insertStack({Stack1, 4});
+
+  auto R = T.determineRoots();
+  EXPECT_EQ(R.size(), 0U);
+}
+
+TEST(PerThreadCallsiteTrieTest, DetectRootsUnknownFct) {
+  MockCallsiteTree T;
+
+  uptr Stack1[]{501, 302, 202, 102};
+  // The MockCallsiteTree address resolver resolves addresses over 100, so 40
+  // will be mapped to 0.
+  uptr Stack2[]{601, 40, 203, 102};
+  T.insertStack({Stack1, 4});
+  T.insertStack({Stack2, 4});
+
+  auto R = T.determineRoots();
+  EXPECT_EQ(R.size(), 2U);
----------------
snehasish wrote:

nit: `ASSERT_EQ(R, SizeIs(2U));`

Same elsewhere.

https://github.com/llvm/llvm-project/pull/133106


More information about the llvm-commits mailing list