| """ | |
| Verify that `scripting extension list --instances` reports the live scripted | |
| extension objects along with the script file their class was imported from. | |
| """ | |
| import os | |
| import lldb | |
| from lldbsuite.test.decorators import * | |
| from lldbsuite.test.lldbtest import * | |
| class TestScriptingExtensionInstances(TestBase): | |
| NO_DEBUG_INFO_TESTCASE = True | |
| def list_instances(self, args=""): | |
| result = lldb.SBCommandReturnObject() | |
| self.dbg.GetCommandInterpreter().HandleCommand( | |
| "scripting extension list --instances --json " + args, result | |
| ) | |
| self.assertTrue(result.Succeeded(), result.GetError()) | |
| data = lldb.SBStructuredData() | |
| self.assertSuccess(data.SetFromJSON(result.GetOutput())) | |
| self.assertEqual(data.GetType(), lldb.eStructuredDataTypeArray) | |
| return [data.GetItemAtIndex(i) for i in range(data.GetSize())] | |
| def find_group(self, class_name, args=""): | |
| for group in self.list_instances(args): | |
| if group.GetValueForKey("class_name").GetStringValue(256) == class_name: | |
| return group | |
| return None | |
| def get_instances(self, group): | |
| instances = group.GetValueForKey("instances") | |
| return [instances.GetItemAtIndex(i) for i in range(instances.GetSize())] | |
| def test_instances(self): | |
| script_path = os.path.join(self.getSourceDir(), "instance_cmds.py") | |
| self.runCmd("command script import " + script_path) | |
| self.runCmd("command script add -c instance_cmds.EchoCommand echo-cmd") | |
| self.addTearDownHook( | |
| lambda: self.runCmd("command script delete echo-cmd", check=False) | |
| ) | |
| group = self.find_group("instance_cmds.EchoCommand") | |
| self.assertIsNotNone(group, "scripted command instance is not listed") | |
| self.assertEqual( | |
| group.GetValueForKey("extension").GetStringValue(256), "ScriptedCommand" | |
| ) | |
| self.assertEqual( | |
| os.path.realpath(group.GetValueForKey("source_path").GetStringValue(4096)), | |
| os.path.realpath(script_path), | |
| ) | |
| (instance,) = self.get_instances(group) | |
| self.assertTrue(instance.GetValueForKey("uuid").GetStringValue(64)) | |
| self.assertNotEqual( | |
| instance.GetValueForKey("address").GetUnsignedIntegerValue(), 0 | |
| ) | |
| # Scripted commands take no extra arguments. | |
| self.assertFalse(instance.GetValueForKey("args").IsValid()) | |
| self.assertIsNotNone( | |
| self.find_group("instance_cmds.EchoCommand", "ScriptedCommand") | |
| ) | |
| self.assertIsNone( | |
| self.find_group("instance_cmds.EchoCommand", "ScriptedProcess") | |
| ) | |
| self.expect( | |
| "scripting extension list --instances", | |
| substrs=[ | |
| "instance_cmds.EchoCommand", | |
| "Extension: ScriptedCommand", | |
| "Path: " + script_path, | |
| "Instance: ", | |
| ], | |
| ) | |
| self.runCmd("command script delete echo-cmd") | |
| self.assertIsNone(self.find_group("instance_cmds.EchoCommand")) | |
| def test_instances_grouped_by_class(self): | |
| script_path = os.path.join(self.getSourceDir(), "instance_cmds.py") | |
| self.runCmd("command script import " + script_path) | |
| self.assertTrue(self.dbg.CreateTarget(""), VALID_TARGET) | |
| self.runCmd("target stop-hook add -P instance_cmds.StopHook -k answer -v 42") | |
| self.runCmd("target stop-hook add -P instance_cmds.StopHook -k answer -v 42") | |
| group = self.find_group("instance_cmds.StopHook", "ScriptedHook") | |
| self.assertIsNotNone(group, "stop hook instances are not listed") | |
| self.assertEqual( | |
| os.path.realpath(group.GetValueForKey("source_path").GetStringValue(4096)), | |
| os.path.realpath(script_path), | |
| ) | |
| # Identical class and arguments, but still two distinct instances. | |
| instances = self.get_instances(group) | |
| self.assertEqual(len(instances), 2) | |
| uuids = {i.GetValueForKey("uuid").GetStringValue(64) for i in instances} | |
| self.assertEqual(len(uuids), 2) | |
| # Both objects are alive at once, so they can't share an address. | |
| addresses = { | |
| i.GetValueForKey("address").GetUnsignedIntegerValue() for i in instances | |
| } | |
| self.assertEqual(len(addresses), 2) | |
| for instance in instances: | |
| args = instance.GetValueForKey("args") | |
| # Stop hooks store numeric values as integers. | |
| self.assertEqual( | |
| args.GetValueForKey("answer").GetUnsignedIntegerValue(), 42 | |
| ) | |
| self.expect( | |
| "scripting extension list --instances ScriptedHook", | |
| substrs=[ | |
| "instance_cmds.StopHook", | |
| "Instances:", | |
| "[0] ", | |
| "[1] ", | |
| "- answer: 42", | |
| ], | |
| ) | |
| def test_destroyed_instances_are_removed(self): | |
| script_path = os.path.join(self.getSourceDir(), "instance_cmds.py") | |
| self.runCmd("command script import " + script_path) | |
| target = self.dbg.CreateTarget("") | |
| self.assertTrue(target, VALID_TARGET) | |
| self.runCmd("target stop-hook add -P instance_cmds.StopHook -k name -v first") | |
| self.runCmd("target stop-hook add -P instance_cmds.StopHook -k name -v second") | |
| def names(): | |
| group = self.find_group("instance_cmds.StopHook") | |
| if not group: | |
| return [] | |
| return [ | |
| i.GetValueForKey("args").GetValueForKey("name").GetStringValue(64) | |
| for i in self.get_instances(group) | |
| ] | |
| self.assertEqual(names(), ["first", "second"]) | |
| self.runCmd("target stop-hook delete 1") | |
| self.assertEqual(names(), ["second"]) | |
| self.assertTrue(self.dbg.DeleteTarget(target)) | |
| self.assertEqual(names(), []) | |
| def test_path_without_command_script_import(self): | |
| self.runCmd( | |
| "script import sys; sys.path.insert(0, %r); import instance_cmds" | |
| % self.getSourceDir() | |
| ) | |
| self.runCmd("command script add -c instance_cmds.EchoCommand echo-cmd") | |
| self.addTearDownHook( | |
| lambda: self.runCmd("command script delete echo-cmd", check=False) | |
| ) | |
| group = self.find_group("instance_cmds.EchoCommand") | |
| self.assertIsNotNone(group, "scripted command instance is not listed") | |
| self.assertEqual( | |
| os.path.realpath(group.GetValueForKey("source_path").GetStringValue(4096)), | |
| os.path.realpath(os.path.join(self.getSourceDir(), "instance_cmds.py")), | |
| ) | |
| def test_path_of_class_in_submodule(self): | |
| pkg_path = os.path.join(self.getSourceDir(), "instance_pkg") | |
| self.runCmd("command script import " + pkg_path) | |
| self.runCmd("command script add -c instance_pkg.cmds.EchoCommand echo-cmd") | |
| self.addTearDownHook( | |
| lambda: self.runCmd("command script delete echo-cmd", check=False) | |
| ) | |
| # The module defining the class, not the package that was imported. | |
| group = self.find_group("instance_pkg.cmds.EchoCommand") | |
| self.assertIsNotNone(group, "scripted command instance is not listed") | |
| self.assertEqual( | |
| os.path.realpath(group.GetValueForKey("source_path").GetStringValue(4096)), | |
| os.path.realpath(os.path.join(pkg_path, "cmds.py")), | |
| ) | |
| def test_invalid_extension_name(self): | |
| self.expect( | |
| "scripting extension list --instances NotAnExtension", | |
| error=True, | |
| substrs=["no scripted extension named 'NotAnExtension'"], | |
| ) |