summaryrefslogtreecommitdiff
path: root/lldb/test/API/functionalities/plugins/python_os_plugin
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/test/API/functionalities/plugins/python_os_plugin')
-rw-r--r--lldb/test/API/functionalities/plugins/python_os_plugin/stepping_plugin_threads/Makefile4
-rw-r--r--lldb/test/API/functionalities/plugins/python_os_plugin/stepping_plugin_threads/TestOSPluginStepping.py113
-rw-r--r--lldb/test/API/functionalities/plugins/python_os_plugin/stepping_plugin_threads/main.cpp55
-rw-r--r--lldb/test/API/functionalities/plugins/python_os_plugin/stepping_plugin_threads/operating_system.py62
4 files changed, 234 insertions, 0 deletions
diff --git a/lldb/test/API/functionalities/plugins/python_os_plugin/stepping_plugin_threads/Makefile b/lldb/test/API/functionalities/plugins/python_os_plugin/stepping_plugin_threads/Makefile
new file mode 100644
index 000000000000..c46619c66234
--- /dev/null
+++ b/lldb/test/API/functionalities/plugins/python_os_plugin/stepping_plugin_threads/Makefile
@@ -0,0 +1,4 @@
+CXX_SOURCES := main.cpp
+ENABLE_THREADS := YES
+
+include Makefile.rules
diff --git a/lldb/test/API/functionalities/plugins/python_os_plugin/stepping_plugin_threads/TestOSPluginStepping.py b/lldb/test/API/functionalities/plugins/python_os_plugin/stepping_plugin_threads/TestOSPluginStepping.py
new file mode 100644
index 000000000000..5bba48364731
--- /dev/null
+++ b/lldb/test/API/functionalities/plugins/python_os_plugin/stepping_plugin_threads/TestOSPluginStepping.py
@@ -0,0 +1,113 @@
+"""
+Test that stepping works even when the OS Plugin doesn't report
+all threads at every stop.
+"""
+
+from __future__ import print_function
+
+
+import os
+import lldb
+from lldbsuite.test.lldbtest import *
+import lldbsuite.test.lldbutil as lldbutil
+
+
+class TestOSPluginStepping(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def test_python_os_plugin(self):
+ """Test that stepping works when the OS Plugin doesn't report all
+ threads at every stop"""
+ self.build()
+ self.main_file = lldb.SBFileSpec('main.cpp')
+ self.run_python_os_step_missing_thread(False)
+
+ def test_python_os_plugin_prune(self):
+ """Test that pruning the unreported PlanStacks works"""
+ self.build()
+ self.main_file = lldb.SBFileSpec('main.cpp')
+ self.run_python_os_step_missing_thread(True)
+
+ def get_os_thread(self):
+ return self.process.GetThreadByID(0x111111111)
+
+ def is_os_thread(self, thread):
+ id = thread.GetID()
+ return id == 0x111111111
+
+ def run_python_os_step_missing_thread(self, do_prune):
+ """Test that the Python operating system plugin works correctly"""
+
+ # Our OS plugin does NOT report all threads:
+ result = self.dbg.HandleCommand("settings set target.experimental.os-plugin-reports-all-threads false")
+
+ python_os_plugin_path = os.path.join(self.getSourceDir(),
+ "operating_system.py")
+ (target, self.process, thread, thread_bkpt) = lldbutil.run_to_source_breakpoint(
+ self, "first stop in thread - do a step out", self.main_file)
+
+ main_bkpt = target.BreakpointCreateBySourceRegex('Stop here and do not make a memory thread for thread_1',
+ self.main_file)
+ self.assertEqual(main_bkpt.GetNumLocations(), 1, "Main breakpoint has one location")
+
+ # There should not be an os thread before we load the plugin:
+ self.assertFalse(self.get_os_thread().IsValid(), "No OS thread before loading plugin")
+
+ # Now load the python OS plug-in which should update the thread list and we should have
+ # an OS plug-in thread overlaying thread_1 with id 0x111111111
+ command = "settings set target.process.python-os-plugin-path '%s'" % python_os_plugin_path
+ self.dbg.HandleCommand(command)
+
+ # Verify our OS plug-in threads showed up
+ os_thread = self.get_os_thread()
+ self.assertTrue(
+ os_thread.IsValid(),
+ "Make sure we added the thread 0x111111111 after we load the python OS plug-in")
+
+ # Now we are going to step-out. This should get interrupted by main_bkpt. We've
+ # set up the OS plugin so at this stop, we have lost the OS thread 0x111111111.
+ # Make sure both of these are true:
+ os_thread.StepOut()
+
+ stopped_threads = lldbutil.get_threads_stopped_at_breakpoint(self.process, main_bkpt)
+ self.assertEqual(len(stopped_threads), 1, "Stopped at main_bkpt")
+ thread = self.process.GetThreadByID(0x111111111)
+ self.assertFalse(thread.IsValid(), "No thread 0x111111111 on second stop.")
+
+ # Make sure we still have the thread plans for this thread:
+ # First, don't show unreported threads, that should fail:
+ command = "thread plan list -t 0x111111111"
+ result = lldb.SBCommandReturnObject()
+ interp = self.dbg.GetCommandInterpreter()
+ interp.HandleCommand(command, result)
+ self.assertFalse(result.Succeeded(), "We found no plans for the unreported thread.")
+ # Now do it again but with the -u flag:
+ command = "thread plan list -u -t 0x111111111"
+ result = lldb.SBCommandReturnObject()
+ interp.HandleCommand(command, result)
+ self.assertTrue(result.Succeeded(), "We found plans for the unreported thread.")
+
+ if do_prune:
+ # Prune the thread plan and continue, and we will run to exit.
+ interp.HandleCommand("thread plan prune 0x111111111", result)
+ self.assertTrue(result.Succeeded(), "Found the plan for 0x111111111 and pruned it")
+
+ # List again, make sure it doesn't work:
+ command = "thread plan list -u -t 0x111111111"
+ interp.HandleCommand(command, result)
+ self.assertFalse(result.Succeeded(), "We still found plans for the unreported thread.")
+
+ self.process.Continue()
+ self.assertEqual(self.process.GetState(), lldb.eStateExited, "We exited.")
+ else:
+ # Now we are going to continue, and when we hit the step-out breakpoint, we will
+ # put the OS plugin thread back, lldb will recover its ThreadPlanStack, and
+ # we will stop with a "step-out" reason.
+ self.process.Continue()
+ os_thread = self.get_os_thread()
+ self.assertTrue(os_thread.IsValid(), "The OS thread is back after continue")
+ self.assertTrue("step out" in os_thread.GetStopDescription(100), "Completed step out plan")
+
+
diff --git a/lldb/test/API/functionalities/plugins/python_os_plugin/stepping_plugin_threads/main.cpp b/lldb/test/API/functionalities/plugins/python_os_plugin/stepping_plugin_threads/main.cpp
new file mode 100644
index 000000000000..6fd6b1c4332c
--- /dev/null
+++ b/lldb/test/API/functionalities/plugins/python_os_plugin/stepping_plugin_threads/main.cpp
@@ -0,0 +1,55 @@
+// This test will present lldb with two threads one of which the test will
+// overlay with an OSPlugin thread. Then we'll do a step out on the thread_1,
+// but arrange to hit a breakpoint in main before the step out completes. At
+// that point we will not report an OS plugin thread for thread_1. Then we'll
+// run again and hit the step out breakpoint. Make sure we haven't deleted
+// that, and recognize it.
+
+#include <condition_variable>
+#include <mutex>
+#include <stdio.h>
+#include <thread>
+
+static int g_value = 0; // I don't have access to the real threads in the
+ // OS Plugin, and I don't want to have to count
+ // StopID's. So I'm using this value to tell me which
+ // stop point the program has reached.
+std::mutex g_mutex;
+std::condition_variable g_cv;
+static int g_condition = 0; // Using this as the variable backing g_cv
+ // to prevent spurious wakeups.
+
+void step_out_of_here() {
+ std::unique_lock<std::mutex> func_lock(g_mutex);
+ // Set a breakpoint:first stop in thread - do a step out.
+ g_condition = 1;
+ g_cv.notify_one();
+ g_cv.wait(func_lock, [&] { return g_condition == 2; });
+}
+
+void *thread_func() {
+ // Do something
+ step_out_of_here();
+
+ // Return
+ return NULL;
+}
+
+int main() {
+ // Lock the mutex so we can block the thread:
+ std::unique_lock<std::mutex> main_lock(g_mutex);
+ // Create the thread
+ std::thread thread_1(thread_func);
+ g_cv.wait(main_lock, [&] { return g_condition == 1; });
+ g_value = 1;
+ g_condition = 2;
+ // Stop here and do not make a memory thread for thread_1.
+ g_cv.notify_one();
+ g_value = 2;
+ main_lock.unlock();
+
+ // Wait for the threads to finish
+ thread_1.join();
+
+ return 0;
+}
diff --git a/lldb/test/API/functionalities/plugins/python_os_plugin/stepping_plugin_threads/operating_system.py b/lldb/test/API/functionalities/plugins/python_os_plugin/stepping_plugin_threads/operating_system.py
new file mode 100644
index 000000000000..ff9a57367a2a
--- /dev/null
+++ b/lldb/test/API/functionalities/plugins/python_os_plugin/stepping_plugin_threads/operating_system.py
@@ -0,0 +1,62 @@
+#!/usr/bin/python
+
+import lldb
+import struct
+
+
+class OperatingSystemPlugIn(object):
+ """Class that provides a OS plugin that along with the particular code in main.cpp
+ emulates the following scenario:
+ a) We stop in an OS Plugin created thread - which should be thread index 1
+ b) We step-out from that thread
+ c) We hit a breakpoint in another thread, and DON'T produce the OS Plugin thread.
+ d) We continue, and when we hit the step out breakpoint, we again produce the same
+ OS Plugin thread.
+ main.cpp sets values into the global variable g_value, which we use to tell the OS
+ plugin whether to produce the OS plugin thread or not.
+ Since we are always producing an OS plugin thread with a backing thread, we don't
+ need to implement get_register_info or get_register_data.
+ """
+
+ def __init__(self, process):
+ '''Initialization needs a valid.SBProcess object.
+
+ This plug-in will get created after a live process is valid and has stopped for the
+ first time.'''
+ print("Plugin initialized.")
+ self.process = None
+ self.start_stop_id = 0
+ self.g_value = lldb.SBValue()
+
+ if isinstance(process, lldb.SBProcess) and process.IsValid():
+ self.process = process
+ self.g_value = process.GetTarget().FindFirstGlobalVariable("g_value")
+ if not self.g_value.IsValid():
+ print("Could not find g_value")
+
+ def create_thread(self, tid, context):
+ print("Called create thread with tid: ", tid)
+ return None
+
+ def get_thread_info(self):
+ g_value = self.g_value.GetValueAsUnsigned()
+ print("Called get_thread_info: g_value: %d"%(g_value))
+ if g_value == 0 or g_value == 2:
+ return [{'tid': 0x111111111,
+ 'name': 'one',
+ 'queue': 'queue1',
+ 'state': 'stopped',
+ 'stop_reason': 'breakpoint',
+ 'core' : 1 }]
+ else:
+ return []
+
+ def get_register_info(self):
+ print ("called get_register_info")
+ return None
+
+
+ def get_register_data(self, tid):
+ print("Get register data called for tid: %d"%(tid))
+ return None
+