diff options
| author | Jim Ingham <jingham@apple.com> | 2020-03-18 12:05:08 -0700 |
|---|---|---|
| committer | Jim Ingham <jingham@apple.com> | 2020-04-03 14:56:28 -0700 |
| commit | 1893065d7bf5c41fbd0dbbee0a39933d3a99806b (patch) | |
| tree | 28e5de235fbed60aae5cc3ab6ec2b65c9aac3940 /lldb/test/API/functionalities/plugins/python_os_plugin | |
| parent | 61e8e6882de7c89933d3cc4c4d44719679e4b4f0 (diff) | |
Allow the ThreadPlanStackMap to hold the thread plans for threads
that were not reported by the OS plugin. To facilitate this, move
adding/updating the ThreadPlans for a Thread to the ThreadPlanStackMap.
Also move dumping thread plans there as well.
Added some tests for "thread plan list" and "thread plan discard" since
I didn't seem to have written any originally.
Differential Revision: https://reviews.llvm.org/D76814
Diffstat (limited to 'lldb/test/API/functionalities/plugins/python_os_plugin')
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 + |
