diff options
Diffstat (limited to 'packages/Python/lldbsuite/test/functionalities/thread/backtrace_all')
3 files changed, 214 insertions, 0 deletions
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() |