[compiler-rt] aa44f59 - [NFC][sanitizer] Add Debug utility to print thread history (#111948)

via llvm-commits llvm-commits at lists.llvm.org
Fri Oct 11 16:58:45 PDT 2024


Author: Vitaly Buka
Date: 2024-10-11T16:58:42-07:00
New Revision: aa44f59abf399f81585898fb95e66518ef3591af

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

LOG: [NFC][sanitizer] Add Debug utility to print thread history (#111948)

For #111949

Added: 
    compiler-rt/lib/sanitizer_common/sanitizer_thread_history.cpp
    compiler-rt/lib/sanitizer_common/sanitizer_thread_history.h

Modified: 
    compiler-rt/lib/sanitizer_common/CMakeLists.txt
    compiler-rt/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cpp

Removed: 
    


################################################################################
diff  --git a/compiler-rt/lib/sanitizer_common/CMakeLists.txt b/compiler-rt/lib/sanitizer_common/CMakeLists.txt
index 556a64f3017488..09391e4f5f3704 100644
--- a/compiler-rt/lib/sanitizer_common/CMakeLists.txt
+++ b/compiler-rt/lib/sanitizer_common/CMakeLists.txt
@@ -94,6 +94,7 @@ set(SANITIZER_SYMBOLIZER_SOURCES
   sanitizer_symbolizer_report.cpp
   sanitizer_symbolizer_report_fuchsia.cpp
   sanitizer_symbolizer_win.cpp
+  sanitizer_thread_history.cpp
   sanitizer_unwind_linux_libcdep.cpp
   sanitizer_unwind_fuchsia.cpp
   sanitizer_unwind_win.cpp

diff  --git a/compiler-rt/lib/sanitizer_common/sanitizer_thread_history.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_thread_history.cpp
new file mode 100644
index 00000000000000..0f5bec3ca083e3
--- /dev/null
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_thread_history.cpp
@@ -0,0 +1,72 @@
+//===-- sanitizer_thread_history.cpp --------------------------------------===//
+//
+// 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 "sanitizer_thread_history.h"
+
+#include "sanitizer_stackdepot.h"
+namespace __sanitizer {
+
+void PrintThreadHistory(ThreadRegistry &registry, InternalScopedString &out) {
+  ThreadRegistryLock l(&registry);
+  // Stack traces are largest part of printout and they often the same for
+  // multiple threads, so we will deduplicate them.
+  InternalMmapVector<const ThreadContextBase *> stacks;
+
+  registry.RunCallbackForEachThreadLocked(
+      [](ThreadContextBase *context, void *arg) {
+        static_cast<decltype(&stacks)>(arg)->push_back(context);
+      },
+      &stacks);
+
+  Sort(stacks.data(), stacks.size(),
+       [](const ThreadContextBase *a, const ThreadContextBase *b) {
+         if (a->stack_id < b->stack_id)
+           return true;
+         if (a->stack_id > b->stack_id)
+           return false;
+         return a->unique_id < b->unique_id;
+       });
+
+  auto describe_thread = [&](const ThreadContextBase *context) {
+    if (!context) {
+      out.Append("T-1");
+      return;
+    }
+    out.AppendF("T%llu/%llu", context->unique_id, context->os_id);
+    if (internal_strlen(context->name))
+      out.AppendF(" (%s)", context->name);
+  };
+
+  auto get_parent =
+      [&](const ThreadContextBase *context) -> const ThreadContextBase * {
+    if (!context)
+      return nullptr;
+    ThreadContextBase *parent = registry.GetThreadLocked(context->parent_tid);
+    if (!parent)
+      return nullptr;
+    if (parent->unique_id >= context->unique_id)
+      return nullptr;
+    return parent;
+  };
+
+  const ThreadContextBase *prev = nullptr;
+  for (const ThreadContextBase *context : stacks) {
+    if (prev && prev->stack_id != context->stack_id)
+      StackDepotGet(prev->stack_id).PrintTo(&out);
+    prev = context;
+    out.Append("Thread ");
+    describe_thread(context);
+    out.Append(" was created by ");
+    describe_thread(get_parent(context));
+    out.Append("\n");
+  }
+  if (prev)
+    StackDepotGet(prev->stack_id).PrintTo(&out);
+}
+
+}  // namespace __sanitizer

diff  --git a/compiler-rt/lib/sanitizer_common/sanitizer_thread_history.h b/compiler-rt/lib/sanitizer_common/sanitizer_thread_history.h
new file mode 100644
index 00000000000000..2995f6015fe50e
--- /dev/null
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_thread_history.h
@@ -0,0 +1,24 @@
+//===-- sanitizer_thread_history.h ------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Utility to print thread histroy from ThreadRegistry.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_THREAD_HISTORY_H
+#define SANITIZER_THREAD_HISTORY_H
+
+#include "sanitizer_thread_registry.h"
+
+namespace __sanitizer {
+
+void PrintThreadHistory(ThreadRegistry& registry, InternalScopedString& out);
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_THREAD_HISTORY_H

diff  --git a/compiler-rt/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cpp b/compiler-rt/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cpp
index c3cac707f0a0b3..6e84ecdfeba683 100644
--- a/compiler-rt/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cpp
+++ b/compiler-rt/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cpp
@@ -11,11 +11,19 @@
 //===----------------------------------------------------------------------===//
 #include "sanitizer_common/sanitizer_thread_registry.h"
 
+#include <iostream>
 #include <vector>
 
+#include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "sanitizer_common/sanitizer_thread_history.h"
 #include "sanitizer_pthread_wrappers.h"
 
+using testing::HasSubstr;
+
 namespace __sanitizer {
 
 static Mutex tctx_allocator_lock;
@@ -239,4 +247,56 @@ TEST(SanitizerCommon, ThreadRegistryThreadedTest) {
   ThreadedTestRegistry(&registry);
 }
 
+TEST(SanitizerCommon, PrintThreadHistory) {
+  ThreadRegistry registry(GetThreadContext<TestThreadContext>,
+                          kThreadsPerShard * kNumShards + 1, 10, 0);
+
+  UNINITIALIZED BufferedStackTrace stack1;
+  stack1.Unwind(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), nullptr, false,
+                /*max_depth=*/1);
+
+  UNINITIALIZED BufferedStackTrace stack2;
+  stack2.Unwind(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), nullptr, false,
+                /*max_depth=*/1);
+
+  EXPECT_EQ(0U, registry.CreateThread(0, true, -1, 0, nullptr));
+  for (int i = 0; i < 5; i++) {
+    registry.CreateThread(0, true, 0, StackDepotPut(stack1), nullptr);
+    registry.CreateThread(0, true, 0, StackDepotPut(stack2), nullptr);
+  }
+
+  InternalScopedString out;
+  PrintThreadHistory(registry, out);
+
+  std::string substrings[] = {
+      "Thread T0/0 was created by T-1",
+      "<empty stack>",
+      "",
+      "Thread T1/0 was created by T0/0",
+      "Thread T3/0 was created by T0/0",
+      "Thread T5/0 was created by T0/0",
+      "Thread T7/0 was created by T0/0",
+      "Thread T9/0 was created by T0/0",
+      "#0 0x",
+      "",
+      "Thread T2/0 was created by T0/0",
+      "Thread T4/0 was created by T0/0",
+      "Thread T6/0 was created by T0/0",
+      "Thread T8/0 was created by T0/0",
+      "Thread T10/0 was created by T0/0",
+      "#0 0x",
+      "",
+  };
+
+  std::stringstream ss(out.data());
+  std::string line;
+
+  for (auto substr : substrings) {
+    std::getline(ss, line);
+    EXPECT_THAT(line, HasSubstr(substr)) << line;
+  }
+
+  EXPECT_FALSE(std::getline(ss, line)) << "Unmatched line: " << line;
+}
+
 }  // namespace __sanitizer


        


More information about the llvm-commits mailing list