summaryrefslogtreecommitdiffstats
path: root/packages/Python/lldbsuite/test/api/multithreaded
diff options
context:
space:
mode:
Diffstat (limited to 'packages/Python/lldbsuite/test/api/multithreaded')
-rw-r--r--packages/Python/lldbsuite/test/api/multithreaded/Makefile9
-rw-r--r--packages/Python/lldbsuite/test/api/multithreaded/TestMultithreaded.py91
-rw-r--r--packages/Python/lldbsuite/test/api/multithreaded/common.h68
-rw-r--r--packages/Python/lldbsuite/test/api/multithreaded/driver.cpp38
-rw-r--r--packages/Python/lldbsuite/test/api/multithreaded/inferior.cpp17
-rw-r--r--packages/Python/lldbsuite/test/api/multithreaded/listener_test.cpp74
-rw-r--r--packages/Python/lldbsuite/test/api/multithreaded/lldb-headers.h11
-rw-r--r--packages/Python/lldbsuite/test/api/multithreaded/test_breakpoint_callback.cpp49
-rw-r--r--packages/Python/lldbsuite/test/api/multithreaded/test_listener_event_description.cpp97
-rw-r--r--packages/Python/lldbsuite/test/api/multithreaded/test_listener_event_process_state.cpp68
-rw-r--r--packages/Python/lldbsuite/test/api/multithreaded/test_listener_resume.cpp53
11 files changed, 575 insertions, 0 deletions
diff --git a/packages/Python/lldbsuite/test/api/multithreaded/Makefile b/packages/Python/lldbsuite/test/api/multithreaded/Makefile
new file mode 100644
index 0000000..37323ea
--- /dev/null
+++ b/packages/Python/lldbsuite/test/api/multithreaded/Makefile
@@ -0,0 +1,9 @@
+LEVEL = ../../make
+
+ENABLE_THREADS := YES
+CXX_SOURCES := main.cpp
+
+include $(LEVEL)/Makefile.rules
+
+clean::
+ rm -rf $(wildcard *.o *.d *.dSYM)
diff --git a/packages/Python/lldbsuite/test/api/multithreaded/TestMultithreaded.py b/packages/Python/lldbsuite/test/api/multithreaded/TestMultithreaded.py
new file mode 100644
index 0000000..0b40509
--- /dev/null
+++ b/packages/Python/lldbsuite/test/api/multithreaded/TestMultithreaded.py
@@ -0,0 +1,91 @@
+"""Test the lldb public C++ api breakpoint callbacks."""
+
+from __future__ import print_function
+
+# __package__ = "lldbsuite.test"
+
+
+import os, re
+from lldbsuite.test.lldbtest import *
+import lldbsuite.test.lldbutil as lldbutil
+import subprocess
+
+class SBBreakpointCallbackCase(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+
+ @skipIfRemote
+ @skipIfNoSBHeaders
+ @skipIfWindows # clang-cl does not support throw or catch (llvm.org/pr24538)
+ def test_breakpoint_callback(self):
+ """Test the that SBBreakpoint callback is invoked when a breakpoint is hit. """
+ self.build_and_test('driver.cpp test_breakpoint_callback.cpp',
+ 'test_breakpoint_callback')
+
+ @skipIfRemote
+ @skipIfNoSBHeaders
+ @skipIfWindows # clang-cl does not support throw or catch (llvm.org/pr24538)
+ @expectedFlakeyFreeBSD
+ def test_sb_api_listener_event_description(self):
+ """ Test the description of an SBListener breakpoint event is valid."""
+ self.build_and_test('driver.cpp listener_test.cpp test_listener_event_description.cpp',
+ 'test_listener_event_description')
+ pass
+
+ @skipIfRemote
+ @skipIfNoSBHeaders
+ @skipIfWindows # clang-cl does not support throw or catch (llvm.org/pr24538)
+ @expectedFlakeyFreeBSD
+ @expectedFlakeyLinux # Driver occasionally returns '1' as exit status
+ @expectedFailureAll("llvm.org/pr23139", oslist=["linux"], compiler="gcc", compiler_version=[">=","4.9"], archs=["x86_64"])
+ def test_sb_api_listener_event_process_state(self):
+ """ Test that a registered SBListener receives events when a process
+ changes state.
+ """
+ self.build_and_test('driver.cpp listener_test.cpp test_listener_event_process_state.cpp',
+ 'test_listener_event_process_state')
+ pass
+
+
+ @skipIfRemote
+ @skipIfNoSBHeaders
+ @skipIfWindows # clang-cl does not support throw or catch (llvm.org/pr24538)
+ @expectedFlakeyFreeBSD
+ @expectedFlakeyLinux
+ def test_sb_api_listener_resume(self):
+ """ Test that a process can be resumed from a non-main thread. """
+ self.build_and_test('driver.cpp listener_test.cpp test_listener_resume.cpp',
+ 'test_listener_resume')
+ pass
+
+ def build_and_test(self, sources, test_name, args = None):
+ """ Build LLDB test from sources, and run expecting 0 exit code """
+
+ # These tests link against host lldb API.
+ # Compiler's target triple must match liblldb triple
+ # because remote is disabled, we can assume that the os is the same
+ # still need to check architecture
+ if self.getLldbArchitecture() != self.getArchitecture():
+ self.skipTest("This test is only run if the target arch is the same as the lldb binary arch")
+
+ self.inferior = 'inferior_program'
+ self.buildProgram('inferior.cpp', self.inferior)
+ self.addTearDownHook(lambda: os.remove(self.inferior))
+
+ self.buildDriver(sources, test_name)
+ self.addTearDownHook(lambda: os.remove(test_name))
+
+ test_exe = os.path.join(os.getcwd(), test_name)
+ self.signBinary(test_exe)
+ exe = [test_exe, self.inferior]
+
+ env = {self.dylibPath : self.getLLDBLibraryEnvVal()}
+ if self.TraceOn():
+ print("Running test %s" % " ".join(exe))
+ check_call(exe, env=env)
+ else:
+ with open(os.devnull, 'w') as fnull:
+ check_call(exe, env=env, stdout=fnull, stderr=fnull)
+
+ def build_program(self, sources, program):
+ return self.buildDriver(sources, program)
diff --git a/packages/Python/lldbsuite/test/api/multithreaded/common.h b/packages/Python/lldbsuite/test/api/multithreaded/common.h
new file mode 100644
index 0000000..dad8bba
--- /dev/null
+++ b/packages/Python/lldbsuite/test/api/multithreaded/common.h
@@ -0,0 +1,68 @@
+#ifndef LLDB_TEST_API_COMMON_H
+#define LLDB_TEST_API_COMMON_H
+
+#include <condition_variable>
+#include <chrono>
+#include <exception>
+#include <iostream>
+#include <mutex>
+#include <string>
+#include <queue>
+
+#include <unistd.h>
+
+/// Simple exception class with a message
+struct Exception : public std::exception
+{
+ std::string s;
+ Exception(std::string ss) : s(ss) {}
+ virtual ~Exception() throw () { }
+ const char* what() const throw() { return s.c_str(); }
+};
+
+// Synchronized data structure for listener to send events through
+template<typename T>
+class multithreaded_queue {
+ std::condition_variable m_condition;
+ std::mutex m_mutex;
+ std::queue<T> m_data;
+ bool m_notified;
+
+public:
+
+ void push(T e) {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ m_data.push(e);
+ m_notified = true;
+ m_condition.notify_all();
+ }
+
+ T pop(int timeout_seconds, bool &success) {
+ int count = 0;
+ while (count < timeout_seconds) {
+ std::unique_lock<std::mutex> lock(m_mutex);
+ if (!m_data.empty()) {
+ m_notified = false;
+ T ret = m_data.front();
+ m_data.pop();
+ success = true;
+ return ret;
+ } else if (!m_notified)
+ m_condition.wait_for(lock, std::chrono::seconds(1));
+ count ++;
+ }
+ success = false;
+ return T();
+ }
+};
+
+/// Allocates a char buffer with the current working directory
+inline char* get_working_dir() {
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__)
+ return getwd(0);
+#else
+ return get_current_dir_name();
+#endif
+}
+
+#endif // LLDB_TEST_API_COMMON_H
diff --git a/packages/Python/lldbsuite/test/api/multithreaded/driver.cpp b/packages/Python/lldbsuite/test/api/multithreaded/driver.cpp
new file mode 100644
index 0000000..fa0c48e
--- /dev/null
+++ b/packages/Python/lldbsuite/test/api/multithreaded/driver.cpp
@@ -0,0 +1,38 @@
+
+/// LLDB C API Test Driver
+
+#include <algorithm>
+#include <iostream>
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include "lldb-headers.h"
+
+#include "common.h"
+
+using namespace std;
+using namespace lldb;
+
+void test(SBDebugger &dbg, std::vector<string> args);
+
+int main(int argc, char** argv) {
+ int code = 0;
+
+ SBDebugger::Initialize();
+ SBDebugger dbg = SBDebugger::Create();
+
+ try {
+ if (!dbg.IsValid())
+ throw Exception("invalid debugger");
+ vector<string> args(argv + 1, argv + argc);
+
+ test(dbg, args);
+ } catch (Exception &e) {
+ cout << "ERROR: " << e.what() << endl;
+ code = 1;
+ }
+
+ SBDebugger::Destroy(dbg);
+ return code;
+}
diff --git a/packages/Python/lldbsuite/test/api/multithreaded/inferior.cpp b/packages/Python/lldbsuite/test/api/multithreaded/inferior.cpp
new file mode 100644
index 0000000..9dbb289
--- /dev/null
+++ b/packages/Python/lldbsuite/test/api/multithreaded/inferior.cpp
@@ -0,0 +1,17 @@
+
+#include <iostream>
+
+using namespace std;
+
+int next() {
+ static int i = 0;
+ cout << "incrementing " << i << endl;
+ return ++i;
+}
+
+int main() {
+ int i = 0;
+ while (i < 5)
+ i = next();
+ return 0;
+}
diff --git a/packages/Python/lldbsuite/test/api/multithreaded/listener_test.cpp b/packages/Python/lldbsuite/test/api/multithreaded/listener_test.cpp
new file mode 100644
index 0000000..b20868f
--- /dev/null
+++ b/packages/Python/lldbsuite/test/api/multithreaded/listener_test.cpp
@@ -0,0 +1,74 @@
+// LLDB test snippet that registers a listener with a process that hits
+// a breakpoint.
+
+#include <atomic>
+#include <iostream>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include "lldb-headers.h"
+#include "common.h"
+
+using namespace lldb;
+using namespace std;
+
+void listener_func();
+void check_listener(SBDebugger &dbg);
+
+// Listener thread and related variables
+atomic<bool> g_done;
+SBListener g_listener("test-listener");
+thread g_listener_thread;
+
+void shutdown_listener() {
+ g_done.store(true);
+ if (g_listener_thread.joinable())
+ g_listener_thread.join();
+}
+
+void test(SBDebugger &dbg, std::vector<string> args) {
+ try {
+ g_done.store(false);
+ SBTarget target = dbg.CreateTarget(args.at(0).c_str());
+ if (!target.IsValid()) throw Exception("invalid target");
+
+ SBBreakpoint breakpoint = target.BreakpointCreateByName("next");
+ if (!breakpoint.IsValid()) throw Exception("invalid breakpoint");
+
+ std::unique_ptr<char> working_dir(get_working_dir());
+
+ SBError error;
+ SBProcess process = target.Launch(g_listener,
+ 0, 0, 0, 0, 0,
+ working_dir.get(),
+ 0,
+ false,
+ error);
+ if (!error.Success())
+ throw Exception("Error launching process.");
+
+ /* FIXME: the approach below deadlocks
+ SBProcess process = target.LaunchSimple (0, 0, working_dir.get());
+
+ // get debugger listener (which is attached to process by default)
+ g_listener = dbg.GetListener();
+ */
+
+ // FIXME: because a listener is attached to the process at launch-time,
+ // registering the listener below results in two listeners being attached,
+ // which is not supported by LLDB.
+ // register listener
+ // process.GetBroadcaster().AddListener(g_listener,
+ // SBProcess::eBroadcastBitStateChanged);
+
+ // start listener thread
+ g_listener_thread = thread(listener_func);
+ check_listener(dbg);
+
+ } catch (Exception &e) {
+ shutdown_listener();
+ throw e;
+ }
+ shutdown_listener();
+}
diff --git a/packages/Python/lldbsuite/test/api/multithreaded/lldb-headers.h b/packages/Python/lldbsuite/test/api/multithreaded/lldb-headers.h
new file mode 100644
index 0000000..da0914b
--- /dev/null
+++ b/packages/Python/lldbsuite/test/api/multithreaded/lldb-headers.h
@@ -0,0 +1,11 @@
+
+#ifndef LLDB_HEADERS_H
+#define LLDB_HEADERS_H
+
+#ifdef __APPLE__
+#include <LLDB/LLDB.h>
+#else
+#include "lldb/API/LLDB.h"
+#endif
+
+#endif // LLDB_HEADERS_H
diff --git a/packages/Python/lldbsuite/test/api/multithreaded/test_breakpoint_callback.cpp b/packages/Python/lldbsuite/test/api/multithreaded/test_breakpoint_callback.cpp
new file mode 100644
index 0000000..def31f8
--- /dev/null
+++ b/packages/Python/lldbsuite/test/api/multithreaded/test_breakpoint_callback.cpp
@@ -0,0 +1,49 @@
+
+// LLDB C++ API Test: verify that the function registered with
+// SBBreakpoint.SetCallback() is invoked when a breakpoint is hit.
+
+#include <mutex>
+#include <iostream>
+#include <vector>
+#include <string>
+
+#include "lldb-headers.h"
+
+#include "common.h"
+
+using namespace std;
+using namespace lldb;
+
+mutex g_mutex;
+condition_variable g_condition;
+int g_breakpoint_hit_count = 0;
+
+bool BPCallback (void *baton,
+ SBProcess &process,
+ SBThread &thread,
+ SBBreakpointLocation &location) {
+ lock_guard<mutex> lock(g_mutex);
+ g_breakpoint_hit_count += 1;
+ g_condition.notify_all();
+ return true;
+}
+
+void test(SBDebugger &dbg, vector<string> args) {
+ dbg.SetAsync(false);
+ SBTarget target = dbg.CreateTarget(args.at(0).c_str());
+ if (!target.IsValid()) throw Exception("invalid target");
+
+ SBBreakpoint breakpoint = target.BreakpointCreateByName("next");
+ if (!breakpoint.IsValid()) throw Exception("invalid breakpoint");
+ breakpoint.SetCallback(BPCallback, 0);
+
+ std::unique_ptr<char> working_dir(get_working_dir());
+ SBProcess process = target.LaunchSimple (0, 0, working_dir.get());
+
+ {
+ unique_lock<mutex> lock(g_mutex);
+ g_condition.wait_for(lock, chrono::seconds(5));
+ if (g_breakpoint_hit_count != 1)
+ throw Exception("Breakpoint hit count expected to be 1");
+ }
+}
diff --git a/packages/Python/lldbsuite/test/api/multithreaded/test_listener_event_description.cpp b/packages/Python/lldbsuite/test/api/multithreaded/test_listener_event_description.cpp
new file mode 100644
index 0000000..0d7844d
--- /dev/null
+++ b/packages/Python/lldbsuite/test/api/multithreaded/test_listener_event_description.cpp
@@ -0,0 +1,97 @@
+
+// LLDB C++ API Test: verify the event description that is received by an
+// SBListener object registered with a process with a breakpoint.
+
+#include <atomic>
+#include <array>
+#include <iostream>
+#include <string>
+#include <thread>
+
+#include "lldb-headers.h"
+
+#include "common.h"
+
+using namespace lldb;
+using namespace std;
+
+// listener thread control
+extern atomic<bool> g_done;
+extern SBListener g_listener;
+
+multithreaded_queue<string> g_event_descriptions;
+string g_error_desc;
+
+void listener_func() {
+ while (!g_done) {
+ SBEvent event;
+ bool got_event = g_listener.WaitForEvent(1, event);
+
+ if (got_event) {
+ if (!event.IsValid())
+ throw Exception("event is not valid in listener thread");
+
+ SBStream description;
+ event.GetDescription(description);
+ string str(description.GetData());
+ g_event_descriptions.push(str);
+ }
+ }
+}
+
+bool check_state(string &state, string &desc, bool got_description)
+{
+ g_error_desc.clear();
+
+ if(!got_description)
+ {
+ g_error_desc.append("Did not get expected event description");
+ return false;
+ }
+
+ if (desc.find("state-changed") == desc.npos)
+ g_error_desc.append("Event description incorrect: missing 'state-changed' ");
+
+ if (desc.find("pid = ") == desc.npos)
+ g_error_desc.append("Event description incorrect: missing process pid ");
+
+ string state_search_str = "state = " + state;
+ if (desc.find(state_search_str) == desc.npos)
+ {
+ string errString = ("Event description incorrect: expected state "
+ + state
+ + " but desc was "
+ + desc);
+ g_error_desc.append(errString);
+ }
+
+ if (g_error_desc.length() > 0)
+ return false;
+
+ cout << "check_state: " << state << " OK\n";
+ return true;
+}
+
+void check_listener(SBDebugger &dbg)
+{
+ bool got_description;
+ string state;
+
+ // check for "launching" state, this may or may not be present
+ string desc = g_event_descriptions.pop(5, got_description);
+ state = "launching";
+ if (check_state(state, desc, got_description))
+ {
+ // found a 'launching' state, pop next one from queue
+ desc = g_event_descriptions.pop(5, got_description);
+ }
+
+ state = "running";
+ if( !check_state(state, desc, got_description) )
+ throw Exception(g_error_desc);
+
+ desc = g_event_descriptions.pop(5, got_description);
+ state = "stopped";
+ if( !check_state(state, desc, got_description) )
+ throw Exception(g_error_desc);
+}
diff --git a/packages/Python/lldbsuite/test/api/multithreaded/test_listener_event_process_state.cpp b/packages/Python/lldbsuite/test/api/multithreaded/test_listener_event_process_state.cpp
new file mode 100644
index 0000000..0a698d1
--- /dev/null
+++ b/packages/Python/lldbsuite/test/api/multithreaded/test_listener_event_process_state.cpp
@@ -0,0 +1,68 @@
+
+// LLDB C++ API Test: verify the event description as obtained by calling
+// SBEvent::GetCStringFromEvent that is received by an
+// SBListener object registered with a process with a breakpoint.
+
+#include <atomic>
+#include <iostream>
+#include <string>
+#include <thread>
+
+#include "lldb-headers.h"
+
+#include "common.h"
+
+using namespace lldb;
+using namespace std;
+
+// listener thread control
+extern atomic<bool> g_done;
+
+multithreaded_queue<string> g_thread_descriptions;
+multithreaded_queue<string> g_frame_functions;
+
+extern SBListener g_listener;
+
+void listener_func() {
+ while (!g_done) {
+ SBEvent event;
+ bool got_event = g_listener.WaitForEvent(1, event);
+ if (got_event) {
+ if (!event.IsValid())
+ throw Exception("event is not valid in listener thread");
+
+ // send process description
+ SBProcess process = SBProcess::GetProcessFromEvent(event);
+ SBStream description;
+
+ for (int i = 0; i < process.GetNumThreads(); ++i) {
+ // send each thread description
+ description.Clear();
+ SBThread thread = process.GetThreadAtIndex(i);
+ thread.GetDescription(description);
+ g_thread_descriptions.push(description.GetData());
+
+ // send each frame function name
+ uint32_t num_frames = thread.GetNumFrames();
+ for(int j = 0; j < num_frames; ++j) {
+ const char* function_name = thread.GetFrameAtIndex(j).GetSymbol().GetName();
+ if (function_name)
+ g_frame_functions.push(function_name);
+ }
+ }
+ }
+ }
+}
+
+void check_listener(SBDebugger &dbg) {
+ // check thread description
+ bool got_description = false;
+ string desc = g_thread_descriptions.pop(5, got_description);
+ if (!got_description)
+ throw Exception("Expected at least one thread description string");
+
+ // check at least one frame has a function name
+ desc = g_frame_functions.pop(5, got_description);
+ if (!got_description)
+ throw Exception("Expected at least one frame function name string");
+}
diff --git a/packages/Python/lldbsuite/test/api/multithreaded/test_listener_resume.cpp b/packages/Python/lldbsuite/test/api/multithreaded/test_listener_resume.cpp
new file mode 100644
index 0000000..8cf786b
--- /dev/null
+++ b/packages/Python/lldbsuite/test/api/multithreaded/test_listener_resume.cpp
@@ -0,0 +1,53 @@
+
+// LLDB C++ API Test: verify the event description as obtained by calling
+// SBEvent::GetCStringFromEvent that is received by an
+// SBListener object registered with a process with a breakpoint.
+
+#include <atomic>
+#include <iostream>
+#include <string>
+#include <thread>
+
+#include "lldb-headers.h"
+
+#include "common.h"
+
+using namespace lldb;
+using namespace std;
+
+// listener thread control
+extern atomic<bool> g_done;
+
+// used by listener thread to communicate a successful process continue command
+// back to the checking thread.
+
+multithreaded_queue<bool> g_process_started;
+
+extern SBListener g_listener;
+
+void listener_func() {
+ while (!g_done) {
+ SBEvent event;
+ bool got_event = g_listener.WaitForEvent(1, event);
+ if (got_event) {
+ if (!event.IsValid())
+ throw Exception("event is not valid in listener thread");
+
+ SBProcess process = SBProcess::GetProcessFromEvent(event);
+ if (process.GetState() == eStateStopped) {
+ SBError error = process.Continue();
+ if (!error.Success())
+ throw Exception(string("Cannot continue process from listener thread: ")
+ + error.GetCString());
+ g_process_started.push(true);
+ }
+ }
+ }
+}
+
+void check_listener(SBDebugger &dbg) {
+ bool got_message = false;
+ while (!got_message)
+ g_process_started.pop(5, got_message);
+ g_done = true;
+}
OpenPOWER on IntegriCloud