This revision was automatically updated to reflect the committed changes.
mgorny marked an inline comment as done.
Closed by commit rG86e472317c8f: [lldb] [test] Improve stability of llgs 
vCont-threads tests (authored by mgorny).
Herald added a project: LLDB.

Changed prior to commit:
  https://reviews.llvm.org/D129012?vs=442823&id=442916#toc

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129012/new/

https://reviews.llvm.org/D129012

Files:
  lldb/test/API/tools/lldb-server/vCont-threads/TestGdbRemote_vContThreads.py
  lldb/test/API/tools/lldb-server/vCont-threads/TestPartialResume.py
  lldb/test/API/tools/lldb-server/vCont-threads/TestSignal.py
  lldb/test/API/tools/lldb-server/vCont-threads/main.cpp

Index: lldb/test/API/tools/lldb-server/vCont-threads/main.cpp
===================================================================
--- lldb/test/API/tools/lldb-server/vCont-threads/main.cpp
+++ lldb/test/API/tools/lldb-server/vCont-threads/main.cpp
@@ -1,31 +1,54 @@
 #include "pseudo_barrier.h"
 #include "thread.h"
+#include <atomic>
 #include <chrono>
 #include <cinttypes>
 #include <csignal>
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
+#include <mutex>
 #include <thread>
 #include <unistd.h>
 #include <vector>
 
 pseudo_barrier_t barrier;
