[Lldb-commits] [lldb] r181819 - Adding C++ tests that drive LLDB from multiple threads
Daniel Malea
daniel.malea at intel.com
Tue May 14 12:13:26 PDT 2013
Author: dmalea
Date: Tue May 14 14:13:25 2013
New Revision: 181819
URL: http://llvm.org/viewvc/llvm-project?rev=181819&view=rev
Log:
Adding C++ tests that drive LLDB from multiple threads
- test_breakpoint_callback -- filed llvm.org/pr-16000
- test_listener_resume -- resume a process from a thread waiting on SBListener
- test_listener_event_description -- SBEvent description from SBListener thread
- test_listener_event_process -- query process/thread/stack info from SBListener thread
Added:
lldb/trunk/test/api/multithreaded/
lldb/trunk/test/api/multithreaded/Makefile
lldb/trunk/test/api/multithreaded/TestMultithreaded.py
lldb/trunk/test/api/multithreaded/common.h
lldb/trunk/test/api/multithreaded/driver.cpp
lldb/trunk/test/api/multithreaded/inferior.cpp
lldb/trunk/test/api/multithreaded/listener_test.cpp
lldb/trunk/test/api/multithreaded/lldb-headers.h
lldb/trunk/test/api/multithreaded/test_breakpoint_callback.cpp
lldb/trunk/test/api/multithreaded/test_listener_event_description.cpp
lldb/trunk/test/api/multithreaded/test_listener_event_process_state.cpp
lldb/trunk/test/api/multithreaded/test_listener_resume.cpp
Added: lldb/trunk/test/api/multithreaded/Makefile
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/api/multithreaded/Makefile?rev=181819&view=auto
==============================================================================
--- lldb/trunk/test/api/multithreaded/Makefile (added)
+++ lldb/trunk/test/api/multithreaded/Makefile Tue May 14 14:13:25 2013
@@ -0,0 +1,7 @@
+LEVEL = ../../make
+
+CXX_SOURCES := main.cpp
+
+clean: OBJECTS+=*.d.* *.d *.o *.pyc *.dSYM
+
+include $(LEVEL)/Makefile.rules
Added: lldb/trunk/test/api/multithreaded/TestMultithreaded.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/api/multithreaded/TestMultithreaded.py?rev=181819&view=auto
==============================================================================
--- lldb/trunk/test/api/multithreaded/TestMultithreaded.py (added)
+++ lldb/trunk/test/api/multithreaded/TestMultithreaded.py Tue May 14 14:13:25 2013
@@ -0,0 +1,73 @@
+"""Test the lldb public C++ api breakpoint callbacks. """
+
+import os, re, StringIO
+import unittest2
+from lldbtest import *
+import lldbutil
+import subprocess
+
+class SBBreakpointCallbackCase(TestBase):
+
+ mydir = os.path.join("api", "multithreaded")
+
+ def setUp(self):
+ TestBase.setUp(self)
+ self.lib_dir = os.environ["LLDB_LIB_DIR"]
+ self.inferior = 'inferior_program'
+ if self.getArchitecture() != "i386":
+ self.buildProgram('inferior.cpp', self.inferior)
+ self.addTearDownHook(lambda: os.remove(self.inferior))
+
+ @unittest2.expectedFailure # llvm.org/pr-1600: SBBreakpoint.SetCallback() does nothing
+ @skipIfi386
+ 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')
+
+ @skipIfi386
+ 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
+
+ @skipIfi386
+ 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
+
+
+ @skipIfi386
+ 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 """
+ self.buildDriver(sources, test_name)
+ self.addTearDownHook(lambda: os.remove(test_name))
+
+ exe = [os.path.join(os.getcwd(), test_name), self.inferior]
+
+ if self.TraceOn():
+ print "Running test %s" % " ".join(exe)
+
+ check_call(exe, env={self.dylibPath : self.getLLDBLibraryEnvVal()})
+
+
+
+ def build_program(self, sources, program):
+ return self.buildDriver(sources, program)
+
+if __name__ == '__main__':
+ import atexit
+ lldb.SBDebugger.Initialize()
+ atexit.register(lambda: lldb.SBDebugger.Terminate())
+ unittest2.main()
Added: lldb/trunk/test/api/multithreaded/common.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/api/multithreaded/common.h?rev=181819&view=auto
==============================================================================
--- lldb/trunk/test/api/multithreaded/common.h (added)
+++ lldb/trunk/test/api/multithreaded/common.h Tue May 14 14:13:25 2013
@@ -0,0 +1,67 @@
+#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) {}
+ 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 on Linux/Darwin
+inline char* get_working_dir() {
+#ifdef __APPLE__
+ return getwd(0);
+#else
+ return get_current_dir_name();
+#endif
+}
+
+#endif // LLDB_TEST_API_COMMON_H
Added: lldb/trunk/test/api/multithreaded/driver.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/api/multithreaded/driver.cpp?rev=181819&view=auto
==============================================================================
--- lldb/trunk/test/api/multithreaded/driver.cpp (added)
+++ lldb/trunk/test/api/multithreaded/driver.cpp Tue May 14 14:13:25 2013
@@ -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;
+}
Added: lldb/trunk/test/api/multithreaded/inferior.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/api/multithreaded/inferior.cpp?rev=181819&view=auto
==============================================================================
--- lldb/trunk/test/api/multithreaded/inferior.cpp (added)
+++ lldb/trunk/test/api/multithreaded/inferior.cpp Tue May 14 14:13:25 2013
@@ -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;
+}
Added: lldb/trunk/test/api/multithreaded/listener_test.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/api/multithreaded/listener_test.cpp?rev=181819&view=auto
==============================================================================
--- lldb/trunk/test/api/multithreaded/listener_test.cpp (added)
+++ lldb/trunk/test/api/multithreaded/listener_test.cpp Tue May 14 14:13:25 2013
@@ -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();
+}
Added: lldb/trunk/test/api/multithreaded/lldb-headers.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/api/multithreaded/lldb-headers.h?rev=181819&view=auto
==============================================================================
--- lldb/trunk/test/api/multithreaded/lldb-headers.h (added)
+++ lldb/trunk/test/api/multithreaded/lldb-headers.h Tue May 14 14:13:25 2013
@@ -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
Added: lldb/trunk/test/api/multithreaded/test_breakpoint_callback.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/api/multithreaded/test_breakpoint_callback.cpp?rev=181819&view=auto
==============================================================================
--- lldb/trunk/test/api/multithreaded/test_breakpoint_callback.cpp (added)
+++ lldb/trunk/test/api/multithreaded/test_breakpoint_callback.cpp Tue May 14 14:13:25 2013
@@ -0,0 +1,48 @@
+
+// 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) {
+ 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");
+ }
+}
Added: lldb/trunk/test/api/multithreaded/test_listener_event_description.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/api/multithreaded/test_listener_event_description.cpp?rev=181819&view=auto
==============================================================================
--- lldb/trunk/test/api/multithreaded/test_listener_event_description.cpp (added)
+++ lldb/trunk/test/api/multithreaded/test_listener_event_description.cpp Tue May 14 14:13:25 2013
@@ -0,0 +1,64 @@
+
+// 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;
+
+multithreaded_queue<string> g_event_descriptions;
+
+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");
+
+ SBStream description;
+ event.GetDescription(description);
+ string str(description.GetData());
+ g_event_descriptions.push(str);
+ }
+ }
+}
+
+void check_listener(SBDebugger &dbg) {
+ array<string, 2> expected_states = {"running", "stopped"};
+ for(string & state : expected_states) {
+ bool got_description = false;
+ string desc = g_event_descriptions.pop(5, got_description);
+
+ if (!got_description)
+ throw Exception("Did not get expected event description");
+
+
+ if (desc.find("state-changed") == desc.npos)
+ throw Exception("Event description incorrect: missing 'state-changed'");
+
+ string state_search_str = "state = " + state;
+ if (desc.find(state_search_str) == desc.npos)
+ throw Exception("Event description incorrect: expected state "
+ + state
+ + " but desc was "
+ + desc);
+
+ if (desc.find("pid = ") == desc.npos)
+ throw Exception("Event description incorrect: missing process pid");
+ }
+}
Added: lldb/trunk/test/api/multithreaded/test_listener_event_process_state.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/api/multithreaded/test_listener_event_process_state.cpp?rev=181819&view=auto
==============================================================================
--- lldb/trunk/test/api/multithreaded/test_listener_event_process_state.cpp (added)
+++ lldb/trunk/test/api/multithreaded/test_listener_event_process_state.cpp Tue May 14 14:13:25 2013
@@ -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).GetFunction().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");
+}
Added: lldb/trunk/test/api/multithreaded/test_listener_resume.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/api/multithreaded/test_listener_resume.cpp?rev=181819&view=auto
==============================================================================
--- lldb/trunk/test/api/multithreaded/test_listener_resume.cpp (added)
+++ lldb/trunk/test/api/multithreaded/test_listener_resume.cpp Tue May 14 14:13:25 2013
@@ -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;
+}
More information about the lldb-commits
mailing list