diff options
Diffstat (limited to 'packages/Python/lldbsuite/test/api/multithreaded')
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 0000000000000..37323ea78190b --- /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 0000000000000..0b405091ebd1c --- /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 0000000000000..dad8bba07a3f3 --- /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 0000000000000..fa0c48e0ecfbe --- /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 0000000000000..9dbb289f98be6 --- /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 0000000000000..b20868ff94f4a --- /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 0000000000000..da0914b3b0716 --- /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 0000000000000..def31f82b0c7a --- /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 0000000000000..0d7844dce6d09 --- /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 0000000000000..0a698d1081d47 --- /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 0000000000000..8cf786b116033 --- /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; +} | 