+std::mutex print_mutex;
+std::atomic<bool> can_work = ATOMIC_VAR_INIT(false);
+thread_local volatile sig_atomic_t can_exit_now = false;
 
 static void sigusr1_handler(int signo) {
-  char buf[100];
-  std::snprintf(buf, sizeof(buf),
-                "received SIGUSR1 on thread id: %" PRIx64 "\n",
-                get_thread_id());
-  write(STDOUT_FILENO, buf, strlen(buf));
+  std::lock_guard<std::mutex> lock{print_mutex};
+  std::printf("received SIGUSR1 on thread id: %" PRIx64 "\n", get_thread_id());
+  can_exit_now = true;
 }
 
 static void thread_func() {
+  // this ensures that all threads start before we SIGSTOP
   pseudo_barrier_wait(barrier);
-  for (int i = 0; i < 300; ++i) {
+
+  // wait till the main thread indicates that we can go
+  // (note: using a mutex here causes hang on FreeBSD when another thread
+  // is suspended)
+  while (!can_work.load())
+    std::this_thread::sleep_for(std::chrono::milliseconds(50));
+
+  // the mutex guarantees that two writes don't get interspersed
+  {
+    std::lock_guard<std::mutex> lock{print_mutex};
     std::printf("thread %" PRIx64 " running\n", get_thread_id());
+  }
+
+  // give other threads a fair chance to run
+  for (int i = 0; i < 5; ++i) {
+    std::this_thread::yield();
     std::this_thread::sleep_for(std::chrono::milliseconds(200));
+    if (can_exit_now)
+      return;
   }
+
+  // if we didn't get signaled, terminate the program explicitly.
+  _exit(0);
 }
 
 int main(int argc, char **argv) {
@@ -36,12 +59,15 @@
   signal(SIGUSR1, sigusr1_handler);
 
   std::vector<std::thread> threads;
-  for(int i = 0; i < num; ++i)
+  for (int i = 0; i < num; ++i)
     threads.emplace_back(thread_func);
 
+  // use the barrier to make sure all threads start before we SIGSTOP
   pseudo_barrier_wait(barrier);
+  std::raise(SIGSTOP);
 
-  std::puts("@started");
+  // allow the threads to work
+  can_work.store(true);
 
   for (std::thread &thread : threads)
     thread.join();
Index: lldb/test/API/tools/lldb-server/vCont-threads/TestSignal.py
===================================================================
--- lldb/test/API/tools/lldb-server/vCont-threads/TestSignal.py
+++ lldb/test/API/tools/lldb-server/vCont-threads/TestSignal.py
@@ -1,23 +1,18 @@
-import json
 import re
-import time
 
 import gdbremote_testcase
 from lldbsuite.test.decorators import *
 from lldbsuite.test.lldbtest import *
 from lldbsuite.test import lldbutil
 
-class TestGdbRemote_vContThreads(gdbremote_testcase.GdbRemoteTestCaseBase):
 
+class TestSignal(gdbremote_testcase.GdbRemoteTestCaseBase):
     def start_threads(self, num):
         procs = self.prep_debug_monitor_and_inferior(inferior_args=[str(num)])
-        # start the process and wait for output
         self.test_sequence.add_log_lines([
             "read packet: $c#63",
-            {"type": "output_match", "regex": r".*@started\r\n.*"},
+            {"direction": "send", "regex": "[$]T.*;reason:signal.*"},
         ], True)
-        # then interrupt it
-        self.add_interrupt_packets()
         self.add_threadinfo_collection_packets()
 
         context = self.expect_gdbremote_sequence()
@@ -29,22 +24,21 @@
         self.reset_test_sequence()
         return threads
 
+    SIGNAL_MATCH_RE = re.compile(r"received SIGUSR1 on thread id: ([0-9a-f]+)")
+
     def send_and_check_signal(self, vCont_data, threads):
         self.test_sequence.add_log_lines([
             "read packet: $vCont;{0}#00".format(vCont_data),
-            {"type": "output_match",
-             "regex": len(threads) *
-                      r".*received SIGUSR1 on thread id: ([0-9a-f]+)\r\n.*",
-             "capture": dict((i, "tid{0}".format(i)) for i
-                             in range(1, len(threads)+1)),
-             },
+            "send packet: $W00#00",
         ], True)
-
-        context = self.expect_gdbremote_sequence()
-        self.assertIsNotNone(context)
-        tids = sorted(int(context["tid{0}".format(x)], 16)
-                      for x in range(1, len(threads)+1))
-        self.assertEqual(tids, sorted(threads))
+        exp = self.expect_gdbremote_sequence()
+        self.reset_test_sequence()
+        tids = []
+        for line in exp["O_content"].decode().splitlines():
+            m = self.SIGNAL_MATCH_RE.match(line)
+            if m is not None:
+                tids.append(int(m.group(1), 16))
+        self.assertEqual(sorted(tids), sorted(threads))
 
     def get_pid(self):
         self.add_process_info_collection_packets()
@@ -242,72 +236,3 @@
 
         context = self.expect_gdbremote_sequence()
         self.assertIsNotNone(context)
-
-    THREAD_MATCH_RE = re.compile(r"thread ([0-9a-f]+) running")
-
-    def continue_and_get_threads_running(self, continue_packet):
-        self.test_sequence.add_log_lines(
-            ["read packet: ${}#00".format(continue_packet),
-             ], True)
-        self.expect_gdbremote_sequence()
-        self.reset_test_sequence()
-        time.sleep(1)
-        self.add_interrupt_packets()
-        exp = self.expect_gdbremote_sequence()
-        found = set()
-        for line in exp["O_content"].decode().splitlines():
-            m = self.THREAD_MATCH_RE.match(line)
-            if m is not None:
-                found.add(int(m.group(1), 16))
-        return found
-
-    @skipIfWindows
-    @add_test_categories(["llgs"])
-    def test_vCont_run_subset_of_threads(self):
-        self.build()
-        self.set_inferior_startup_launch()
-
-        threads = set(self.start_threads(3))
-        all_subthreads = self.continue_and_get_threads_running("c")
-        all_subthreads_list = list(all_subthreads)
-        self.assertEqual(len(all_subthreads), 3)
-        self.assertEqual(threads & all_subthreads, all_subthreads)
-
-        # resume two threads explicitly, stop the third one implicitly
-        self.assertEqual(
-            self.continue_and_get_threads_running(
-                "vCont;c:{:x};c:{:x}".format(*all_subthreads_list[:2])),
-            set(all_subthreads_list[:2]))
-
-        # resume two threads explicitly, stop others explicitly
-        self.assertEqual(
-            self.continue_and_get_threads_running(
-                "vCont;c:{:x};c:{:x};t".format(*all_subthreads_list[:2])),
-            set(all_subthreads_list[:2]))
-
-        # stop one thread explicitly, resume others
-        self.assertEqual(
-            self.continue_and_get_threads_running(
-                "vCont;t:{:x};c".format(all_subthreads_list[-1])),
-            set(all_subthreads_list[:2]))
-
-        # resume one thread explicitly, stop one explicitly,
-        # resume others
-        self.assertEqual(
-            self.continue_and_get_threads_running(
-                "vCont;c:{:x};t:{:x};c".format(*all_subthreads_list[-2:])),
-            set(all_subthreads_list[:2]))
-
-        # resume one thread explicitly, stop one explicitly,
-        # stop others implicitly
-        self.assertEqual(
-            self.continue_and_get_threads_running(
-                "vCont;t:{:x};c:{:x}".format(*all_subthreads_list[:2])),
-            set(all_subthreads_list[1:2]))
-
-        # resume one thread explicitly, stop one explicitly,
-        # stop others explicitly
-        self.assertEqual(
-            self.continue_and_get_threads_running(
-                "vCont;t:{:x};c:{:x};t".format(*all_subthreads_list[:2])),
-            set(all_subthreads_list[1:2]))
Index: lldb/test/API/tools/lldb-server/vCont-threads/TestPartialResume.py
===================================================================
--- /dev/null
+++ lldb/test/API/tools/lldb-server/vCont-threads/TestPartialResume.py
@@ -0,0 +1,128 @@
+import re
+
+import gdbremote_testcase
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+
+
+class TestPartialResume(gdbremote_testcase.GdbRemoteTestCaseBase):
+    THREAD_MATCH_RE = re.compile(r"thread ([0-9a-f]+) running")
+
+    def start_vCont_run_subset_of_threads_test(self):
+        self.build()
+        self.set_inferior_startup_launch()
+
+        procs = self.prep_debug_monitor_and_inferior(inferior_args=["3"])
+        # grab the main thread id
+        self.add_threadinfo_collection_packets()
+        main_thread = self.parse_threadinfo_packets(
+            self.expect_gdbremote_sequence())
+        self.assertEqual(len(main_thread), 1)
+        self.reset_test_sequence()
+
+        # run until threads start, then grab full thread list
+        self.test_sequence.add_log_lines([
+            "read packet: $c#63",
+            {"direction": "send", "regex": "[$]T.*;reason:signal.*"},
+        ], True)
+        self.add_threadinfo_collection_packets()
+
+        all_threads = self.parse_threadinfo_packets(
+            self.expect_gdbremote_sequence())
+        self.assertEqual(len(all_threads), 4)
+        self.assertIn(main_thread[0], all_threads)
+        self.reset_test_sequence()
+
+        all_subthreads = set(all_threads) - set(main_thread)
+        self.assertEqual(len(all_subthreads), 3)
+
+        return (main_thread[0], list(all_subthreads))
+
+    def continue_and_get_threads_running(self, main_thread, vCont_req):
+        self.test_sequence.add_log_lines(
+            ["read packet: $vCont;c:{:x};{}#00".format(main_thread, vCont_req),
+             "send packet: $W00#00",
+             ], True)
+        exp = self.expect_gdbremote_sequence()
+        self.reset_test_sequence()
+        found = set()
+        for line in exp["O_content"].decode().splitlines():
+            m = self.THREAD_MATCH_RE.match(line)
+            if m is not None:
+                found.add(int(m.group(1), 16))
+        return found
+
+    @skipIfWindows
+    @add_test_categories(["llgs"])
+    def test_vCont_cxcx(self):
+        main_thread, all_subthreads_list = (
+            self.start_vCont_run_subset_of_threads_test())
+        # resume two threads explicitly, stop the third one implicitly
+        self.assertEqual(
+            self.continue_and_get_threads_running(
+                main_thread,
+                "c:{:x};c:{:x}".format(*all_subthreads_list[:2])),
+            set(all_subthreads_list[:2]))
+
+    @skipIfWindows
+    @add_test_categories(["llgs"])
+    def test_vCont_cxcxt(self):
+        main_thread, all_subthreads_list = (
+            self.start_vCont_run_subset_of_threads_test())
+        # resume two threads explicitly, stop others explicitly
+        self.assertEqual(
+            self.continue_and_get_threads_running(
+                main_thread,
+                "c:{:x};c:{:x};t".format(*all_subthreads_list[:2])),
+            set(all_subthreads_list[:2]))
+
+    @skipIfWindows
+    @add_test_categories(["llgs"])
+    def test_vCont_txc(self):
+        main_thread, all_subthreads_list = (
+            self.start_vCont_run_subset_of_threads_test())
+        # stop one thread explicitly, resume others
+        self.assertEqual(
+            self.continue_and_get_threads_running(
+                main_thread,
+                "t:{:x};c".format(all_subthreads_list[-1])),
+            set(all_subthreads_list[:2]))
+
+    @skipIfWindows
+    @add_test_categories(["llgs"])
+    def test_vCont_cxtxc(self):
+        main_thread, all_subthreads_list = (
+            self.start_vCont_run_subset_of_threads_test())
+        # resume one thread explicitly, stop one explicitly,
+        # resume others
+        self.assertEqual(
+            self.continue_and_get_threads_running(
+                main_thread,
+                "c:{:x};t:{:x};c".format(*all_subthreads_list[-2:])),
+            set(all_subthreads_list[:2]))
+
+    @skipIfWindows
+    @add_test_categories(["llgs"])
+    def test_vCont_txcx(self):
+        main_thread, all_subthreads_list = (
+            self.start_vCont_run_subset_of_threads_test())
+        # resume one thread explicitly, stop one explicitly,
+        # stop others implicitly
+        self.assertEqual(
+            self.continue_and_get_threads_running(
+                main_thread,
+                "t:{:x};c:{:x}".format(*all_subthreads_list[:2])),
+            set(all_subthreads_list[1:2]))
+
+    @skipIfWindows
+    @add_test_categories(["llgs"])
+    def test_vCont_txcxt(self):
+        main_thread, all_subthreads_list = (
+            self.start_vCont_run_subset_of_threads_test())
+        # resume one thread explicitly, stop one explicitly,
+        # stop others explicitly
+        self.assertEqual(
+            self.continue_and_get_threads_running(
+                main_thread,
+                "t:{:x};c:{:x};t".format(*all_subthreads_list[:2])),
+            set(all_subthreads_list[1:2]))
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to