--- /dev/null
+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]))
-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()
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()
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]))
#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) {
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();