[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