summaryrefslogtreecommitdiffstats
path: root/packages/Python/lldbsuite/test/api
diff options
context:
space:
mode:
Diffstat (limited to 'packages/Python/lldbsuite/test/api')
-rw-r--r--packages/Python/lldbsuite/test/api/check_public_api_headers/Makefile5
-rw-r--r--packages/Python/lldbsuite/test/api/check_public_api_headers/TestPublicAPIHeaders.py88
-rw-r--r--packages/Python/lldbsuite/test/api/check_public_api_headers/main.cpp.template24
-rw-r--r--packages/Python/lldbsuite/test/api/multiple-debuggers/.categories1
-rw-r--r--packages/Python/lldbsuite/test/api/multiple-debuggers/Makefile8
-rw-r--r--packages/Python/lldbsuite/test/api/multiple-debuggers/TestMultipleDebuggers.py44
-rw-r--r--packages/Python/lldbsuite/test/api/multiple-debuggers/multi-process-driver.cpp283
-rw-r--r--packages/Python/lldbsuite/test/api/multiple-debuggers/testprog.cpp12
-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
19 files changed, 1040 insertions, 0 deletions
diff --git a/packages/Python/lldbsuite/test/api/check_public_api_headers/Makefile b/packages/Python/lldbsuite/test/api/check_public_api_headers/Makefile
new file mode 100644
index 0000000..8a7102e
--- /dev/null
+++ b/packages/Python/lldbsuite/test/api/check_public_api_headers/Makefile
@@ -0,0 +1,5 @@
+LEVEL = ../../make
+
+CXX_SOURCES := main.cpp
+
+include $(LEVEL)/Makefile.rules
diff --git a/packages/Python/lldbsuite/test/api/check_public_api_headers/TestPublicAPIHeaders.py b/packages/Python/lldbsuite/test/api/check_public_api_headers/TestPublicAPIHeaders.py
new file mode 100644
index 0000000..0d0507f
--- /dev/null
+++ b/packages/Python/lldbsuite/test/api/check_public_api_headers/TestPublicAPIHeaders.py
@@ -0,0 +1,88 @@
+"""Test the integrity of the lldb public api directory containing SB*.h headers.
+
+There should be nothing unwanted there and a simpe main.cpp which includes SB*.h
+should compile and link with the LLDB framework."""
+
+from __future__ import print_function
+
+
+
+import os, re
+from lldbsuite.test.lldbtest import *
+import lldbsuite.test.lldbutil as lldbutil
+
+class SBDirCheckerCase(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+
+ def setUp(self):
+ TestBase.setUp(self)
+ self.template = 'main.cpp.template'
+ self.source = 'main.cpp'
+ self.exe_name = 'a.out'
+
+ @skipIfNoSBHeaders
+ def test_sb_api_directory(self):
+ """Test the SB API directory and make sure there's no unwanted stuff."""
+
+ # Only proceed if this is an Apple OS, "x86_64", and local platform.
+ if not (self.platformIsDarwin() and self.getArchitecture() == "x86_64"):
+ self.skipTest("This test is only for LLDB.framework built 64-bit")
+ if self.getArchitecture() == "i386":
+ self.skipTest("LLDB is 64-bit and cannot be linked to 32-bit test program.")
+
+ # Generate main.cpp, build it, and execute.
+ self.generate_main_cpp()
+ self.buildDriver(self.source, self.exe_name)
+ self.sanity_check_executable(self.exe_name)
+
+ def generate_main_cpp(self):
+ """Generate main.cpp from main.cpp.template."""
+ temp = os.path.join(os.getcwd(), self.template)
+ with open(temp, 'r') as f:
+ content = f.read()
+
+ public_api_dir = os.path.join(os.environ["LLDB_SRC"], "include", "lldb", "API")
+
+ # Look under the include/lldb/API directory and add #include statements
+ # for all the SB API headers.
+ public_headers = os.listdir(public_api_dir)
+ # For different platforms, the include statement can vary.
+ if self.platformIsDarwin():
+ include_stmt = "'#include <%s>' % os.path.join('LLDB', header)"
+ if self.getPlatform() == "freebsd" or self.getPlatform() == "linux" or os.environ.get('LLDB_BUILD_TYPE') == 'Makefile':
+ include_stmt = "'#include <%s>' % os.path.join(public_api_dir, header)"
+ list = [eval(include_stmt) for header in public_headers if (header.startswith("SB") and
+ header.endswith(".h"))]
+ includes = '\n'.join(list)
+ new_content = content.replace('%include_SB_APIs%', includes)
+ src = os.path.join(os.getcwd(), self.source)
+ with open(src, 'w') as f:
+ f.write(new_content)
+
+ # The main.cpp has been generated, add a teardown hook to remove it.
+ self.addTearDownHook(lambda: os.remove(src))
+
+ def sanity_check_executable(self, exe_name):
+ """Sanity check executable compiled from the auto-generated program."""
+ exe = os.path.join(os.getcwd(), exe_name)
+ self.runCmd("file %s" % exe, CURRENT_EXECUTABLE_SET)
+
+ self.line_to_break = line_number(self.source, '// Set breakpoint here.')
+
+ env_cmd = "settings set target.env-vars %s=%s" %(self.dylibPath, self.getLLDBLibraryEnvVal())
+ if self.TraceOn():
+ print("Set environment to: ", env_cmd)
+ self.runCmd(env_cmd)
+ self.addTearDownHook(lambda: self.dbg.HandleCommand("settings remove target.env-vars %s" % self.dylibPath))
+
+ lldbutil.run_break_set_by_file_and_line (self, self.source, self.line_to_break, num_expected_locations = -1)
+
+ 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'])
+
+ self.runCmd('frame variable')
diff --git a/packages/Python/lldbsuite/test/api/check_public_api_headers/main.cpp.template b/packages/Python/lldbsuite/test/api/check_public_api_headers/main.cpp.template
new file mode 100644
index 0000000..ed754ad
--- /dev/null
+++ b/packages/Python/lldbsuite/test/api/check_public_api_headers/main.cpp.template
@@ -0,0 +1,24 @@
+//===-- main.cpp ------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <stdio.h>
+%include_SB_APIs%
+
+using namespace lldb;
+int
+main(int argc, char const *argv[])
+{
+ SBDebugger::Initialize();
+ SBDebugger dbg = SBDebugger::Create();
+
+ printf("Hello SBDebugger %llu\n", dbg.GetID()); // Set breakpoint here.
+
+ SBDebugger::Terminate();
+ return 0;
+}
diff --git a/packages/Python/lldbsuite/test/api/multiple-debuggers/.categories b/packages/Python/lldbsuite/test/api/multiple-debuggers/.categories
new file mode 100644
index 0000000..6e70196
--- /dev/null
+++ b/packages/Python/lldbsuite/test/api/multiple-debuggers/.categories
@@ -0,0 +1 @@
+stresstest
diff --git a/packages/Python/lldbsuite/test/api/multiple-debuggers/Makefile b/packages/Python/lldbsuite/test/api/multiple-debuggers/Makefile
new file mode 100644
index 0000000..08f8850
--- /dev/null
+++ b/packages/Python/lldbsuite/test/api/multiple-debuggers/Makefile
@@ -0,0 +1,8 @@
+LEVEL = ../../make
+
+MAKE_DSYM := NO
+
+ENABLE_THREADS := YES
+CXX_SOURCES := multi-process-driver.cpp testprog.cpp
+
+include $(LEVEL)/Makefile.rules
diff --git a/packages/Python/lldbsuite/test/api/multiple-debuggers/TestMultipleDebuggers.py b/packages/Python/lldbsuite/test/api/multiple-debuggers/TestMultipleDebuggers.py
new file mode 100644
index 0000000..67dca22
--- /dev/null
+++ b/packages/Python/lldbsuite/test/api/multiple-debuggers/TestMultipleDebuggers.py
@@ -0,0 +1,44 @@
+"""Test the lldb public C++ api when doing multiple debug sessions simultaneously."""
+
+from __future__ import print_function
+
+
+
+import os, re
+from lldbsuite.test.lldbtest import *
+import lldbsuite.test.lldbutil as lldbutil
+import lldb
+import subprocess
+
+class TestMultipleSimultaneousDebuggers(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+
+ @skipIfi386
+ @skipIfNoSBHeaders
+ @expectedFailureFreeBSD("llvm.org/pr20282")
+ @expectedFailureLinux("llvm.org/pr20282")
+ @expectedFailureWindows # Test crashes
+ @expectedFlakeyDarwin()
+ def test_multiple_debuggers(self):
+ env = {self.dylibPath : self.getLLDBLibraryEnvVal()}
+
+ self.driver_exe = os.path.join(os.getcwd(), "multi-process-driver")
+ self.buildDriver('multi-process-driver.cpp', self.driver_exe)
+ self.addTearDownHook(lambda: os.remove(self.driver_exe))
+ self.signBinary(self.driver_exe)
+
+ self.inferior_exe = os.path.join(os.getcwd(), "testprog")
+ self.buildDriver('testprog.cpp', self.inferior_exe)
+ self.addTearDownHook(lambda: os.remove(self.inferior_exe))
+
+# check_call will raise a CalledProcessError if multi-process-driver doesn't return
+# exit code 0 to indicate success. We can let this exception go - the test harness
+# will recognize it as a test failure.
+
+ if self.TraceOn():
+ print("Running test %s" % self.driver_exe)
+ check_call([self.driver_exe, self.inferior_exe], env=env)
+ else:
+ with open(os.devnull, 'w') as fnull:
+ check_call([self.driver_exe, self.inferior_exe], env=env, stdout=fnull, stderr=fnull)
diff --git a/packages/Python/lldbsuite/test/api/multiple-debuggers/multi-process-driver.cpp b/packages/Python/lldbsuite/test/api/multiple-debuggers/multi-process-driver.cpp
new file mode 100644
index 0000000..1c2689f
--- /dev/null
+++ b/packages/Python/lldbsuite/test/api/multiple-debuggers/multi-process-driver.cpp
@@ -0,0 +1,283 @@
+
+// This program creates NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS of pthreads,
+// creates an lldb Debugger on each thread, creates targets, inserts two
+// breakpoints, runs to the first breakpoint, backtraces, runs to the second
+// breakpoint, backtraces, kills the inferior process, closes down the
+// debugger.
+
+// The main thread keeps track of which pthreads have completed and which
+// pthreads have completed successfully, and exits when all pthreads have
+// completed successfully, or our time limit has been exceeded.
+
+// This test file helps to uncover race conditions and locking mistakes
+// that are hit when lldb is being used to debug multiple processes
+// simultaneously.
+
+#include <stdio.h>
+#include <stdlib.h>
+
+
+#include "lldb/API/LLDB.h"
+#include "lldb/API/SBCommandInterpreter.h"
+#include "lldb/API/SBCommandReturnObject.h"
+#include "lldb/API/SBDebugger.h"
+
+#include <chrono>
+#include <thread>
+
+#define NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS 20
+
+#define DEBUG 0
+
+using namespace lldb;
+
+bool *completed_threads_array = 0;
+bool *successful_threads_array = 0;
+
+const char *inferior_process_name = "testprog";
+
+bool
+wait_for_stop_event (SBProcess process, SBListener listener)
+{
+ bool stopped = false;
+ while (!stopped)
+ {
+ SBEvent event;
+ bool waitfor_ret = listener.WaitForEvent (2, event);
+ if (event.GetType() == SBProcess::eBroadcastBitStateChanged)
+ {
+ if (process.GetState() == StateType::eStateStopped
+ || process.GetState() == StateType::eStateCrashed
+ || process.GetState() == StateType::eStateDetached
+ || process.GetState() == StateType::eStateExited)
+ {
+ stopped = true;
+ }
+ }
+ }
+ return stopped;
+}
+
+bool
+walk_stack_to_main (SBThread thread)
+{
+ if (thread.IsValid() == 0)
+ {
+ return false;
+ }
+
+ bool found_main = false;
+ uint32_t curr_frame = 0;
+ const uint32_t framecount = thread.GetNumFrames();
+ while (!found_main && curr_frame < framecount)
+ {
+ SBFrame frame = thread.GetFrameAtIndex (curr_frame);
+ if (strcmp (frame.GetFunctionName(), "main") == 0)
+ {
+ found_main = true;
+ break;
+ }
+ curr_frame += 1;
+ }
+ return found_main;
+}
+
+void *do_one_debugger (void *in)
+{
+ uint64_t threadnum = (uint64_t) in;
+
+#if defined (__APPLE__)
+ char *threadname;
+ asprintf (&threadname, "thread #%lld", threadnum);
+ pthread_setname_np (threadname);
+ free (threadname);
+#endif
+
+#if DEBUG == 1
+ printf ("#%lld: Starting debug session\n", threadnum);
+#endif
+
+ SBDebugger debugger = lldb::SBDebugger::Create (false);
+ if (debugger.IsValid ())
+ {
+ debugger.SetAsync (true);
+ SBTarget target = debugger.CreateTargetWithFileAndArch(inferior_process_name, "x86_64");
+ SBCommandInterpreter command_interp = debugger.GetCommandInterpreter();
+ if (target.IsValid())
+ {
+ SBBreakpoint bar_br = target.BreakpointCreateByName ("bar", "testprog");
+ if (!bar_br.IsValid())
+ {
+ printf ("#%lld: failed to set breakpoint on bar, exiting.\n", threadnum);
+ exit (1);
+ }
+ SBBreakpoint foo_br = target.BreakpointCreateByName ("foo", "testprog");
+ if (!foo_br.IsValid())
+ {
+ printf ("#%lld: Failed to set breakpoint on foo()\n", threadnum);
+ }
+
+ SBLaunchInfo launch_info (NULL);
+ SBError error;
+ SBProcess process = target.Launch (launch_info, error);
+ if (process.IsValid())
+ {
+ SBListener listener = debugger.GetListener();
+ SBBroadcaster broadcaster = process.GetBroadcaster();
+ uint32_t rc = broadcaster.AddListener (listener, SBProcess::eBroadcastBitStateChanged);
+ if (rc == 0)
+ {
+ printf ("adding listener failed\n");
+ exit (1);
+ }
+
+ wait_for_stop_event (process, listener);
+
+ if (!walk_stack_to_main (process.GetThreadAtIndex(0)))
+ {
+ printf ("#%lld: backtrace while @ foo() failed\n", threadnum);
+ completed_threads_array[threadnum] = true;
+ return (void *) 1;
+ }
+
+ if (strcmp (process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetFunctionName(), "foo") != 0)
+ {
+#if DEBUG == 1
+ printf ("#%lld: First breakpoint did not stop at foo(), instead stopped at '%s'\n", threadnum, process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetFunctionName());
+#endif
+ completed_threads_array[threadnum] = true;
+ return (void*) 1;
+ }
+
+ process.Continue();
+
+ wait_for_stop_event (process, listener);
+
+ if (process.GetState() == StateType::eStateExited)
+ {
+ printf ("#%lld: Process exited\n", threadnum);
+ completed_threads_array[threadnum] = true;
+ return (void *) 1;
+ }
+
+
+ if (!walk_stack_to_main (process.GetThreadAtIndex(0)))
+ {
+ printf ("#%lld: backtrace while @ bar() failed\n", threadnum);
+ completed_threads_array[threadnum] = true;
+ return (void *) 1;
+ }
+
+ if (strcmp (process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetFunctionName(), "bar") != 0)
+ {
+ printf ("#%lld: First breakpoint did not stop at bar()\n", threadnum);
+ completed_threads_array[threadnum] = true;
+ return (void*) 1;
+ }
+
+ process.Kill();
+
+ wait_for_stop_event (process, listener);
+
+ SBDebugger::Destroy(debugger);
+
+#if DEBUG == 1
+ printf ("#%lld: All good!\n", threadnum);
+#endif
+ successful_threads_array[threadnum] = true;
+ completed_threads_array[threadnum] = true;
+ return (void*) 0;
+ }
+ else
+ {
+ printf("#%lld: process failed to launch\n", threadnum);
+ successful_threads_array[threadnum] = false;
+ completed_threads_array[threadnum] = true;
+ return (void*) 0;
+ }
+ }
+ else
+ {
+ printf ("#%lld: did not get valid target\n", threadnum);
+ successful_threads_array[threadnum] = false;
+ completed_threads_array[threadnum] = true;
+ return (void*) 0;
+ }
+ }
+ else
+ {
+ printf ("#%lld: did not get debugger\n", threadnum);
+ successful_threads_array[threadnum] = false;
+ completed_threads_array[threadnum] = true;
+ return (void*) 0;
+ }
+ completed_threads_array[threadnum] = true;
+ return (void*) 1;
+}
+
+int main (int argc, char **argv)
+{
+ SBDebugger::Initialize();
+
+ completed_threads_array = (bool *) malloc (sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
+ memset (completed_threads_array, 0, sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
+ successful_threads_array = (bool *) malloc (sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
+ memset (successful_threads_array, 0, sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
+
+ if (argc > 1 && argv[1] != NULL)
+ {
+ inferior_process_name = argv[1];
+ }
+
+ std::thread threads[NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS];
+ for (uint64_t i = 0; i< NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS; i++)
+ {
+ threads[i] = std::move(std::thread(do_one_debugger, (void*)i));
+ }
+
+
+ int max_time_to_wait = 20; // 20 iterations, or 60 seconds
+ int iter = 0;
+ while (1)
+ {
+ std::this_thread::sleep_for(std::chrono::seconds(3));
+ bool all_done = true;
+ int successful_threads = 0;
+ int total_completed_threads = 0;
+ for (uint64_t i = 0; i < NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS; i++)
+ {
+ if (successful_threads_array[i] == true)
+ successful_threads++;
+ if (completed_threads_array[i] == true)
+ total_completed_threads++;
+ if (completed_threads_array[i] == false)
+ {
+ all_done = false;
+ }
+ }
+ if (all_done)
+ {
+#if DEBUG == 1
+ printf ("All threads completed.\n");
+ printf ("%d threads completed successfully out of %d\n", successful_threads, NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
+#endif
+ SBDebugger::Terminate();
+ exit(0);
+ }
+ else
+ {
+#if DEBUG == 1
+ printf ("%d threads completed so far (%d successfully), out of %d\n", total_completed_threads, successful_threads, NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
+#endif
+ }
+ if (iter++ == max_time_to_wait)
+ {
+ printf ("reached maximum timeout but only %d threads have completed so far (%d successfully), out of %d. Exiting.\n", total_completed_threads, successful_threads, NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
+ break;
+ }
+ }
+
+
+ SBDebugger::Terminate();
+ exit (1);
+}
diff --git a/packages/Python/lldbsuite/test/api/multiple-debuggers/testprog.cpp b/packages/Python/lldbsuite/test/api/multiple-debuggers/testprog.cpp
new file mode 100644
index 0000000..c9d1ea1
--- /dev/null
+++ b/packages/Python/lldbsuite/test/api/multiple-debuggers/testprog.cpp
@@ -0,0 +1,12 @@
+int bar ()
+{
+ return 5;
+}
+int foo ()
+{
+ return bar() + 5;
+}
+int main ()
+{
+ return foo();
+}
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