diff options
author | dim <dim@FreeBSD.org> | 2016-01-06 20:12:03 +0000 |
---|---|---|
committer | dim <dim@FreeBSD.org> | 2016-01-06 20:12:03 +0000 |
commit | 78b9749c0a4ea980a8b934645da6ae98fcc665e8 (patch) | |
tree | dd2a1ddf0476664c2b823409c36cbccd52662ca7 /packages/Python/lldbsuite/test/functionalities/thread | |
parent | 60cb593f9d55fa5ca7a5372b731f2330345b4b9a (diff) | |
download | FreeBSD-src-78b9749c0a4ea980a8b934645da6ae98fcc665e8.zip FreeBSD-src-78b9749c0a4ea980a8b934645da6ae98fcc665e8.tar.gz |
Vendor import of lldb trunk r256945:
https://llvm.org/svn/llvm-project/lldb/trunk@256945
Diffstat (limited to 'packages/Python/lldbsuite/test/functionalities/thread')
49 files changed, 3414 insertions, 0 deletions
diff --git a/packages/Python/lldbsuite/test/functionalities/thread/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/Makefile new file mode 100644 index 0000000..644e297 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp +ENABLE_THREADS := YES +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/TestNumThreads.py b/packages/Python/lldbsuite/test/functionalities/thread/TestNumThreads.py new file mode 100644 index 0000000..8184ddc --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/TestNumThreads.py @@ -0,0 +1,53 @@ +""" +Test number of threads. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class NumberOfThreadsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break inside main(). + self.line = line_number('main.cpp', '// Set break point at this line.') + + def test(self): + """Test number of threads.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint with 1 location. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1) + + # The breakpoint list should show 3 locations. + self.expect("breakpoint list -f", "Breakpoint location shown correctly", + substrs = ["1: file = 'main.cpp', line = %d, exact_match = 0, locations = 1" % self.line]) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # Stopped once. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ["stop reason = breakpoint 1."]) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # Get the number of threads + num_threads = process.GetNumThreads() + + # Using std::thread may involve extra threads, so we assert that there are + # at least 4 rather than exactly 4. + self.assertTrue(num_threads >= 4, 'Number of expected threads and actual threads do not match.') diff --git a/packages/Python/lldbsuite/test/functionalities/thread/backtrace_all/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/backtrace_all/Makefile new file mode 100644 index 0000000..24e6801 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/backtrace_all/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +CXXFLAGS += -std=c++11 +CXX_SOURCES := ParallelTask.cpp +ENABLE_THREADS := YES +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/backtrace_all/ParallelTask.cpp b/packages/Python/lldbsuite/test/functionalities/thread/backtrace_all/ParallelTask.cpp new file mode 100755 index 0000000..71fb8e3 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/backtrace_all/ParallelTask.cpp @@ -0,0 +1,151 @@ +#include <cstdint> +#include <thread> +#include <vector> +#include <queue> +#include <future> +#include <iostream> +#include <cassert> + +class TaskPoolImpl +{ +public: + TaskPoolImpl(uint32_t num_threads) : + m_stop(false) + { + for (uint32_t i = 0; i < num_threads; ++i) + m_threads.emplace_back(Worker, this); + } + + ~TaskPoolImpl() + { + Stop(); + } + + template<typename F, typename... Args> + std::future<typename std::result_of<F(Args...)>::type> + AddTask(F&& f, Args&&... args) + { + auto task = std::make_shared<std::packaged_task<typename std::result_of<F(Args...)>::type()>>( + std::bind(std::forward<F>(f), std::forward<Args>(args)...)); + + std::unique_lock<std::mutex> lock(m_tasks_mutex); + assert(!m_stop && "Can't add task to TaskPool after it is stopped"); + m_tasks.emplace([task](){ (*task)(); }); + lock.unlock(); + m_tasks_cv.notify_one(); + + return task->get_future(); + } + + void + Stop() + { + std::unique_lock<std::mutex> lock(m_tasks_mutex); + m_stop = true; + m_tasks_mutex.unlock(); + m_tasks_cv.notify_all(); + for (auto& t : m_threads) + t.join(); + } + +private: + static void + Worker(TaskPoolImpl* pool) + { + while (true) + { + std::unique_lock<std::mutex> lock(pool->m_tasks_mutex); + if (pool->m_tasks.empty()) + pool->m_tasks_cv.wait(lock, [pool](){ return !pool->m_tasks.empty() || pool->m_stop; }); + if (pool->m_tasks.empty()) + break; + + std::function<void()> f = pool->m_tasks.front(); + pool->m_tasks.pop(); + lock.unlock(); + + f(); + } + } + + std::queue<std::function<void()>> m_tasks; + std::mutex m_tasks_mutex; + std::condition_variable m_tasks_cv; + bool m_stop; + std::vector<std::thread> m_threads; +}; + +class TaskPool +{ +public: + // Add a new task to the thread pool and return a std::future belongs for the newly created task. + // The caller of this function have to wait on the future for this task to complete. + template<typename F, typename... Args> + static std::future<typename std::result_of<F(Args...)>::type> + AddTask(F&& f, Args&&... args) + { + return GetImplementation().AddTask(std::forward<F>(f), std::forward<Args>(args)...); + } + + // Run all of the specified tasks on the thread pool and wait until all of them are finished + // before returning + template<typename... T> + static void + RunTasks(T&&... t) + { + RunTaskImpl<T...>::Run(std::forward<T>(t)...); + } + +private: + static TaskPoolImpl& + GetImplementation() + { + static TaskPoolImpl g_task_pool_impl(std::thread::hardware_concurrency()); + return g_task_pool_impl; + } + + template<typename... T> + struct RunTaskImpl; +}; + +template<typename H, typename... T> +struct TaskPool::RunTaskImpl<H, T...> +{ + static void + Run(H&& h, T&&... t) + { + auto f = AddTask(std::forward<H>(h)); + RunTaskImpl<T...>::Run(std::forward<T>(t)...); + f.wait(); + } +}; + +template<> +struct TaskPool::RunTaskImpl<> +{ + static void + Run() {} +}; + +int main() +{ + std::vector<std::future<uint32_t>> tasks; + for (int i = 0; i < 100000; ++i) + { + tasks.emplace_back(TaskPool::AddTask([](int i){ + uint32_t s = 0; + for (int j = 0; j <= i; ++j) + s += j; + return s; + }, + i)); + } + + for (auto& it : tasks) // Set breakpoint here + it.wait(); + + TaskPool::RunTasks( + []() { return 1; }, + []() { return "aaaa"; } + ); +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/backtrace_all/TestBacktraceAll.py b/packages/Python/lldbsuite/test/functionalities/thread/backtrace_all/TestBacktraceAll.py new file mode 100644 index 0000000..91bc685 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/backtrace_all/TestBacktraceAll.py @@ -0,0 +1,57 @@ +""" +Test regression for Bug 25251. +""" + +import os, time +import unittest2 +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class BreakpointAfterJoinTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number for our breakpoint. + self.breakpoint = line_number('ParallelTask.cpp', '// Set breakpoint here') + + @skipIfTargetAndroid(archs=["arm"]) # The android-arm compiler can't compile the inferior + # because of an issue around std::future. + # TODO: Change the test to don't depend on std::future<T> + def test(self): + """Test breakpoint handling after a thread join.""" + self.build(dictionary=self.getBuildFlags()) + + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint + lldbutil.run_break_set_by_file_and_line (self, "ParallelTask.cpp", self.breakpoint, num_expected_locations=-1) + + # The breakpoint list should show 1 location. + self.expect("breakpoint list -f", "Breakpoint location shown correctly", + substrs = ["1: file = 'ParallelTask.cpp', line = %d, exact_match = 0" % self.breakpoint]) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This should not result in a segmentation fault + self.expect("thread backtrace all", STOPPED_DUE_TO_BREAKPOINT, + substrs = ["stop reason = breakpoint 1."]) + + # Run to completion + self.runCmd("continue") + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() diff --git a/packages/Python/lldbsuite/test/functionalities/thread/break_after_join/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/break_after_join/Makefile new file mode 100644 index 0000000..67aa166 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/break_after_join/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +ENABLE_THREADS := YES +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/break_after_join/TestBreakAfterJoin.py b/packages/Python/lldbsuite/test/functionalities/thread/break_after_join/TestBreakAfterJoin.py new file mode 100644 index 0000000..43397a1 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/break_after_join/TestBreakAfterJoin.py @@ -0,0 +1,77 @@ +""" +Test number of threads. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class BreakpointAfterJoinTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number for our breakpoint. + self.breakpoint = line_number('main.cpp', '// Set breakpoint here') + + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureFreeBSD("llvm.org/pr18190") # thread states not properly maintained + @expectedFailureLinux("llvm.org/pr15824") # thread states not properly maintained + def test(self): + """Test breakpoint handling after a thread join.""" + self.build(dictionary=self.getBuildFlags()) + + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint in the main thread. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.breakpoint, num_expected_locations=1) + + # The breakpoint list should show 1 location. + self.expect("breakpoint list -f", "Breakpoint location shown correctly", + substrs = ["1: file = 'main.cpp', line = %d, exact_match = 0, locations = 1" % self.breakpoint]) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # The exit probably occurred during breakpoint handling, but it isn't + # guaranteed. The main thing we're testing here is that the debugger + # handles this cleanly is some way. + + # Get the number of threads + num_threads = process.GetNumThreads() + + # Make sure we see at least six threads + self.assertTrue(num_threads >= 6, 'Number of expected threads and actual threads do not match.') + + # Make sure all threads are stopped + for i in range(0, num_threads): + self.assertTrue(process.GetThreadAtIndex(i).IsStopped(), + "Thread {0} didn't stop during breakpoint.".format(i)) + + # Run to completion + self.runCmd("continue") + + # If the process hasn't exited, collect some information + if process.GetState() != lldb.eStateExited: + self.runCmd("thread list") + self.runCmd("process status") + + # At this point, the inferior process should have exited. + self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED) diff --git a/packages/Python/lldbsuite/test/functionalities/thread/break_after_join/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/break_after_join/main.cpp new file mode 100644 index 0000000..a630795 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/break_after_join/main.cpp @@ -0,0 +1,118 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This test is intended to create a situation in which one thread will exit +// while a breakpoint is being handled in another thread. This may not always +// happen because it's possible that the exiting thread will exit before the +// breakpoint is hit. The test case should be flexible enough to treat that +// as success. + +#include <atomic> +#include <chrono> +#include <thread> + +volatile int g_test = 0; + +// Note that although hogging the CPU while waiting for a variable to change +// would be terrible in production code, it's great for testing since it +// avoids a lot of messy context switching to get multiple threads synchronized. +#define do_nothing() + +#define pseudo_barrier_wait(bar) \ + --bar; \ + while (bar > 0) \ + do_nothing(); + +#define pseudo_barrier_init(bar, count) (bar = count) + +// A barrier to synchronize all the threads. +std::atomic_int g_barrier1; + +// A barrier to keep the threads from exiting until after the breakpoint has +// been passed. +std::atomic_int g_barrier2; + +void * +break_thread_func () +{ + // Wait until all the threads are running + pseudo_barrier_wait(g_barrier1); + + // Wait for the join thread to join + std::this_thread::sleep_for(std::chrono::microseconds(50)); + + // Do something + g_test++; // Set breakpoint here + + // Synchronize after the breakpoint + pseudo_barrier_wait(g_barrier2); + + // Return + return NULL; +} + +void * +wait_thread_func () +{ + // Wait until the entire first group of threads is running + pseudo_barrier_wait(g_barrier1); + + // Wait until the breakpoint has been passed + pseudo_barrier_wait(g_barrier2); + + // Return + return NULL; +} + +void * +join_thread_func (void *input) +{ + std::thread *thread_to_join = (std::thread *)input; + + // Sync up with the rest of the threads. + pseudo_barrier_wait(g_barrier1); + + // Join the other thread + thread_to_join->join(); + + // Return + return NULL; +} + +int main () +{ + // The first barrier waits for the non-joining threads to start. + // This thread will also participate in that barrier. + // The idea here is to guarantee that the joining thread will be + // last in the internal list maintained by the debugger. + pseudo_barrier_init(g_barrier1, 5); + + // The second barrier keeps the waiting threads around until the breakpoint + // has been passed. + pseudo_barrier_init(g_barrier2, 4); + + // Create a thread to hit the breakpoint + std::thread thread_1(break_thread_func); + + // Create more threads to slow the debugger down during processing. + std::thread thread_2(wait_thread_func); + std::thread thread_3(wait_thread_func); + std::thread thread_4(wait_thread_func); + + // Create a thread to join the breakpoint thread + std::thread thread_5(join_thread_func, &thread_1); + + // Wait for the threads to finish + thread_5.join(); // implies thread_1 is already finished + thread_4.join(); + thread_3.join(); + thread_2.join(); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/Makefile new file mode 100644 index 0000000..469c080 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +ENABLE_THREADS := YES + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentEvents.py b/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentEvents.py new file mode 100644 index 0000000..9eb25b6 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/TestConcurrentEvents.py @@ -0,0 +1,491 @@ +""" +A stress-test of sorts for LLDB's handling of threads in the inferior. + +This test sets a breakpoint in the main thread where test parameters (numbers of +threads) can be adjusted, runs the inferior to that point, and modifies the +locals that control the event thread counts. This test also sets a breakpoint in +breakpoint_func (the function executed by each 'breakpoint' thread) and a +watchpoint on a global modified in watchpoint_func. The inferior is continued +until exit or a crash takes place, and the number of events seen by LLDB is +verified to match the expected number of events. +""" + +from __future__ import print_function + + + +import unittest2 +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +@skipIfWindows +@expectedFailureAll(archs=['mips64', 'mips64el']) # Atomic sequences are not supported yet for MIPS in LLDB. +class ConcurrentEventsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + # + ## Tests for multiple threads that generate a single event. + # + @unittest2.skipIf(TestBase.skipLongRunningTest(), "Skip this long running test") + def test_many_breakpoints(self): + """Test 100 breakpoints from 100 threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=100) + + @unittest2.skipIf(TestBase.skipLongRunningTest(), "Skip this long running test") + def test_many_watchpoints(self): + """Test 100 watchpoints from 100 threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_watchpoint_threads=100) + + @unittest2.skipIf(TestBase.skipLongRunningTest(), "Skip this long running test") + def test_many_signals(self): + """Test 100 signals from 100 threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_signal_threads=100) + + @unittest2.skipIf(TestBase.skipLongRunningTest(), "Skip this long running test") + def test_many_crash(self): + """Test 100 threads that cause a segfault.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_crash_threads=100) + + + # + ## Tests for concurrent signal and breakpoint + # + @skipIfFreeBSD # timing out on buildbot + def test_signal_break(self): + """Test signal and a breakpoint in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=1, num_signal_threads=1) + + @skipIfFreeBSD # timing out on buildbot + def test_delay_signal_break(self): + """Test (1-second delay) signal and a breakpoint in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=1, num_delay_signal_threads=1) + + @skipIfFreeBSD # timing out on buildbot + def test_signal_delay_break(self): + """Test signal and a (1 second delay) breakpoint in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_delay_breakpoint_threads=1, num_signal_threads=1) + + + # + ## Tests for concurrent watchpoint and breakpoint + # + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_watch_break(self): + """Test watchpoint and a breakpoint in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=1, num_watchpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_delay_watch_break(self): + """Test (1-second delay) watchpoint and a breakpoint in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=1, num_delay_watchpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_watch_break_delay(self): + """Test watchpoint and a (1 second delay) breakpoint in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_delay_breakpoint_threads=1, num_watchpoint_threads=1) + + # + ## Tests for concurrent signal and watchpoint + # + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_signal_watch(self): + """Test a watchpoint and a signal in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_signal_threads=1, num_watchpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_delay_signal_watch(self): + """Test a watchpoint and a (1 second delay) signal in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_delay_signal_threads=1, num_watchpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + @expectedFailureAll("llvm.org/pr16714", oslist=["linux"], archs=["i386"]) + def test_signal_delay_watch(self): + """Test a (1 second delay) watchpoint and a signal in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_signal_threads=1, num_delay_watchpoint_threads=1) + + + # + ## Tests for multiple breakpoint threads + # + @skipIfFreeBSD # timing out on buildbot + def test_two_breakpoint_threads(self): + """Test two threads that trigger a breakpoint. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=2) + + @skipIfFreeBSD # timing out on buildbot + def test_breakpoint_one_delay_breakpoint_threads(self): + """Test threads that trigger a breakpoint where one thread has a 1 second delay. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=1, + num_delay_breakpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + def test_two_breakpoints_one_signal(self): + """Test two threads that trigger a breakpoint and one signal thread. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=2, num_signal_threads=1) + + @skipIfFreeBSD # timing out on buildbot + def test_breakpoint_delay_breakpoint_one_signal(self): + """Test two threads that trigger a breakpoint (one with a 1 second delay) and one signal thread. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=1, + num_delay_breakpoint_threads=1, + num_signal_threads=1) + + @skipIfFreeBSD # timing out on buildbot + def test_two_breakpoints_one_delay_signal(self): + """Test two threads that trigger a breakpoint and one (1 second delay) signal thread. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=2, num_delay_signal_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_two_breakpoints_one_watchpoint(self): + """Test two threads that trigger a breakpoint and one watchpoint thread. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=2, num_watchpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_breakpoints_delayed_breakpoint_one_watchpoint(self): + """Test a breakpoint, a delayed breakpoint, and one watchpoint thread. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_breakpoint_threads=1, + num_delay_breakpoint_threads=1, + num_watchpoint_threads=1) + + # + ## Tests for multiple watchpoint threads + # + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_two_watchpoint_threads(self): + """Test two threads that trigger a watchpoint. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_watchpoint_threads=2) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_watchpoint_with_delay_watchpoint_threads(self): + """Test two threads that trigger a watchpoint where one thread has a 1 second delay. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_watchpoint_threads=1, + num_delay_watchpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_two_watchpoints_one_breakpoint(self): + """Test two threads that trigger a watchpoint and one breakpoint thread. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_watchpoint_threads=2, num_breakpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_two_watchpoints_one_delay_breakpoint(self): + """Test two threads that trigger a watchpoint and one (1 second delay) breakpoint thread. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_watchpoint_threads=2, num_delay_breakpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_watchpoint_delay_watchpoint_one_breakpoint(self): + """Test two threads that trigger a watchpoint (one with a 1 second delay) and one breakpoint thread. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_watchpoint_threads=1, + num_delay_watchpoint_threads=1, + num_breakpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_two_watchpoints_one_signal(self): + """Test two threads that trigger a watchpoint and one signal thread. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_watchpoint_threads=2, num_signal_threads=1) + + # + ## Test for watchpoint, signal and breakpoint happening concurrently + # + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_signal_watch_break(self): + """Test a signal/watchpoint/breakpoint in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_signal_threads=1, + num_watchpoint_threads=1, + num_breakpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_signal_watch_break(self): + """Test one signal thread with 5 watchpoint and breakpoint threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_signal_threads=1, + num_watchpoint_threads=5, + num_breakpoint_threads=5) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_signal_watch_break(self): + """Test with 5 watchpoint and breakpoint threads.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_watchpoint_threads=5, + num_breakpoint_threads=5) + + + # + ## Test for crashing threads happening concurrently with other events + # + @skipIfFreeBSD # timing out on buildbot + def test_crash_with_break(self): + """ Test a thread that crashes while another thread hits a breakpoint.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_crash_threads=1, num_breakpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_crash_with_watchpoint(self): + """ Test a thread that crashes while another thread hits a watchpoint.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_crash_threads=1, num_watchpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + def test_crash_with_signal(self): + """ Test a thread that crashes while another thread generates a signal.""" + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_crash_threads=1, num_signal_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_crash_with_watchpoint_breakpoint_signal(self): + """ Test a thread that crashes while other threads generate a signal and hit a watchpoint and breakpoint. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_crash_threads=1, + num_breakpoint_threads=1, + num_signal_threads=1, + num_watchpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + @skipIfRemoteDueToDeadlock + def test_delayed_crash_with_breakpoint_watchpoint(self): + """ Test a thread with a delayed crash while other threads hit a watchpoint and a breakpoint. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_delay_crash_threads=1, + num_breakpoint_threads=1, + num_watchpoint_threads=1) + + @skipIfFreeBSD # timing out on buildbot + def test_delayed_crash_with_breakpoint_signal(self): + """ Test a thread with a delayed crash while other threads generate a signal and hit a breakpoint. """ + self.build(dictionary=self.getBuildFlags()) + self.do_thread_actions(num_delay_crash_threads=1, + num_breakpoint_threads=1, + num_signal_threads=1) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number for our breakpoint. + self.filename = 'main.cpp' + self.thread_breakpoint_line = line_number(self.filename, '// Set breakpoint here') + self.setup_breakpoint_line = line_number(self.filename, '// Break here and adjust num') + self.finish_breakpoint_line = line_number(self.filename, '// Break here and verify one thread is active') + + def describe_threads(self): + ret = [] + for x in self.inferior_process: + id = x.GetIndexID() + reason = x.GetStopReason() + status = "stopped" if x.IsStopped() else "running" + reason_str = lldbutil.stop_reason_to_str(reason) + if reason == lldb.eStopReasonBreakpoint: + bpid = x.GetStopReasonDataAtIndex(0) + bp = self.inferior_target.FindBreakpointByID(bpid) + reason_str = "%s hit %d times" % (lldbutil.get_description(bp), bp.GetHitCount()) + elif reason == lldb.eStopReasonWatchpoint: + watchid = x.GetStopReasonDataAtIndex(0) + watch = self.inferior_target.FindWatchpointByID(watchid) + reason_str = "%s hit %d times" % (lldbutil.get_description(watch), watch.GetHitCount()) + elif reason == lldb.eStopReasonSignal: + signals = self.inferior_process.GetUnixSignals() + signal_name = signals.GetSignalAsCString(x.GetStopReasonDataAtIndex(0)) + reason_str = "signal %s" % signal_name + + location = "\t".join([lldbutil.get_description(x.GetFrameAtIndex(i)) for i in range(x.GetNumFrames())]) + ret.append("thread %d %s due to %s at\n\t%s" % (id, status, reason_str, location)) + return ret + + def add_breakpoint(self, line, descriptions): + """ Adds a breakpoint at self.filename:line and appends its description to descriptions, and + returns the LLDB SBBreakpoint object. + """ + + bpno = lldbutil.run_break_set_by_file_and_line(self, self.filename, line, num_expected_locations=-1) + bp = self.inferior_target.FindBreakpointByID(bpno) + descriptions.append(": file = 'main.cpp', line = %d" % self.finish_breakpoint_line) + return bp + + def inferior_done(self): + """ Returns true if the inferior is done executing all the event threads (and is stopped at self.finish_breakpoint, + or has terminated execution. + """ + return self.finish_breakpoint.GetHitCount() > 0 or \ + self.crash_count > 0 or \ + self.inferior_process.GetState() == lldb.eStateExited + + def count_signaled_threads(self): + count = 0 + for thread in self.inferior_process: + if thread.GetStopReason() == lldb.eStopReasonSignal and thread.GetStopReasonDataAtIndex(0) == self.inferior_process.GetUnixSignals().GetSignalNumberFromName('SIGUSR1'): + count += 1 + return count + + def do_thread_actions(self, + num_breakpoint_threads = 0, + num_signal_threads = 0, + num_watchpoint_threads = 0, + num_crash_threads = 0, + num_delay_breakpoint_threads = 0, + num_delay_signal_threads = 0, + num_delay_watchpoint_threads = 0, + num_delay_crash_threads = 0): + """ Sets a breakpoint in the main thread where test parameters (numbers of threads) can be adjusted, runs the inferior + to that point, and modifies the locals that control the event thread counts. Also sets a breakpoint in + breakpoint_func (the function executed by each 'breakpoint' thread) and a watchpoint on a global modified in + watchpoint_func. The inferior is continued until exit or a crash takes place, and the number of events seen by LLDB + is verified to match the expected number of events. + """ + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Get the target + self.inferior_target = self.dbg.GetSelectedTarget() + + expected_bps = [] + + # Initialize all the breakpoints (main thread/aux thread) + self.setup_breakpoint = self.add_breakpoint(self.setup_breakpoint_line, expected_bps) + self.finish_breakpoint = self.add_breakpoint(self.finish_breakpoint_line, expected_bps) + + # Set the thread breakpoint + if num_breakpoint_threads + num_delay_breakpoint_threads > 0: + self.thread_breakpoint = self.add_breakpoint(self.thread_breakpoint_line, expected_bps) + + # Verify breakpoints + self.expect("breakpoint list -f", "Breakpoint locations shown correctly", substrs = expected_bps) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # Check we are at line self.setup_breakpoint + self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT, + substrs = ["stop reason = breakpoint 1."]) + + # Initialize the (single) watchpoint on the global variable (g_watchme) + if num_watchpoint_threads + num_delay_watchpoint_threads > 0: + self.runCmd("watchpoint set variable g_watchme") + for w in self.inferior_target.watchpoint_iter(): + self.thread_watchpoint = w + self.assertTrue("g_watchme" in str(self.thread_watchpoint), "Watchpoint location not shown correctly") + + # Get the process + self.inferior_process = self.inferior_target.GetProcess() + + # We should be stopped at the setup site where we can set the number of + # threads doing each action (break/crash/signal/watch) + self.assertEqual(self.inferior_process.GetNumThreads(), 1, 'Expected to stop before any additional threads are spawned.') + + self.runCmd("expr num_breakpoint_threads=%d" % num_breakpoint_threads) + self.runCmd("expr num_crash_threads=%d" % num_crash_threads) + self.runCmd("expr num_signal_threads=%d" % num_signal_threads) + self.runCmd("expr num_watchpoint_threads=%d" % num_watchpoint_threads) + + self.runCmd("expr num_delay_breakpoint_threads=%d" % num_delay_breakpoint_threads) + self.runCmd("expr num_delay_crash_threads=%d" % num_delay_crash_threads) + self.runCmd("expr num_delay_signal_threads=%d" % num_delay_signal_threads) + self.runCmd("expr num_delay_watchpoint_threads=%d" % num_delay_watchpoint_threads) + + # Continue the inferior so threads are spawned + self.runCmd("continue") + + # Make sure we see all the threads. The inferior program's threads all synchronize with a pseudo-barrier; that is, + # the inferior program ensures all threads are started and running before any thread triggers its 'event'. + num_threads = self.inferior_process.GetNumThreads() + expected_num_threads = num_breakpoint_threads + num_delay_breakpoint_threads \ + + num_signal_threads + num_delay_signal_threads \ + + num_watchpoint_threads + num_delay_watchpoint_threads \ + + num_crash_threads + num_delay_crash_threads + 1 + self.assertEqual(num_threads, expected_num_threads, + 'Expected to see %d threads, but seeing %d. Details:\n%s' % (expected_num_threads, + num_threads, + "\n\t".join(self.describe_threads()))) + + self.signal_count = self.count_signaled_threads() + self.crash_count = len(lldbutil.get_crashed_threads(self, self.inferior_process)) + + # Run to completion (or crash) + while not self.inferior_done(): + if self.TraceOn(): + self.runCmd("thread backtrace all") + self.runCmd("continue") + self.signal_count += self.count_signaled_threads() + self.crash_count += len(lldbutil.get_crashed_threads(self, self.inferior_process)) + + if num_crash_threads > 0 or num_delay_crash_threads > 0: + # Expecting a crash + self.assertTrue(self.crash_count > 0, + "Expecting at least one thread to crash. Details: %s" % "\t\n".join(self.describe_threads())) + + # Ensure the zombie process is reaped + self.runCmd("process kill") + + elif num_crash_threads == 0 and num_delay_crash_threads == 0: + # There should be a single active thread (the main one) which hit the breakpoint after joining + self.assertEqual(1, self.finish_breakpoint.GetHitCount(), "Expected main thread (finish) breakpoint to be hit once") + + num_threads = self.inferior_process.GetNumThreads() + self.assertEqual(1, num_threads, "Expecting 1 thread but seeing %d. Details:%s" % (num_threads, + "\n\t".join(self.describe_threads()))) + self.runCmd("continue") + + # The inferior process should have exited without crashing + self.assertEqual(0, self.crash_count, "Unexpected thread(s) in crashed state") + self.assertEqual(self.inferior_process.GetState(), lldb.eStateExited, PROCESS_EXITED) + + # Verify the number of actions took place matches expected numbers + expected_breakpoint_threads = num_delay_breakpoint_threads + num_breakpoint_threads + breakpoint_hit_count = self.thread_breakpoint.GetHitCount() if expected_breakpoint_threads > 0 else 0 + self.assertEqual(expected_breakpoint_threads, breakpoint_hit_count, + "Expected %d breakpoint hits, but got %d" % (expected_breakpoint_threads, breakpoint_hit_count)) + + expected_signal_threads = num_delay_signal_threads + num_signal_threads + self.assertEqual(expected_signal_threads, self.signal_count, + "Expected %d stops due to signal delivery, but got %d" % (expected_signal_threads, self.signal_count)) + + expected_watchpoint_threads = num_delay_watchpoint_threads + num_watchpoint_threads + watchpoint_hit_count = self.thread_watchpoint.GetHitCount() if expected_watchpoint_threads > 0 else 0 + self.assertEqual(expected_watchpoint_threads, watchpoint_hit_count, + "Expected %d watchpoint hits, got %d" % (expected_watchpoint_threads, watchpoint_hit_count)) diff --git a/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/main.cpp new file mode 100644 index 0000000..ac2535c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/concurrent_events/main.cpp @@ -0,0 +1,200 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This test is intended to create a situation in which multiple events +// (breakpoints, watchpoints, crashes, and signal generation/delivery) happen +// from multiple threads. The test expects the debugger to set a breakpoint on +// the main thread (before any worker threads are spawned) and modify variables +// which control the number of threads that are spawned for each action. + +#include <atomic> +#include <vector> +using namespace std; + +#include <pthread.h> + +#include <signal.h> +#include <sys/types.h> +#include <unistd.h> + +// Note that although hogging the CPU while waiting for a variable to change +// would be terrible in production code, it's great for testing since it +// avoids a lot of messy context switching to get multiple threads synchronized. +#define do_nothing() + +#define pseudo_barrier_wait(bar) \ + --bar; \ + while (bar > 0) \ + do_nothing(); + +#define pseudo_barrier_init(bar, count) (bar = count) + +typedef std::vector<std::pair<unsigned, void*(*)(void*)> > action_counts; +typedef std::vector<pthread_t> thread_vector; + +std::atomic_int g_barrier; +int g_breakpoint = 0; +int g_sigusr1_count = 0; +std::atomic_int g_watchme; + +struct action_args { + int delay; +}; + +// Perform any extra actions required by thread 'input' arg +void do_action_args(void *input) { + if (input) { + action_args *args = static_cast<action_args*>(input); + sleep(args->delay); + } +} + +void * +breakpoint_func (void *input) +{ + // Wait until all threads are running + pseudo_barrier_wait(g_barrier); + do_action_args(input); + + // Do something + g_breakpoint++; // Set breakpoint here + return 0; +} + +void * +signal_func (void *input) { + // Wait until all threads are running + pseudo_barrier_wait(g_barrier); + do_action_args(input); + + // Send a user-defined signal to the current process + //kill(getpid(), SIGUSR1); + // Send a user-defined signal to the current thread + pthread_kill(pthread_self(), SIGUSR1); + + return 0; +} + +void * +watchpoint_func (void *input) { + pseudo_barrier_wait(g_barrier); + do_action_args(input); + + g_watchme += 1; // watchpoint triggers here + return 0; +} + +void * +crash_func (void *input) { + pseudo_barrier_wait(g_barrier); + do_action_args(input); + + int *a = 0; + *a = 5; // crash happens here + return 0; +} + +void sigusr1_handler(int sig) { + if (sig == SIGUSR1) + g_sigusr1_count += 1; // Break here in signal handler +} + +/// Register a simple function for to handle signal +void register_signal_handler(int signal, void (*handler)(int)) +{ + sigset_t empty_sigset; + sigemptyset(&empty_sigset); + + struct sigaction action; + action.sa_sigaction = 0; + action.sa_mask = empty_sigset; + action.sa_flags = 0; + action.sa_handler = handler; + sigaction(SIGUSR1, &action, 0); +} + +void start_threads(thread_vector& threads, + action_counts& actions, + void* args = 0) { + action_counts::iterator b = actions.begin(), e = actions.end(); + for(action_counts::iterator i = b; i != e; ++i) { + for(unsigned count = 0; count < i->first; ++count) { + pthread_t t; + pthread_create(&t, 0, i->second, args); + threads.push_back(t); + } + } +} + +int dotest() +{ + g_watchme = 0; + + // Actions are triggered immediately after the thread is spawned + unsigned num_breakpoint_threads = 1; + unsigned num_watchpoint_threads = 0; + unsigned num_signal_threads = 1; + unsigned num_crash_threads = 0; + + // Actions below are triggered after a 1-second delay + unsigned num_delay_breakpoint_threads = 0; + unsigned num_delay_watchpoint_threads = 0; + unsigned num_delay_signal_threads = 0; + unsigned num_delay_crash_threads = 0; + + register_signal_handler(SIGUSR1, sigusr1_handler); // Break here and adjust num_[breakpoint|watchpoint|signal|crash]_threads + + unsigned total_threads = num_breakpoint_threads \ + + num_watchpoint_threads \ + + num_signal_threads \ + + num_crash_threads \ + + num_delay_breakpoint_threads \ + + num_delay_watchpoint_threads \ + + num_delay_signal_threads \ + + num_delay_crash_threads; + + // Don't let either thread do anything until they're both ready. + pseudo_barrier_init(g_barrier, total_threads); + + action_counts actions; + actions.push_back(std::make_pair(num_breakpoint_threads, breakpoint_func)); + actions.push_back(std::make_pair(num_watchpoint_threads, watchpoint_func)); + actions.push_back(std::make_pair(num_signal_threads, signal_func)); + actions.push_back(std::make_pair(num_crash_threads, crash_func)); + + action_counts delay_actions; + delay_actions.push_back(std::make_pair(num_delay_breakpoint_threads, breakpoint_func)); + delay_actions.push_back(std::make_pair(num_delay_watchpoint_threads, watchpoint_func)); + delay_actions.push_back(std::make_pair(num_delay_signal_threads, signal_func)); + delay_actions.push_back(std::make_pair(num_delay_crash_threads, crash_func)); + + // Create threads that handle instant actions + thread_vector threads; + start_threads(threads, actions); + + // Create threads that handle delayed actions + action_args delay_arg; + delay_arg.delay = 1; + start_threads(threads, delay_actions, &delay_arg); + + // Join all threads + typedef std::vector<pthread_t>::iterator thread_iterator; + for(thread_iterator t = threads.begin(); t != threads.end(); ++t) + pthread_join(*t, 0); + + return 0; +} + +int main () +{ + dotest(); + return 0; // Break here and verify one thread is active. +} + + diff --git a/packages/Python/lldbsuite/test/functionalities/thread/crash_during_step/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/crash_during_step/Makefile new file mode 100644 index 0000000..26db481 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/crash_during_step/Makefile @@ -0,0 +1,4 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/crash_during_step/TestCrashDuringStep.py b/packages/Python/lldbsuite/test/functionalities/thread/crash_during_step/TestCrashDuringStep.py new file mode 100644 index 0000000..24b5bf0 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/crash_during_step/TestCrashDuringStep.py @@ -0,0 +1,53 @@ +""" +Test that step-inst over a crash behaves correctly. +""" + +from __future__ import print_function + + + +import os +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class CreateDuringStepTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + TestBase.setUp(self) + self.breakpoint = line_number('main.cpp', '// Set breakpoint here') + + @expectedFailureWindows("llvm.org/pr24778") + @expectedFailureAndroid("llvm.org/pr24497", archs=['arm', 'aarch64']) + @expectedFailureAll(archs=['mips', 'mipsel', 'mips64', 'mips64el']) # IO error due to breakpoint at invalid address + def test_step_inst_with(self): + """Test thread creation during step-inst handling.""" + self.build(dictionary=self.getBuildFlags()) + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target and target.IsValid(), "Target is valid") + + self.bp_num = lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.breakpoint, num_expected_locations=1) + + # Run the program. + process = target.LaunchSimple(None, None, self.get_process_working_directory()) + self.assertTrue(process and process.IsValid(), PROCESS_IS_VALID) + + # The stop reason should be breakpoint. + self.assertEqual(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) + self.assertEqual(lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint).IsValid(), 1, + STOPPED_DUE_TO_BREAKPOINT) + + thread = process.GetThreadAtIndex(0) + self.assertTrue(thread and thread.IsValid(), "Thread is valid") + + # Keep stepping until the inferior crashes + while process.GetState() == lldb.eStateStopped and not lldbutil.is_thread_crashed(self, thread): + thread.StepInstruction(False) + + self.assertEqual(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) + self.assertTrue(lldbutil.is_thread_crashed(self, thread), "Thread has crashed") + process.Kill() diff --git a/packages/Python/lldbsuite/test/functionalities/thread/crash_during_step/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/crash_during_step/main.cpp new file mode 100644 index 0000000..02f3ce3 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/crash_during_step/main.cpp @@ -0,0 +1,16 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +void (*crash)() = nullptr; + +int main() +{ + crash(); // Set breakpoint here + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/create_after_attach/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/create_after_attach/Makefile new file mode 100644 index 0000000..67aa166 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/create_after_attach/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +ENABLE_THREADS := YES +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/create_after_attach/TestCreateAfterAttach.py b/packages/Python/lldbsuite/test/functionalities/thread/create_after_attach/TestCreateAfterAttach.py new file mode 100644 index 0000000..9772543 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/create_after_attach/TestCreateAfterAttach.py @@ -0,0 +1,122 @@ +""" +Test thread creation after process attach. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class CreateAfterAttachTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfFreeBSD # Hangs. May be the same as Linux issue llvm.org/pr16229 but + # not yet investigated. Revisit once required functionality + # is implemented for FreeBSD. + @skipIfWindows # Occasionally hangs on Windows, may be same as other issues. + @skipIfiOSSimulator + def test_create_after_attach_with_popen(self): + """Test thread creation after process attach.""" + self.build(dictionary=self.getBuildFlags(use_cpp11=False)) + self.create_after_attach(use_fork=False) + + @skipIfFreeBSD # Hangs. Revisit once required functionality is implemented + # for FreeBSD. + @skipIfRemote + @skipIfWindows # Windows doesn't have fork. + @expectedFlakeyLinux("llvm.org/pr16229") # 1/100 dosep, build 3546, clang-3.5 x84_64 + @skipIfiOSSimulator + def test_create_after_attach_with_fork(self): + """Test thread creation after process attach.""" + self.build(dictionary=self.getBuildFlags(use_cpp11=False)) + self.create_after_attach(use_fork=True) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers for our breakpoints. + self.break_1 = line_number('main.cpp', '// Set first breakpoint here') + self.break_2 = line_number('main.cpp', '// Set second breakpoint here') + self.break_3 = line_number('main.cpp', '// Set third breakpoint here') + + def create_after_attach(self, use_fork): + """Test thread creation after process attach.""" + + exe = os.path.join(os.getcwd(), "a.out") + + # Spawn a new process + if use_fork: + pid = self.forkSubprocess(exe) + else: + popen = self.spawnSubprocess(exe) + pid = popen.pid + self.addTearDownHook(self.cleanupSubprocesses) + + # Attach to the spawned process + self.runCmd("process attach -p " + str(pid)) + + target = self.dbg.GetSelectedTarget() + + process = target.GetProcess() + self.assertTrue(process, PROCESS_IS_VALID) + + # This should create a breakpoint in the main thread. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_1, num_expected_locations=1) + + # This should create a breakpoint in the second child thread. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_2, num_expected_locations=1) + + # This should create a breakpoint in the first child thread. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_3, num_expected_locations=1) + + # Note: With std::thread, we cannot rely on particular thread numbers. Using + # std::thread may cause the program to spin up a thread pool (and it does on + # Windows), so the thread numbers are non-deterministic. + + # Run to the first breakpoint + self.runCmd("continue") + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + '* thread #', + 'main', + 'stop reason = breakpoint']) + + # Change a variable to escape the loop + self.runCmd("expression main_thread_continue = 1") + + # Run to the second breakpoint + self.runCmd("continue") + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + '* thread #', + 'thread_2_func', + 'stop reason = breakpoint']) + + # Change a variable to escape the loop + self.runCmd("expression child_thread_continue = 1") + + # Run to the third breakpoint + self.runCmd("continue") + + # The stop reason of the thread should be breakpoint. + # Thread 3 may or may not have already exited. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + '* thread #', + 'thread_1_func', + 'stop reason = breakpoint']) + + # Run to completion + self.runCmd("continue") + + # At this point, the inferior process should have exited. + self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED) diff --git a/packages/Python/lldbsuite/test/functionalities/thread/create_after_attach/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/create_after_attach/main.cpp new file mode 100644 index 0000000..8434458 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/create_after_attach/main.cpp @@ -0,0 +1,78 @@ +#include <stdio.h> +#include <chrono> +#include <thread> + +using std::chrono::microseconds; + +#if defined(__linux__) +#include <sys/prctl.h> +#endif + +volatile int g_thread_2_continuing = 0; + +void * +thread_1_func (void *input) +{ + // Waiting to be released by the debugger. + while (!g_thread_2_continuing) // Another thread will change this value + { + std::this_thread::sleep_for(microseconds(1)); + } + + // Return + return NULL; // Set third breakpoint here +} + +void * +thread_2_func (void *input) +{ + // Waiting to be released by the debugger. + int child_thread_continue = 0; + while (!child_thread_continue) // The debugger will change this value + { + std::this_thread::sleep_for(microseconds(1)); // Set second breakpoint here + } + + // Release thread 1 + g_thread_2_continuing = 1; + + // Return + return NULL; +} + +int main(int argc, char const *argv[]) +{ +#if defined(__linux__) + // Immediately enable any ptracer so that we can allow the stub attach + // operation to succeed. Some Linux kernels are locked down so that + // only an ancestor process can be a ptracer of a process. This disables that + // restriction. Without it, attach-related stub tests will fail. +#if defined(PR_SET_PTRACER) && defined(PR_SET_PTRACER_ANY) + int prctl_result; + + // For now we execute on best effort basis. If this fails for + // some reason, so be it. + prctl_result = prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0); + (void) prctl_result; +#endif +#endif + + // Create a new thread + std::thread thread_1(thread_1_func, nullptr); + + // Waiting to be attached by the debugger. + int main_thread_continue = 0; + while (!main_thread_continue) // The debugger will change this value + { + std::this_thread::sleep_for(microseconds(1)); // Set first breakpoint here + } + + // Create another new thread + std::thread thread_2(thread_2_func, nullptr); + + // Wait for the threads to finish. + thread_1.join(); + thread_2.join(); + + printf("Exiting now\n"); +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/create_during_step/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/create_during_step/Makefile new file mode 100644 index 0000000..67aa166 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/create_during_step/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +ENABLE_THREADS := YES +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/create_during_step/TestCreateDuringStep.py b/packages/Python/lldbsuite/test/functionalities/thread/create_during_step/TestCreateDuringStep.py new file mode 100644 index 0000000..046a865 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/create_during_step/TestCreateDuringStep.py @@ -0,0 +1,127 @@ +""" +Test number of threads. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class CreateDuringStepTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureFreeBSD("llvm.org/pr18190") # thread states not properly maintained + @expectedFailureLinux("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureWindows("llvm.org/pr24668") # Breakpoints not resolved correctly + def test_step_inst(self): + """Test thread creation during step-inst handling.""" + self.build(dictionary=self.getBuildFlags()) + self.create_during_step_base("thread step-inst -m all-threads", 'stop reason = instruction step') + + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureFreeBSD("llvm.org/pr18190") # thread states not properly maintained + @expectedFailureLinux("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureWindows("llvm.org/pr24668") # Breakpoints not resolved correctly + def test_step_over(self): + """Test thread creation during step-over handling.""" + self.build(dictionary=self.getBuildFlags()) + self.create_during_step_base("thread step-over -m all-threads", 'stop reason = step over') + + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureFreeBSD("llvm.org/pr18190") # thread states not properly maintained + @expectedFailureLinux("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureWindows("llvm.org/pr24668") # Breakpoints not resolved correctly + def test_step_in(self): + """Test thread creation during step-in handling.""" + self.build(dictionary=self.getBuildFlags()) + self.create_during_step_base("thread step-in -m all-threads", 'stop reason = step in') + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break and continue. + self.breakpoint = line_number('main.cpp', '// Set breakpoint here') + self.continuepoint = line_number('main.cpp', '// Continue from here') + + def create_during_step_base(self, step_cmd, step_stop_reason): + """Test thread creation while using step-in.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint in the stepping thread. + self.bp_num = lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.breakpoint, num_expected_locations=1) + + # The breakpoint list should show 1 location. + self.expect("breakpoint list -f", "Breakpoint location shown correctly", + substrs = ["1: file = 'main.cpp', line = %d, locations = 1" % self.breakpoint]) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # Get the number of threads + num_threads = process.GetNumThreads() + + # Make sure we see only two threads + self.assertTrue(num_threads == 2, 'Number of expected threads and actual threads do not match.') + + # Get the thread objects + thread1 = process.GetThreadAtIndex(0) + thread2 = process.GetThreadAtIndex(1) + + # Make sure both threads are stopped + self.assertTrue(thread1.IsStopped(), "Thread 1 didn't stop during breakpoint") + self.assertTrue(thread2.IsStopped(), "Thread 2 didn't stop during breakpoint") + + # Find the thread that is stopped at the breakpoint + stepping_thread = None + for thread in process: + expected_bp_desc = "breakpoint %s." % self.bp_num + if expected_bp_desc in thread.GetStopDescription(100): + stepping_thread = thread + break + self.assertTrue(stepping_thread != None, "unable to find thread stopped at %s" % expected_bp_desc) + current_line = self.breakpoint + # Keep stepping until we've reached our designated continue point + while current_line != self.continuepoint: + if stepping_thread != process.GetSelectedThread(): + process.SetSelectedThread(stepping_thread) + + self.runCmd(step_cmd) + + frame = stepping_thread.GetFrameAtIndex(0) + current_line = frame.GetLineEntry().GetLine() + + # Make sure we're still where we thought we were + self.assertTrue(current_line >= self.breakpoint, "Stepped to unexpected line, " + str(current_line)) + self.assertTrue(current_line <= self.continuepoint, "Stepped to unexpected line, " + str(current_line)) + + # Update the number of threads + num_threads = process.GetNumThreads() + + # Check to see that we increased the number of threads as expected + self.assertTrue(num_threads == 3, 'Number of expected threads and actual threads do not match after thread exit.') + + self.expect("thread list", 'Process state is stopped due to step', + substrs = ['stopped', + step_stop_reason]) + + # Run to completion + self.runCmd("process continue") + + # At this point, the inferior process should have exited. + self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED) diff --git a/packages/Python/lldbsuite/test/functionalities/thread/create_during_step/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/create_during_step/main.cpp new file mode 100644 index 0000000..3a00248 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/create_during_step/main.cpp @@ -0,0 +1,89 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This test is intended to create a situation in which one thread will be +// created while the debugger is stepping in another thread. + +#include <atomic> +#include <thread> + +// Note that although hogging the CPU while waiting for a variable to change +// would be terrible in production code, it's great for testing since it +// avoids a lot of messy context switching to get multiple threads synchronized. +#define do_nothing() + +#define pseudo_barrier_wait(bar) \ + --bar; \ + while (bar > 0) \ + do_nothing(); + +#define pseudo_barrier_init(bar, count) (bar = count) + +std::atomic_int g_barrier; + +volatile int g_thread_created = 0; +volatile int g_test = 0; + +void * +step_thread_func () +{ + g_test = 0; // Set breakpoint here + + while (!g_thread_created) + g_test++; + + // One more time to provide a continue point + g_test++; // Continue from here + + // Return + return NULL; +} + +void * +create_thread_func (void *input) +{ + std::thread *step_thread = (std::thread*)input; + + // Wait until the main thread knows this thread is started. + pseudo_barrier_wait(g_barrier); + + // Wait until the other thread is done. + step_thread->join(); + + // Return + return NULL; +} + +int main () +{ + // Use a simple count to simulate a barrier. + pseudo_barrier_init(g_barrier, 2); + + // Create a thread to hit the breakpoint. + std::thread thread_1(step_thread_func); + + // Wait until the step thread is stepping + while (g_test < 1) + do_nothing(); + + // Create a thread to exit while we're stepping. + std::thread thread_2(create_thread_func, &thread_1); + + // Wait until that thread is started + pseudo_barrier_wait(g_barrier); + + // Let the stepping thread know the other thread is there + g_thread_created = 1; + + // Wait for the threads to finish. + thread_2.join(); + thread_1.join(); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/exit_during_break/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/exit_during_break/Makefile new file mode 100644 index 0000000..67aa166 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/exit_during_break/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +ENABLE_THREADS := YES +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/exit_during_break/TestExitDuringBreak.py b/packages/Python/lldbsuite/test/functionalities/thread/exit_during_break/TestExitDuringBreak.py new file mode 100644 index 0000000..f999ffe --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/exit_during_break/TestExitDuringBreak.py @@ -0,0 +1,81 @@ +""" +Test number of threads. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class ExitDuringBreakpointTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number for our breakpoint. + self.breakpoint = line_number('main.cpp', '// Set breakpoint here') + + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureFreeBSD("llvm.org/pr18190") # thread states not properly maintained + @expectedFailureLinux("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureWindows("llvm.org/pr24668") # Breakpoints not resolved correctly + def test(self): + """Test thread exit during breakpoint handling.""" + self.build(dictionary=self.getBuildFlags()) + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint in the main thread. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.breakpoint, num_expected_locations=1) + + # The breakpoint list should show 1 location. + self.expect("breakpoint list -f", "Breakpoint location shown correctly", + substrs = ["1: file = 'main.cpp', line = %d, locations = 1" % self.breakpoint]) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # The exit probably occurred during breakpoint handling, but it isn't + # guaranteed. The main thing we're testing here is that the debugger + # handles this cleanly is some way. + + # Get the number of threads + num_threads = process.GetNumThreads() + + # Make sure we see at least five threads + self.assertTrue(num_threads >= 5, 'Number of expected threads and actual threads do not match.') + + # Get the thread objects + thread1 = process.GetThreadAtIndex(0) + thread2 = process.GetThreadAtIndex(1) + thread3 = process.GetThreadAtIndex(2) + thread4 = process.GetThreadAtIndex(3) + thread5 = process.GetThreadAtIndex(4) + + # Make sure all threads are stopped + self.assertTrue(thread1.IsStopped(), "Thread 1 didn't stop during breakpoint") + self.assertTrue(thread2.IsStopped(), "Thread 2 didn't stop during breakpoint") + self.assertTrue(thread3.IsStopped(), "Thread 3 didn't stop during breakpoint") + self.assertTrue(thread4.IsStopped(), "Thread 4 didn't stop during breakpoint") + self.assertTrue(thread5.IsStopped(), "Thread 5 didn't stop during breakpoint") + + # Run to completion + self.runCmd("continue") + + # At this point, the inferior process should have exited. + self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED) diff --git a/packages/Python/lldbsuite/test/functionalities/thread/exit_during_break/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/exit_during_break/main.cpp new file mode 100644 index 0000000..3570637 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/exit_during_break/main.cpp @@ -0,0 +1,130 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This test is intended to create a situation in which one thread will exit +// while a breakpoint is being handled in another thread. This may not always +// happen because it's possible that the exiting thread will exit before the +// breakpoint is hit. The test case should be flexible enough to treat that +// as success. + +#include <atomic> +#include <chrono> +#include <thread> + +volatile int g_test = 0; + +// Note that although hogging the CPU while waiting for a variable to change +// would be terrible in production code, it's great for testing since it +// avoids a lot of messy context switching to get multiple threads synchronized. +#define do_nothing() + +#define pseudo_barrier_wait(bar) \ + --bar; \ + while (bar > 0) \ + do_nothing(); + +#define pseudo_barrier_init(bar, count) (bar = count) + +// A barrier to synchronize all the threads except the one that will exit. +std::atomic_int g_barrier1; + +// A barrier to synchronize all the threads including the one that will exit. +std::atomic_int g_barrier2; + +// A barrier to keep the first group of threads from exiting until after the +// breakpoint has been passed. +std::atomic_int g_barrier3; + +void * +break_thread_func () +{ + // Wait until the entire first group of threads is running + pseudo_barrier_wait(g_barrier1); + + // Wait for the exiting thread to start + pseudo_barrier_wait(g_barrier2); + + // Do something + g_test++; // Set breakpoint here + + // Synchronize after the breakpoint + pseudo_barrier_wait(g_barrier3); + + // Return + return NULL; +} + +void * +wait_thread_func () +{ + // Wait until the entire first group of threads is running + pseudo_barrier_wait(g_barrier1); + + // Wait for the exiting thread to start + pseudo_barrier_wait(g_barrier2); + + // Wait until the breakpoint has been passed + pseudo_barrier_wait(g_barrier3); + + // Return + return NULL; +} + +void * +exit_thread_func () +{ + // Sync up with the rest of the threads. + pseudo_barrier_wait(g_barrier2); + + // Try to make sure this thread doesn't exit until the breakpoint is hit. + std::this_thread::sleep_for(std::chrono::microseconds(1)); + + // Return + return NULL; +} + +int main () +{ + + // The first barrier waits for the non-exiting threads to start. + // This thread will also participate in that barrier. + // The idea here is to guarantee that the exiting thread will be + // last in the internal list maintained by the debugger. + pseudo_barrier_init(g_barrier1, 5); + + // The second break synchronyizes thread exection with the breakpoint. + pseudo_barrier_init(g_barrier2, 5); + + // The third barrier keeps the waiting threads around until the breakpoint + // has been passed. + pseudo_barrier_init(g_barrier3, 4); + + // Create a thread to hit the breakpoint + std::thread thread_1(break_thread_func); + + // Create more threads to slow the debugger down during processing. + std::thread thread_2(wait_thread_func); + std::thread thread_3(wait_thread_func); + std::thread thread_4(wait_thread_func); + + // Wait for all these threads to get started. + pseudo_barrier_wait(g_barrier1); + + // Create a thread to exit during the breakpoint + std::thread thread_5(exit_thread_func); + + // Wait for the threads to finish + thread_5.join(); + thread_4.join(); + thread_3.join(); + thread_2.join(); + thread_1.join(); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/exit_during_step/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/exit_during_step/Makefile new file mode 100644 index 0000000..d06a7d4 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/exit_during_step/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +ENABLE_THREADS := YES +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/exit_during_step/TestExitDuringStep.py b/packages/Python/lldbsuite/test/functionalities/thread/exit_during_step/TestExitDuringStep.py new file mode 100644 index 0000000..67d1c96 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/exit_during_step/TestExitDuringStep.py @@ -0,0 +1,144 @@ +""" +Test number of threads. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class ExitDuringStepTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureFreeBSD("llvm.org/pr18190") # thread states not properly maintained + @expectedFailureLinux("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureWindows("llvm.org/pr24681") + def test_thread_state_is_stopped(self): + """Test thread exit during step handling.""" + self.build(dictionary=self.getBuildFlags()) + self.exit_during_step_base("thread step-in -m all-threads", 'stop reason = step in', True) + + @skipIfFreeBSD # llvm.org/pr21411: test is hanging + @expectedFailureWindows("llvm.org/pr24681") + def test(self): + """Test thread exit during step handling.""" + self.build(dictionary=self.getBuildFlags()) + self.exit_during_step_base("thread step-inst -m all-threads", 'stop reason = instruction step', False) + + @skipIfFreeBSD # llvm.org/pr21411: test is hanging + @expectedFailureWindows("llvm.org/pr24681") + def test_step_over(self): + """Test thread exit during step-over handling.""" + self.build(dictionary=self.getBuildFlags()) + self.exit_during_step_base("thread step-over -m all-threads", 'stop reason = step over', False) + + @skipIfFreeBSD # llvm.org/pr21411: test is hanging + @expectedFailureWindows("llvm.org/pr24681") + def test_step_in(self): + """Test thread exit during step-in handling.""" + self.build(dictionary=self.getBuildFlags()) + self.exit_during_step_base("thread step-in -m all-threads", 'stop reason = step in', False) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break and continue. + self.breakpoint = line_number('main.cpp', '// Set breakpoint here') + self.continuepoint = line_number('main.cpp', '// Continue from here') + + def exit_during_step_base(self, step_cmd, step_stop_reason, test_thread_state): + """Test thread exit during step handling.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint in the main thread. + self.bp_num = lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.breakpoint, num_expected_locations=1) + + # The breakpoint list should show 1 location. + self.expect("breakpoint list -f", "Breakpoint location shown correctly", + substrs = ["1: file = 'main.cpp', line = %d, exact_match = 0, locations = 1" % self.breakpoint]) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # Get the number of threads + num_threads = process.GetNumThreads() + + # Make sure we see all three threads + self.assertTrue(num_threads == 3, 'Number of expected threads and actual threads do not match.') + + # Get the thread objects + thread1 = process.GetThreadAtIndex(0) + thread2 = process.GetThreadAtIndex(1) + thread3 = process.GetThreadAtIndex(2) + + # Make sure all threads are stopped + if test_thread_state: + self.assertTrue(thread1.IsStopped(), "Thread 1 didn't stop during breakpoint") + self.assertTrue(thread2.IsStopped(), "Thread 2 didn't stop during breakpoint") + self.assertTrue(thread3.IsStopped(), "Thread 3 didn't stop during breakpoint") + return + + # Find the thread that is stopped at the breakpoint + stepping_thread = None + for thread in process: + expected_bp_desc = "breakpoint %s." % self.bp_num + stop_desc = thread.GetStopDescription(100) + if stop_desc and (expected_bp_desc in stop_desc): + stepping_thread = thread + break + self.assertTrue(stepping_thread != None, "unable to find thread stopped at %s" % expected_bp_desc) + + current_line = self.breakpoint + stepping_frame = stepping_thread.GetFrameAtIndex(0) + self.assertTrue(current_line == stepping_frame.GetLineEntry().GetLine(), "Starting line for stepping doesn't match breakpoint line.") + + # Keep stepping until we've reached our designated continue point + while current_line != self.continuepoint: + # Since we're using the command interpreter to issue the thread command + # (on the selected thread) we need to ensure the selected thread is the + # stepping thread. + if stepping_thread != process.GetSelectedThread(): + process.SetSelectedThread(stepping_thread) + + self.runCmd(step_cmd) + + frame = stepping_thread.GetFrameAtIndex(0) + + current_line = frame.GetLineEntry().GetLine() + + self.assertTrue(current_line >= self.breakpoint, "Stepped to unexpected line, " + str(current_line)) + self.assertTrue(current_line <= self.continuepoint, "Stepped to unexpected line, " + str(current_line)) + + self.runCmd("thread list") + + # Update the number of threads + num_threads = process.GetNumThreads() + + # Check to see that we reduced the number of threads as expected + self.assertTrue(num_threads == 2, 'Number of expected threads and actual threads do not match after thread exit.') + + self.expect("thread list", 'Process state is stopped due to step', + substrs = ['stopped', + step_stop_reason]) + + # Run to completion + self.runCmd("continue") + + # At this point, the inferior process should have exited. + self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED) diff --git a/packages/Python/lldbsuite/test/functionalities/thread/exit_during_step/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/exit_during_step/main.cpp new file mode 100644 index 0000000..d1b364b --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/exit_during_step/main.cpp @@ -0,0 +1,87 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This test is intended to create a situation in which one thread will exit +// while the debugger is stepping in another thread. + +#include <thread> + +// Note that although hogging the CPU while waiting for a variable to change +// would be terrible in production code, it's great for testing since it +// avoids a lot of messy context switching to get multiple threads synchronized. +#define do_nothing() + +#define pseudo_barrier_wait(bar) \ + --bar; \ + while (bar > 0) \ + do_nothing(); + +#define pseudo_barrier_init(bar, count) (bar = count) + +// A barrier to synchronize thread start. +volatile int g_barrier; + +volatile int g_thread_exited = 0; + +volatile int g_test = 0; + +void * +step_thread_func () +{ + // Wait until both threads are started. + pseudo_barrier_wait(g_barrier); + + g_test = 0; // Set breakpoint here + + while (!g_thread_exited) + g_test++; + + // One more time to provide a continue point + g_test++; // Continue from here + + // Return + return NULL; +} + +void * +exit_thread_func () +{ + // Wait until both threads are started. + pseudo_barrier_wait(g_barrier); + + // Wait until the other thread is stepping. + while (g_test == 0) + do_nothing(); + + // Return + return NULL; +} + +int main () +{ + // Synchronize thread start so that doesn't happen during stepping. + pseudo_barrier_init(g_barrier, 2); + + // Create a thread to hit the breakpoint. + std::thread thread_1(step_thread_func); + + // Create a thread to exit while we're stepping. + std::thread thread_2(exit_thread_func); + + // Wait for the exit thread to finish. + thread_2.join(); + + // Let the stepping thread know the other thread is gone. + g_thread_exited = 1; + + // Wait for the stepping thread to finish. + thread_1.join(); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/jump/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/jump/Makefile new file mode 100644 index 0000000..b726fc3 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/jump/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +ENABLE_THREADS := YES +CXX_SOURCES := main.cpp other.cpp +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/jump/TestThreadJump.py b/packages/Python/lldbsuite/test/functionalities/thread/jump/TestThreadJump.py new file mode 100644 index 0000000..be49a21 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/jump/TestThreadJump.py @@ -0,0 +1,60 @@ +""" +Test jumping to different places. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class ThreadJumpTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test(self): + """Test thread jump handling.""" + self.build(dictionary=self.getBuildFlags()) + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Find the line numbers for our breakpoints. + self.mark1 = line_number('main.cpp', '// 1st marker') + self.mark2 = line_number('main.cpp', '// 2nd marker') + self.mark3 = line_number('main.cpp', '// 3rd marker') + self.mark4 = line_number('main.cpp', '// 4th marker') + self.mark5 = line_number('other.cpp', '// other marker') + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.mark3, num_expected_locations=1) + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint 1. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT + " 1", + substrs = ['stopped', + '* thread #1', + 'stop reason = breakpoint 1']) + + self.do_min_test(self.mark3, self.mark1, "i", "4"); # Try the int path, force it to return 'a' + self.do_min_test(self.mark3, self.mark2, "i", "5"); # Try the int path, force it to return 'b' + self.do_min_test(self.mark4, self.mark1, "j", "7"); # Try the double path, force it to return 'a' + self.do_min_test(self.mark4, self.mark2, "j", "8"); # Try the double path, force it to return 'b' + + # Try jumping to another function in a different file. + self.runCmd("thread jump --file other.cpp --line %i --force" % self.mark5) + self.expect("process status", + substrs = ["at other.cpp:%i" % self.mark5]) + + # Try jumping to another function (without forcing) + self.expect("j main.cpp:%i" % self.mark1, COMMAND_FAILED_AS_EXPECTED, error = True, + substrs = ["error"]) + + def do_min_test(self, start, jump, var, value): + self.runCmd("j %i" % start) # jump to the start marker + self.runCmd("thread step-in") # step into the min fn + self.runCmd("j %i" % jump) # jump to the branch we're interested in + self.runCmd("thread step-out") # return out + self.runCmd("thread step-over") # assign to the global + self.expect("expr %s" % var, substrs = [value]) # check it diff --git a/packages/Python/lldbsuite/test/functionalities/thread/jump/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/jump/main.cpp new file mode 100644 index 0000000..3497155 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/jump/main.cpp @@ -0,0 +1,35 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This test verifies the correct handling of program counter jumps. + +int otherfn(); + +template<typename T> +T min(T a, T b) +{ + if (a < b) + { + return a; // 1st marker + } else { + return b; // 2nd marker + } +} + +int main () +{ + int i; + double j; + int min_i_a = 4, min_i_b = 5; + double min_j_a = 7.0, min_j_b = 8.0; + i = min(min_i_a, min_i_b); // 3rd marker + j = min(min_j_a, min_j_b); // 4th marker + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/jump/other.cpp b/packages/Python/lldbsuite/test/functionalities/thread/jump/other.cpp new file mode 100644 index 0000000..8108a52 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/jump/other.cpp @@ -0,0 +1,13 @@ +//===-- other.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +int otherfn() +{ + return 4; // other marker +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/main.cpp new file mode 100644 index 0000000..6a0ea4e --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/main.cpp @@ -0,0 +1,50 @@ +#include <condition_variable> +#include <mutex> +#include <thread> + +std::mutex mutex; +std::condition_variable cond; + +void * +thread3(void *input) +{ + std::unique_lock<std::mutex> lock(mutex); + cond.notify_all(); // Set break point at this line. + return NULL; +} + +void * +thread2(void *input) +{ + std::unique_lock<std::mutex> lock(mutex); + cond.notify_all(); + cond.wait(lock); + return NULL; +} + +void * +thread1(void *input) +{ + std::thread thread_2(thread2, nullptr); + thread_2.join(); + + return NULL; +} + +int main() +{ + std::unique_lock<std::mutex> lock(mutex); + + std::thread thread_1(thread1, nullptr); + cond.wait(lock); + + std::thread thread_3(thread3, nullptr); + cond.wait(lock); + + lock.unlock(); + + thread_1.join(); + thread_3.join(); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/multi_break/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/multi_break/Makefile new file mode 100644 index 0000000..67aa166 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/multi_break/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +ENABLE_THREADS := YES +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/multi_break/TestMultipleBreakpoints.py b/packages/Python/lldbsuite/test/functionalities/thread/multi_break/TestMultipleBreakpoints.py new file mode 100644 index 0000000..9dd2124 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/multi_break/TestMultipleBreakpoints.py @@ -0,0 +1,77 @@ +""" +Test number of threads. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class MultipleBreakpointTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number for our breakpoint. + self.breakpoint = line_number('main.cpp', '// Set breakpoint here') + + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureFreeBSD("llvm.org/pr18190") # thread states not properly maintained + @expectedFailureLinux("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureWindows("llvm.org/pr24668") # Breakpoints not resolved correctly + def test(self): + """Test simultaneous breakpoints in multiple threads.""" + self.build(dictionary=self.getBuildFlags()) + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint in the main thread. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.breakpoint, num_expected_locations=1) + + # The breakpoint list should show 1 location. + self.expect("breakpoint list -f", "Breakpoint location shown correctly", + substrs = ["1: file = 'main.cpp', line = %d, locations = 1" % self.breakpoint]) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + # The breakpoint may be hit in either thread 2 or thread 3. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # Get the number of threads + num_threads = process.GetNumThreads() + + # Make sure we see all three threads + self.assertTrue(num_threads == 3, 'Number of expected threads and actual threads do not match.') + + # Get the thread objects + thread1 = process.GetThreadAtIndex(0) + thread2 = process.GetThreadAtIndex(1) + thread3 = process.GetThreadAtIndex(2) + + # Make sure both threads are stopped + self.assertTrue(thread1.IsStopped(), "Primary thread didn't stop during breakpoint") + self.assertTrue(thread2.IsStopped(), "Secondary thread didn't stop during breakpoint") + self.assertTrue(thread3.IsStopped(), "Tertiary thread didn't stop during breakpoint") + + # Delete the first breakpoint then continue + self.runCmd("breakpoint delete 1") + + # Run to completion + self.runCmd("continue") + + # At this point, the inferior process should have exited. + self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED) diff --git a/packages/Python/lldbsuite/test/functionalities/thread/multi_break/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/multi_break/main.cpp new file mode 100644 index 0000000..01f4b8f --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/multi_break/main.cpp @@ -0,0 +1,61 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This test is intended to create a situation in which a breakpoint will be +// hit in two threads at nearly the same moment. The expected result is that +// the breakpoint in the second thread will be hit while the breakpoint handler +// in the first thread is trying to stop all threads. + +#include <atomic> +#include <thread> + +// Note that although hogging the CPU while waiting for a variable to change +// would be terrible in production code, it's great for testing since it +// avoids a lot of messy context switching to get multiple threads synchronized. +#define do_nothing() + +#define pseudo_barrier_wait(bar) \ + --bar; \ + while (bar > 0) \ + do_nothing(); + +#define pseudo_barrier_init(bar, count) (bar = count) + +std::atomic_int g_barrier; + +volatile int g_test = 0; + +void * +thread_func () +{ + // Wait until both threads are running + pseudo_barrier_wait(g_barrier); + + // Do something + g_test++; // Set breakpoint here + + // Return + return NULL; +} + +int main () +{ + // Don't let either thread do anything until they're both ready. + pseudo_barrier_init(g_barrier, 2); + + // Create two threads + std::thread thread_1(thread_func); + std::thread thread_2(thread_func); + + // Wait for the threads to finish + thread_1.join(); + thread_2.join(); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/state/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/state/Makefile new file mode 100644 index 0000000..26db481 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/state/Makefile @@ -0,0 +1,4 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/state/TestThreadStates.py b/packages/Python/lldbsuite/test/functionalities/thread/state/TestThreadStates.py new file mode 100644 index 0000000..623c659 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/state/TestThreadStates.py @@ -0,0 +1,333 @@ +""" +Test thread states. +""" + +from __future__ import print_function + + + +import unittest2 +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class ThreadStateTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @expectedFailureDarwin("rdar://15367566") + @expectedFailureFreeBSD('llvm.org/pr15824') + @expectedFailureLinux("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureWindows("llvm.org/pr24668") # Breakpoints not resolved correctly + def test_state_after_breakpoint(self): + """Test thread state after breakpoint.""" + self.build(dictionary=self.getBuildFlags(use_cpp11=False)) + self.thread_state_after_breakpoint_test() + + @skipIfDarwin # 'llvm.org/pr23669', cause Python crash randomly + @expectedFailureDarwin('llvm.org/pr23669') + @expectedFailureFreeBSD('llvm.org/pr15824') + @expectedFailureWindows("llvm.org/pr24660") + def test_state_after_continue(self): + """Test thread state after continue.""" + self.build(dictionary=self.getBuildFlags(use_cpp11=False)) + self.thread_state_after_continue_test() + + @skipIfDarwin # 'llvm.org/pr23669', cause Python crash randomly + @expectedFailureDarwin('llvm.org/pr23669') + @expectedFailureWindows("llvm.org/pr24660") + @unittest2.expectedFailure("llvm.org/pr16712") # thread states not properly maintained + def test_state_after_expression(self): + """Test thread state after expression.""" + self.build(dictionary=self.getBuildFlags(use_cpp11=False)) + self.thread_state_after_expression_test() + + @unittest2.expectedFailure("llvm.org/pr16712") # thread states not properly maintained + @expectedFailureWindows("llvm.org/pr24668") # Breakpoints not resolved correctly + def test_process_interrupt(self): + """Test process interrupt.""" + self.build(dictionary=self.getBuildFlags(use_cpp11=False)) + self.process_interrupt_test() + + @unittest2.expectedFailure("llvm.org/pr15824") # thread states not properly maintained + @expectedFailureWindows("llvm.org/pr24668") # Breakpoints not resolved correctly + def test_process_state(self): + """Test thread states (comprehensive).""" + self.build(dictionary=self.getBuildFlags(use_cpp11=False)) + self.thread_states_test() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers for our breakpoints. + self.break_1 = line_number('main.cpp', '// Set first breakpoint here') + self.break_2 = line_number('main.cpp', '// Set second breakpoint here') + + def thread_state_after_breakpoint_test(self): + """Test thread state after breakpoint.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint in the main thread. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_1, num_expected_locations=1) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + '* thread #1', + 'stop reason = breakpoint']) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # Get the number of threads + num_threads = process.GetNumThreads() + + self.assertTrue(num_threads == 1, 'Number of expected threads and actual threads do not match.') + + # Get the thread object + thread = process.GetThreadAtIndex(0) + + # Make sure the thread is in the stopped state. + self.assertTrue(thread.IsStopped(), "Thread state isn't \'stopped\' during breakpoint 1.") + self.assertFalse(thread.IsSuspended(), "Thread state is \'suspended\' during breakpoint 1.") + + # Kill the process + self.runCmd("process kill") + + def wait_for_running_event(self): + listener = self.dbg.GetListener() + if lldb.remote_platform: + lldbutil.expect_state_changes(self, listener, [lldb.eStateConnected]) + lldbutil.expect_state_changes(self, listener, [lldb.eStateRunning]) + + def thread_state_after_continue_test(self): + """Test thread state after continue.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint in the main thread. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_1, num_expected_locations=1) + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_2, num_expected_locations=1) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + '* thread #1', + 'stop reason = breakpoint']) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # Get the number of threads + num_threads = process.GetNumThreads() + + self.assertTrue(num_threads == 1, 'Number of expected threads and actual threads do not match.') + + # Get the thread object + thread = process.GetThreadAtIndex(0) + + # Continue, the inferior will go into an infinite loop waiting for 'g_test' to change. + self.dbg.SetAsync(True) + self.runCmd("continue") + self.wait_for_running_event() + + # Check the thread state. It should be running. + self.assertFalse(thread.IsStopped(), "Thread state is \'stopped\' when it should be running.") + self.assertFalse(thread.IsSuspended(), "Thread state is \'suspended\' when it should be running.") + + # Go back to synchronous interactions + self.dbg.SetAsync(False) + + # Kill the process + self.runCmd("process kill") + + def thread_state_after_expression_test(self): + """Test thread state after expression.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint in the main thread. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_1, num_expected_locations=1) + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_2, num_expected_locations=1) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + '* thread #1', + 'stop reason = breakpoint']) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # Get the number of threads + num_threads = process.GetNumThreads() + + self.assertTrue(num_threads == 1, 'Number of expected threads and actual threads do not match.') + + # Get the thread object + thread = process.GetThreadAtIndex(0) + + # Get the inferior out of its loop + self.runCmd("expression g_test = 1") + + # Check the thread state + self.assertTrue(thread.IsStopped(), "Thread state isn't \'stopped\' after expression evaluation.") + self.assertFalse(thread.IsSuspended(), "Thread state is \'suspended\' after expression evaluation.") + + # Let the process run to completion + self.runCmd("process continue") + + + def process_interrupt_test(self): + """Test process interrupt and continue.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint in the main thread. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_1, num_expected_locations=1) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + '* thread #1', + 'stop reason = breakpoint']) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # Get the number of threads + num_threads = process.GetNumThreads() + + self.assertTrue(num_threads == 1, 'Number of expected threads and actual threads do not match.') + + # Continue, the inferior will go into an infinite loop waiting for 'g_test' to change. + self.dbg.SetAsync(True) + self.runCmd("continue") + self.wait_for_running_event() + + # Go back to synchronous interactions + self.dbg.SetAsync(False) + + # Stop the process + self.runCmd("process interrupt") + + # The stop reason of the thread should be signal. + self.expect("process status", STOPPED_DUE_TO_SIGNAL, + substrs = ['stopped', + '* thread #1', + 'stop reason = signal']) + + # Get the inferior out of its loop + self.runCmd("expression g_test = 1") + + # Run to completion + self.runCmd("continue") + + def thread_states_test(self): + """Test thread states (comprehensive).""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint in the main thread. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_1, num_expected_locations=1) + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_2, num_expected_locations=1) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + '* thread #1', + 'stop reason = breakpoint']) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # Get the number of threads + num_threads = process.GetNumThreads() + + self.assertTrue(num_threads == 1, 'Number of expected threads and actual threads do not match.') + + # Get the thread object + thread = process.GetThreadAtIndex(0) + + # Make sure the thread is in the stopped state. + self.assertTrue(thread.IsStopped(), "Thread state isn't \'stopped\' during breakpoint 1.") + self.assertFalse(thread.IsSuspended(), "Thread state is \'suspended\' during breakpoint 1.") + + # Continue, the inferior will go into an infinite loop waiting for 'g_test' to change. + self.dbg.SetAsync(True) + self.runCmd("continue") + self.wait_for_running_event() + + # Check the thread state. It should be running. + self.assertFalse(thread.IsStopped(), "Thread state is \'stopped\' when it should be running.") + self.assertFalse(thread.IsSuspended(), "Thread state is \'suspended\' when it should be running.") + + # Go back to synchronous interactions + self.dbg.SetAsync(False) + + # Stop the process + self.runCmd("process interrupt") + + # The stop reason of the thread should be signal. + self.expect("process status", STOPPED_DUE_TO_SIGNAL, + substrs = ['stopped', + '* thread #1', + 'stop reason = signal']) + + # Check the thread state + self.assertTrue(thread.IsStopped(), "Thread state isn't \'stopped\' after process stop.") + self.assertFalse(thread.IsSuspended(), "Thread state is \'suspended\' after process stop.") + + # Get the inferior out of its loop + self.runCmd("expression g_test = 1") + + # Check the thread state + self.assertTrue(thread.IsStopped(), "Thread state isn't \'stopped\' after expression evaluation.") + self.assertFalse(thread.IsSuspended(), "Thread state is \'suspended\' after expression evaluation.") + + # The stop reason of the thread should be signal. + self.expect("process status", STOPPED_DUE_TO_SIGNAL, + substrs = ['stopped', + '* thread #1', + 'stop reason = signal']) + + # Run to breakpoint 2 + self.runCmd("continue") + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + '* thread #1', + 'stop reason = breakpoint']) + + # Make sure both threads are stopped + self.assertTrue(thread.IsStopped(), "Thread state isn't \'stopped\' during breakpoint 2.") + self.assertFalse(thread.IsSuspended(), "Thread state is \'suspended\' during breakpoint 2.") + + # Run to completion + self.runCmd("continue") + + # At this point, the inferior process should have exited. + self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED) diff --git a/packages/Python/lldbsuite/test/functionalities/thread/state/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/state/main.cpp new file mode 100644 index 0000000..081203d --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/state/main.cpp @@ -0,0 +1,45 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This test is intended to verify that thread states are properly maintained +// when transitional actions are performed in the debugger. Most of the logic +// is in the test script. This program merely provides places where the test +// can create the intended states. + +#include <chrono> +#include <thread> + +volatile int g_test = 0; + +int addSomething(int a) +{ + return a + g_test; +} + +int doNothing() +{ + int temp = 0; // Set first breakpoint here + + while (!g_test && temp < 5) + { + ++temp; + std::this_thread::sleep_for(std::chrono::seconds(2)); + } + + return temp; // Set second breakpoint here +} + +int main () +{ + int result = doNothing(); + + int i = addSomething(result); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/step_out/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/step_out/Makefile new file mode 100644 index 0000000..035413f --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/step_out/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +ENABLE_THREADS := YES + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/step_out/TestThreadStepOut.py b/packages/Python/lldbsuite/test/functionalities/thread/step_out/TestThreadStepOut.py new file mode 100644 index 0000000..b2d966c --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/step_out/TestThreadStepOut.py @@ -0,0 +1,131 @@ +""" +Test stepping out from a function in a multi-threaded program. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class ThreadStepOutTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfLinux # Test occasionally times out on the Linux build bot + @expectedFailureLinux("llvm.org/pr23477") # Test occasionally times out on the Linux build bot + @expectedFailureFreeBSD("llvm.org/pr18066") # inferior does not exit + @expectedFailureWindows # Test crashes + def test_step_single_thread(self): + """Test thread step out on one thread via command interpreter. """ + self.build(dictionary=self.getBuildFlags()) + self.step_out_test(self.step_out_single_thread_with_cmd) + + @skipIfLinux # Test occasionally times out on the Linux build bot + @expectedFailureLinux("llvm.org/pr23477") # Test occasionally times out on the Linux build bot + @expectedFailureFreeBSD("llvm.org/pr19347") # 2nd thread stops at breakpoint + @expectedFailureWindows # Test crashes + def test_step_all_threads(self): + """Test thread step out on all threads via command interpreter. """ + self.build(dictionary=self.getBuildFlags()) + self.step_out_test(self.step_out_all_threads_with_cmd) + + @skipIfLinux # Test occasionally times out on the Linux build bot + @expectedFailureLinux("llvm.org/pr23477") # Test occasionally times out on the Linux build bot + @expectedFailureFreeBSD("llvm.org/pr19347") + @expectedFailureWindows("llvm.org/pr24681") + def test_python(self): + """Test thread step out on one thread via Python API (dwarf).""" + self.build(dictionary=self.getBuildFlags()) + self.step_out_test(self.step_out_with_python) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number for our breakpoint. + self.breakpoint = line_number('main.cpp', '// Set breakpoint here') + if "gcc" in self.getCompiler() or self.isIntelCompiler(): + self.step_out_destination = line_number('main.cpp', '// Expect to stop here after step-out (icc and gcc)') + else: + self.step_out_destination = line_number('main.cpp', '// Expect to stop here after step-out (clang)') + + def step_out_single_thread_with_cmd(self): + self.step_out_with_cmd("this-thread") + self.expect("thread backtrace all", "Thread location after step out is correct", + substrs = ["main.cpp:%d" % self.step_out_destination, + "main.cpp:%d" % self.breakpoint]) + + def step_out_all_threads_with_cmd(self): + self.step_out_with_cmd("all-threads") + self.expect("thread backtrace all", "Thread location after step out is correct", + substrs = ["main.cpp:%d" % self.step_out_destination]) + + def step_out_with_cmd(self, run_mode): + self.runCmd("thread select %d" % self.step_out_thread.GetIndexID()) + self.runCmd("thread step-out -m %s" % run_mode) + self.expect("process status", "Expected stop reason to be step-out", + substrs = ["stop reason = step out"]) + + self.expect("thread list", "Selected thread did not change during step-out", + substrs = ["* thread #%d" % self.step_out_thread.GetIndexID()]) + + def step_out_with_python(self): + self.step_out_thread.StepOut() + + reason = self.step_out_thread.GetStopReason() + self.assertEqual(lldb.eStopReasonPlanComplete, reason, + "Expected thread stop reason 'plancomplete', but got '%s'" % lldbutil.stop_reason_to_str(reason)) + + # Verify location after stepping out + frame = self.step_out_thread.GetFrameAtIndex(0) + desc = lldbutil.get_description(frame.GetLineEntry()) + expect = "main.cpp:%d" % self.step_out_destination + self.assertTrue(expect in desc, "Expected %s but thread stopped at %s" % (expect, desc)) + + def step_out_test(self, step_out_func): + """Test single thread step out of a function.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint in the main thread. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.breakpoint, num_expected_locations=1) + + # The breakpoint list should show 1 location. + self.expect("breakpoint list -f", "Breakpoint location shown correctly", + substrs = ["1: file = 'main.cpp', line = %d, exact_match = 0, locations = 1" % self.breakpoint]) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # Get the target process + self.inferior_target = self.dbg.GetSelectedTarget() + self.inferior_process = self.inferior_target.GetProcess() + + # Get the number of threads, ensure we see all three. + num_threads = self.inferior_process.GetNumThreads() + self.assertEqual(num_threads, 3, 'Number of expected threads and actual threads do not match.') + + (breakpoint_threads, other_threads) = ([], []) + lldbutil.sort_stopped_threads(self.inferior_process, + breakpoint_threads=breakpoint_threads, + other_threads=other_threads) + + while len(breakpoint_threads) < 2: + self.runCmd("thread continue %s" % " ".join([str(x.GetIndexID()) for x in other_threads])) + lldbutil.sort_stopped_threads(self.inferior_process, + breakpoint_threads=breakpoint_threads, + other_threads=other_threads) + + self.step_out_thread = breakpoint_threads[0] + + # Step out of thread stopped at breakpoint + step_out_func() + + # Run to completion + self.runCmd("continue") + + # At this point, the inferior process should have exited. + self.assertTrue(self.inferior_process.GetState() == lldb.eStateExited, PROCESS_EXITED) diff --git a/packages/Python/lldbsuite/test/functionalities/thread/step_out/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/step_out/main.cpp new file mode 100644 index 0000000..b4c6216 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/step_out/main.cpp @@ -0,0 +1,63 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This test is intended to create a situation in which two threads are stopped +// at a breakpoint and the debugger issues a step-out command. + +#include <atomic> +#include <thread> + +// Note that although hogging the CPU while waiting for a variable to change +// would be terrible in production code, it's great for testing since it +// avoids a lot of messy context switching to get multiple threads synchronized. +#define do_nothing() + +#define pseudo_barrier_wait(bar) \ + --bar; \ + while (bar > 0) \ + do_nothing(); + +#define pseudo_barrier_init(bar, count) (bar = count) + +std::atomic_int g_barrier; + +volatile int g_test = 0; + +void step_out_of_here() { + g_test += 5; // Set breakpoint here +} + +void * +thread_func () +{ + // Wait until both threads are running + pseudo_barrier_wait(g_barrier); + + // Do something + step_out_of_here(); // Expect to stop here after step-out (clang) + + // Return + return NULL; // Expect to stop here after step-out (icc and gcc) +} + +int main () +{ + // Don't let either thread do anything until they're both ready. + pseudo_barrier_init(g_barrier, 2); + + // Create two threads + std::thread thread_1(thread_func); + std::thread thread_2(thread_func); + + // Wait for the threads to finish + thread_1.join(); + thread_2.join(); + + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/thread_exit/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/thread_exit/Makefile new file mode 100644 index 0000000..d06a7d4 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/thread_exit/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +ENABLE_THREADS := YES +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/thread_exit/TestThreadExit.py b/packages/Python/lldbsuite/test/functionalities/thread/thread_exit/TestThreadExit.py new file mode 100644 index 0000000..f785401 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/thread_exit/TestThreadExit.py @@ -0,0 +1,116 @@ +""" +Test number of threads. +""" + +from __future__ import print_function + + + +import os, time +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class ThreadExitTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers for our breakpoints. + self.break_1 = line_number('main.cpp', '// Set first breakpoint here') + self.break_2 = line_number('main.cpp', '// Set second breakpoint here') + self.break_3 = line_number('main.cpp', '// Set third breakpoint here') + self.break_4 = line_number('main.cpp', '// Set fourth breakpoint here') + + @expectedFailureWindows("llvm.org/pr24681") + def test(self): + """Test thread exit handling.""" + self.build(dictionary=self.getBuildFlags()) + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint with 1 location. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_1, num_expected_locations=1) + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_2, num_expected_locations=1) + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_3, num_expected_locations=1) + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_4, num_expected_locations=1) + + # The breakpoint list should show 1 locations. + self.expect("breakpoint list -f", "Breakpoint location shown correctly", + substrs = ["1: file = 'main.cpp', line = %d, exact_match = 0, locations = 1" % self.break_1, + "2: file = 'main.cpp', line = %d, exact_match = 0, locations = 1" % self.break_2, + "3: file = 'main.cpp', line = %d, exact_match = 0, locations = 1" % self.break_3, + "4: file = 'main.cpp', line = %d, exact_match = 0, locations = 1" % self.break_4]) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint 1. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT + " 1", + substrs = ['stopped', + '* thread #1', + 'stop reason = breakpoint 1', + 'thread #2']) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # Get the number of threads + num_threads = process.GetNumThreads() + + self.assertTrue(num_threads == 2, 'Number of expected threads and actual threads do not match at breakpoint 1.') + + # Run to the second breakpoint + self.runCmd("continue") + + # The stop reason of the thread should be breakpoint 1. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT + " 2", + substrs = ['stopped', + 'thread #1', + 'thread #2', + 'stop reason = breakpoint 2', + 'thread #3']) + + # Update the number of threads + num_threads = process.GetNumThreads() + + self.assertTrue(num_threads == 3, 'Number of expected threads and actual threads do not match at breakpoint 2.') + + # Run to the third breakpoint + self.runCmd("continue") + + # The stop reason of the thread should be breakpoint 3. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT + " 3", + substrs = ['stopped', + 'thread #1', + 'stop reason = breakpoint 3', + 'thread #3', + ]) + + # Update the number of threads + num_threads = process.GetNumThreads() + + self.assertTrue(num_threads == 2, 'Number of expected threads and actual threads do not match at breakpoint 3.') + + # Run to the fourth breakpoint + self.runCmd("continue") + + # The stop reason of the thread should be breakpoint 4. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT + " 4", + substrs = ['stopped', + 'thread #1', + 'stop reason = breakpoint 4']) + + # Update the number of threads + num_threads = process.GetNumThreads() + + self.assertTrue(num_threads == 1, 'Number of expected threads and actual threads do not match at breakpoint 4.') + + # Run to completion + self.runCmd("continue") + + # At this point, the inferior process should have exited. + self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED) diff --git a/packages/Python/lldbsuite/test/functionalities/thread/thread_exit/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/thread_exit/main.cpp new file mode 100644 index 0000000..e498db7 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/thread_exit/main.cpp @@ -0,0 +1,85 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This test verifies the correct handling of child thread exits. + +#include <atomic> +#include <thread> + +// Note that although hogging the CPU while waiting for a variable to change +// would be terrible in production code, it's great for testing since it +// avoids a lot of messy context switching to get multiple threads synchronized. +#define do_nothing() + +#define pseudo_barrier_wait(bar) \ + --bar; \ + while (bar > 0) \ + do_nothing(); + +#define pseudo_barrier_init(bar, count) (bar = count) + +std::atomic_int g_barrier1; +std::atomic_int g_barrier2; +std::atomic_int g_barrier3; + +void * +thread1 () +{ + // Synchronize with the main thread. + pseudo_barrier_wait(g_barrier1); + + // Synchronize with the main thread and thread2. + pseudo_barrier_wait(g_barrier2); + + // Return + return NULL; // Set second breakpoint here +} + +void * +thread2 () +{ + // Synchronize with thread1 and the main thread. + pseudo_barrier_wait(g_barrier2); + + // Synchronize with the main thread. + pseudo_barrier_wait(g_barrier3); + + // Return + return NULL; +} + +int main () +{ + pseudo_barrier_init(g_barrier1, 2); + pseudo_barrier_init(g_barrier2, 3); + pseudo_barrier_init(g_barrier3, 2); + + // Create a thread. + std::thread thread_1(thread1); + + // Wait for thread1 to start. + pseudo_barrier_wait(g_barrier1); + + // Create another thread. + std::thread thread_2(thread2); // Set first breakpoint here + + // Wait for thread2 to start. + pseudo_barrier_wait(g_barrier2); + + // Wait for the first thread to finish + thread_1.join(); + + // Synchronize with the remaining thread + pseudo_barrier_wait(g_barrier3); // Set third breakpoint here + + // Wait for the second thread to finish + thread_2.join(); + + return 0; // Set fourth breakpoint here +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break/Makefile new file mode 100644 index 0000000..035413f --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +ENABLE_THREADS := YES + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break/TestThreadSpecificBreakpoint.py b/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break/TestThreadSpecificBreakpoint.py new file mode 100644 index 0000000..3c69b66 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break/TestThreadSpecificBreakpoint.py @@ -0,0 +1,63 @@ +""" +Test that we obey thread conditioned breakpoints. +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class ThreadSpecificBreakTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @add_test_categories(['pyapi']) + @expectedFailureWindows # Thread specific breakpoints cause the inferior to crash + def test_python(self): + """Test that we obey thread conditioned breakpoints.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # This test works by setting a breakpoint in a function conditioned to stop only on + # the main thread, and then calling this function on a secondary thread, joining, + # and then calling again on the main thread. If the thread specific breakpoint works + # then it should not be hit on the secondary thread, only on the main thread. + + main_source_spec = lldb.SBFileSpec ("main.cpp") + + main_breakpoint = target.BreakpointCreateBySourceRegex("Set main breakpoint here", main_source_spec); + thread_breakpoint = target.BreakpointCreateBySourceRegex("Set thread-specific breakpoint here", main_source_spec) + + self.assertTrue(main_breakpoint.IsValid(), "Failed to set main breakpoint.") + self.assertGreater(main_breakpoint.GetNumLocations(), 0, "main breakpoint has no locations associated with it.") + self.assertTrue(thread_breakpoint.IsValid(), "Failed to set thread breakpoint.") + self.assertGreater(thread_breakpoint.GetNumLocations(), 0, "thread breakpoint has no locations associated with it.") + + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + stopped_threads = lldbutil.get_threads_stopped_at_breakpoint(process, main_breakpoint) + self.assertEqual(len(stopped_threads), 1, "main breakpoint stopped at unexpected number of threads") + main_thread = stopped_threads[0] + main_thread_id = main_thread.GetThreadID() + + # Set the thread-specific breakpoint to only stop on the main thread. The run the function + # on another thread and join on it. If the thread-specific breakpoint works, the next + # stop should be on the main thread. + thread_breakpoint.SetThreadID(main_thread_id) + + process.Continue() + next_stop_state = process.GetState() + self.assertEqual(next_stop_state, lldb.eStateStopped, "We should have stopped at the thread breakpoint.") + stopped_threads = lldbutil.get_threads_stopped_at_breakpoint(process, thread_breakpoint) + self.assertEqual(len(stopped_threads), 1, "thread breakpoint stopped at unexpected number of threads") + self.assertEqual(stopped_threads[0].GetThreadID(), main_thread_id, "thread breakpoint stopped at the wrong thread") diff --git a/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break/main.cpp new file mode 100644 index 0000000..7721b5d --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break/main.cpp @@ -0,0 +1,20 @@ +#include <chrono> +#include <thread> + +void +thread_function () +{ + // Set thread-specific breakpoint here. + std::this_thread::sleep_for(std::chrono::microseconds(100)); +} + +int +main () +{ + // Set main breakpoint here. + std::thread t(thread_function); + t.join(); + + thread_function(); + return 0; +} diff --git a/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break_plus_condition/Makefile b/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break_plus_condition/Makefile new file mode 100644 index 0000000..035413f --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break_plus_condition/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +ENABLE_THREADS := YES + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break_plus_condition/TestThreadSpecificBpPlusCondition.py b/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break_plus_condition/TestThreadSpecificBpPlusCondition.py new file mode 100644 index 0000000..b4a0b7a --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break_plus_condition/TestThreadSpecificBpPlusCondition.py @@ -0,0 +1,65 @@ +""" +Test that we obey thread conditioned breakpoints and expression +conditioned breakpoints simultaneously +""" + +from __future__ import print_function + + + +import os, time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class ThreadSpecificBreakPlusConditionTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfFreeBSD # test frequently times out or hangs + @expectedFailureFreeBSD('llvm.org/pr18522') # hits break in another thread in testrun + @add_test_categories(['pyapi']) + @expectedFailureWindows # Thread specific breakpoints cause the inferior to crash. + @expectedFlakeyLinux # this test fails 6/100 dosep runs + def test_python(self): + """Test that we obey thread conditioned breakpoints.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + main_source_spec = lldb.SBFileSpec ("main.cpp") + + # Set a breakpoint in the thread body, and make it active for only the first thread. + break_thread_body = target.BreakpointCreateBySourceRegex ("Break here in thread body.", main_source_spec) + self.assertTrue (break_thread_body.IsValid() and break_thread_body.GetNumLocations() > 0, "Failed to set thread body breakpoint.") + + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + threads = lldbutil.get_threads_stopped_at_breakpoint (process, break_thread_body) + + victim_thread = threads[0] + + # Pick one of the threads, and change the breakpoint so it ONLY stops for this thread, + # but add a condition that it won't stop for this thread's my_value. The other threads + # pass the condition, so they should stop, but if the thread-specification is working + # they should not stop. So nobody should hit the breakpoint anymore, and we should + # just exit cleanly. + + frame = victim_thread.GetFrameAtIndex(0) + value = frame.FindVariable("my_value").GetValueAsSigned(0) + self.assertTrue (value > 0 and value < 11, "Got a reasonable value for my_value.") + + cond_string = "my_value != %d"%(value) + + break_thread_body.SetThreadID(victim_thread.GetThreadID()) + break_thread_body.SetCondition (cond_string) + + process.Continue() + + next_stop_state = process.GetState() + self.assertTrue (next_stop_state == lldb.eStateExited, "We should have not hit the breakpoint again.") diff --git a/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break_plus_condition/main.cpp b/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break_plus_condition/main.cpp new file mode 100644 index 0000000..af8ab84 --- /dev/null +++ b/packages/Python/lldbsuite/test/functionalities/thread/thread_specific_break_plus_condition/main.cpp @@ -0,0 +1,39 @@ +#include <chrono> +#include <thread> +#include <vector> + +void * +thread_function (void *thread_marker) +{ + int keep_going = 1; + int my_value = *((int *)thread_marker); + int counter = 0; + + while (counter < 20) + { + counter++; // Break here in thread body. + std::this_thread::sleep_for(std::chrono::microseconds(10)); + } + return NULL; +} + + +int +main () +{ + std::vector<std::thread> threads; + + int thread_value = 0; + int i; + + for (i = 0; i < 10; i++) + { + thread_value += 1; + threads.push_back(std::thread(thread_function, &thread_value)); + } + + for (i = 0; i < 10; i++) + threads[i].join(); + + return 0; +} |