|  | """Test queues inspection SB APIs.""" | 
|  |  | 
|  | from __future__ import print_function | 
|  |  | 
|  |  | 
|  | import unittest2 | 
|  | import os | 
|  | import time | 
|  | import lldb | 
|  | from lldbsuite.test.decorators import * | 
|  | from lldbsuite.test.lldbtest import * | 
|  | from lldbsuite.test import lldbutil | 
|  |  | 
|  |  | 
|  | class TestQueues(TestBase): | 
|  |  | 
|  | mydir = TestBase.compute_mydir(__file__) | 
|  |  | 
|  | @skipUnlessDarwin | 
|  | @add_test_categories(['pyapi']) | 
|  | def test_with_python_api_queues(self): | 
|  | """Test queues inspection SB APIs.""" | 
|  | self.build() | 
|  | self.queues() | 
|  |  | 
|  | @skipUnlessDarwin | 
|  | @add_test_categories(['pyapi']) | 
|  | def test_with_python_api_queues_with_backtrace(self): | 
|  | """Test queues inspection SB APIs.""" | 
|  | self.build() | 
|  | self.queues_with_libBacktraceRecording() | 
|  |  | 
|  | def setUp(self): | 
|  | # Call super's setUp(). | 
|  | TestBase.setUp(self) | 
|  | # Find the line numbers that we will step to in main: | 
|  | self.main_source = "main.c" | 
|  |  | 
|  | def check_queue_for_valid_queue_id(self, queue): | 
|  | self.assertTrue( | 
|  | queue.GetQueueID() != 0, "Check queue %s for valid QueueID (got 0x%x)" % | 
|  | (queue.GetName(), queue.GetQueueID())) | 
|  |  | 
|  | def check_running_and_pending_items_on_queue( | 
|  | self, queue, expected_running, expected_pending): | 
|  | self.assertTrue( | 
|  | queue.GetNumPendingItems() == expected_pending, | 
|  | "queue %s should have %d pending items, instead has %d pending items" % | 
|  | (queue.GetName(), | 
|  | expected_pending, | 
|  | (queue.GetNumPendingItems()))) | 
|  | self.assertTrue( | 
|  | queue.GetNumRunningItems() == expected_running, | 
|  | "queue %s should have %d running items, instead has %d running items" % | 
|  | (queue.GetName(), | 
|  | expected_running, | 
|  | (queue.GetNumRunningItems()))) | 
|  |  | 
|  | def check_number_of_threads_owned_by_queue(self, queue, number_threads): | 
|  | self.assertTrue( | 
|  | queue.GetNumThreads() == number_threads, | 
|  | "queue %s should have %d thread executing, but has %d" % | 
|  | (queue.GetName(), | 
|  | number_threads, | 
|  | queue.GetNumThreads())) | 
|  |  | 
|  | def check_queue_kind(self, queue, kind): | 
|  | expected_kind_string = "Unknown" | 
|  | if kind == lldb.eQueueKindSerial: | 
|  | expected_kind_string = "Serial queue" | 
|  | if kind == lldb.eQueueKindConcurrent: | 
|  | expected_kind_string = "Concurrent queue" | 
|  | actual_kind_string = "Unknown" | 
|  | if queue.GetKind() == lldb.eQueueKindSerial: | 
|  | actual_kind_string = "Serial queue" | 
|  | if queue.GetKind() == lldb.eQueueKindConcurrent: | 
|  | actual_kind_string = "Concurrent queue" | 
|  | self.assertTrue( | 
|  | queue.GetKind() == kind, | 
|  | "queue %s is expected to be a %s but it is actually a %s" % | 
|  | (queue.GetName(), | 
|  | expected_kind_string, | 
|  | actual_kind_string)) | 
|  |  | 
|  | def check_queues_threads_match_queue(self, queue): | 
|  | for idx in range(0, queue.GetNumThreads()): | 
|  | t = queue.GetThreadAtIndex(idx) | 
|  | self.assertTrue( | 
|  | t.IsValid(), "Queue %s's thread #%d must be valid" % | 
|  | (queue.GetName(), idx)) | 
|  | self.assertTrue( | 
|  | t.GetQueueID() == queue.GetQueueID(), | 
|  | "Queue %s has a QueueID of %d but its thread #%d has a QueueID of %d" % | 
|  | (queue.GetName(), | 
|  | queue.GetQueueID(), | 
|  | idx, | 
|  | t.GetQueueID())) | 
|  | self.assertTrue( | 
|  | t.GetQueueName() == queue.GetName(), | 
|  | "Queue %s has a QueueName of %s but its thread #%d has a QueueName of %s" % | 
|  | (queue.GetName(), | 
|  | queue.GetName(), | 
|  | idx, | 
|  | t.GetQueueName())) | 
|  | self.assertTrue( | 
|  | t.GetQueue().GetQueueID() == queue.GetQueueID(), | 
|  | "Thread #%d's Queue's QueueID of %d is not the same as the QueueID of its owning queue %d" % | 
|  | (idx, | 
|  | t.GetQueue().GetQueueID(), | 
|  | queue.GetQueueID())) | 
|  |  | 
|  | def queues(self): | 
|  | """Test queues inspection SB APIs without libBacktraceRecording.""" | 
|  | exe = self.getBuildArtifact("a.out") | 
|  |  | 
|  | target = self.dbg.CreateTarget(exe) | 
|  | self.assertTrue(target, VALID_TARGET) | 
|  | self.main_source_spec = lldb.SBFileSpec(self.main_source) | 
|  | break1 = target.BreakpointCreateByName("stopper", 'a.out') | 
|  | self.assertTrue(break1, VALID_BREAKPOINT) | 
|  | process = target.LaunchSimple( | 
|  | [], None, self.get_process_working_directory()) | 
|  | self.assertTrue(process, PROCESS_IS_VALID) | 
|  | threads = lldbutil.get_threads_stopped_at_breakpoint(process, break1) | 
|  | if len(threads) != 1: | 
|  | self.fail("Failed to stop at breakpoint 1.") | 
|  |  | 
|  | queue_submittor_1 = lldb.SBQueue() | 
|  | queue_performer_1 = lldb.SBQueue() | 
|  | queue_performer_2 = lldb.SBQueue() | 
|  | queue_performer_3 = lldb.SBQueue() | 
|  | for idx in range(0, process.GetNumQueues()): | 
|  | q = process.GetQueueAtIndex(idx) | 
|  | if q.GetName() == "com.apple.work_submittor_1": | 
|  | queue_submittor_1 = q | 
|  | if q.GetName() == "com.apple.work_performer_1": | 
|  | queue_performer_1 = q | 
|  | if q.GetName() == "com.apple.work_performer_2": | 
|  | queue_performer_2 = q | 
|  | if q.GetName() == "com.apple.work_performer_3": | 
|  | queue_performer_3 = q | 
|  |  | 
|  | self.assertTrue( | 
|  | queue_submittor_1.IsValid() and queue_performer_1.IsValid() and queue_performer_2.IsValid() and queue_performer_3.IsValid(), | 
|  | "Got all four expected queues: %s %s %s %s" % | 
|  | (queue_submittor_1.IsValid(), | 
|  | queue_performer_1.IsValid(), | 
|  | queue_performer_2.IsValid(), | 
|  | queue_performer_3.IsValid())) | 
|  |  | 
|  | self.check_queue_for_valid_queue_id(queue_submittor_1) | 
|  | self.check_queue_for_valid_queue_id(queue_performer_1) | 
|  | self.check_queue_for_valid_queue_id(queue_performer_2) | 
|  | self.check_queue_for_valid_queue_id(queue_performer_3) | 
|  |  | 
|  | self.check_number_of_threads_owned_by_queue(queue_submittor_1, 1) | 
|  | self.check_number_of_threads_owned_by_queue(queue_performer_1, 1) | 
|  | self.check_number_of_threads_owned_by_queue(queue_performer_2, 1) | 
|  | self.check_number_of_threads_owned_by_queue(queue_performer_3, 4) | 
|  |  | 
|  | self.check_queue_kind(queue_submittor_1, lldb.eQueueKindSerial) | 
|  | self.check_queue_kind(queue_performer_1, lldb.eQueueKindSerial) | 
|  | self.check_queue_kind(queue_performer_2, lldb.eQueueKindSerial) | 
|  | self.check_queue_kind(queue_performer_3, lldb.eQueueKindConcurrent) | 
|  |  | 
|  | self.check_queues_threads_match_queue(queue_submittor_1) | 
|  | self.check_queues_threads_match_queue(queue_performer_1) | 
|  | self.check_queues_threads_match_queue(queue_performer_2) | 
|  | self.check_queues_threads_match_queue(queue_performer_3) | 
|  |  | 
|  | # We have threads running with all the different dispatch QoS service | 
|  | # levels - find those threads and check that we can get the correct | 
|  | # QoS name for each of them. | 
|  |  | 
|  | user_initiated_thread = lldb.SBThread() | 
|  | user_interactive_thread = lldb.SBThread() | 
|  | utility_thread = lldb.SBThread() | 
|  | unspecified_thread = lldb.SBThread() | 
|  | background_thread = lldb.SBThread() | 
|  | for th in process.threads: | 
|  | if th.GetName() == "user initiated QoS": | 
|  | user_initiated_thread = th | 
|  | if th.GetName() == "user interactive QoS": | 
|  | user_interactive_thread = th | 
|  | if th.GetName() == "utility QoS": | 
|  | utility_thread = th | 
|  | if th.GetName() == "unspecified QoS": | 
|  | unspecified_thread = th | 
|  | if th.GetName() == "background QoS": | 
|  | background_thread = th | 
|  |  | 
|  | self.assertTrue( | 
|  | user_initiated_thread.IsValid(), | 
|  | "Found user initiated QoS thread") | 
|  | self.assertTrue( | 
|  | user_interactive_thread.IsValid(), | 
|  | "Found user interactive QoS thread") | 
|  | self.assertTrue(utility_thread.IsValid(), "Found utility QoS thread") | 
|  | self.assertTrue( | 
|  | unspecified_thread.IsValid(), | 
|  | "Found unspecified QoS thread") | 
|  | self.assertTrue( | 
|  | background_thread.IsValid(), | 
|  | "Found background QoS thread") | 
|  |  | 
|  | stream = lldb.SBStream() | 
|  | self.assertTrue( | 
|  | user_initiated_thread.GetInfoItemByPathAsString( | 
|  | "requested_qos.printable_name", | 
|  | stream), | 
|  | "Get QoS printable string for user initiated QoS thread") | 
|  | self.assertTrue( | 
|  | stream.GetData() == "User Initiated", | 
|  | "user initiated QoS thread name is valid") | 
|  | stream.Clear() | 
|  | self.assertTrue( | 
|  | user_interactive_thread.GetInfoItemByPathAsString( | 
|  | "requested_qos.printable_name", | 
|  | stream), | 
|  | "Get QoS printable string for user interactive QoS thread") | 
|  | self.assertTrue( | 
|  | stream.GetData() == "User Interactive", | 
|  | "user interactive QoS thread name is valid") | 
|  | stream.Clear() | 
|  | self.assertTrue( | 
|  | utility_thread.GetInfoItemByPathAsString( | 
|  | "requested_qos.printable_name", | 
|  | stream), | 
|  | "Get QoS printable string for utility QoS thread") | 
|  | self.assertTrue( | 
|  | stream.GetData() == "Utility", | 
|  | "utility QoS thread name is valid") | 
|  | stream.Clear() | 
|  | self.assertTrue( | 
|  | unspecified_thread.GetInfoItemByPathAsString( | 
|  | "requested_qos.printable_name", | 
|  | stream), | 
|  | "Get QoS printable string for unspecified QoS thread") | 
|  | qosName = stream.GetData() | 
|  | self.assertTrue( | 
|  | qosName == "User Initiated" or qosName == "Default", | 
|  | "unspecified QoS thread name is valid") | 
|  | stream.Clear() | 
|  | self.assertTrue( | 
|  | background_thread.GetInfoItemByPathAsString( | 
|  | "requested_qos.printable_name", | 
|  | stream), | 
|  | "Get QoS printable string for background QoS thread") | 
|  | self.assertTrue( | 
|  | stream.GetData() == "Background", | 
|  | "background QoS thread name is valid") | 
|  |  | 
|  | def queues_with_libBacktraceRecording(self): | 
|  | """Test queues inspection SB APIs with libBacktraceRecording present.""" | 
|  | exe = self.getBuildArtifact("a.out") | 
|  |  | 
|  | if not os.path.isfile( | 
|  | '/Applications/Xcode.app/Contents/Developer/usr/lib/libBacktraceRecording.dylib'): | 
|  | self.skipTest( | 
|  | "Skipped because libBacktraceRecording.dylib was present on the system.") | 
|  |  | 
|  | if not os.path.isfile( | 
|  | '/usr/lib/system/introspection/libdispatch.dylib'): | 
|  | self.skipTest( | 
|  | "Skipped because introspection libdispatch dylib is not present.") | 
|  |  | 
|  | target = self.dbg.CreateTarget(exe) | 
|  | self.assertTrue(target, VALID_TARGET) | 
|  |  | 
|  | self.main_source_spec = lldb.SBFileSpec(self.main_source) | 
|  |  | 
|  | break1 = target.BreakpointCreateByName("stopper", 'a.out') | 
|  | self.assertTrue(break1, VALID_BREAKPOINT) | 
|  |  | 
|  | # Now launch the process, and do not stop at entry point. | 
|  | libbtr_path = "/Applications/Xcode.app/Contents/Developer/usr/lib/libBacktraceRecording.dylib" | 
|  | if self.getArchitecture() in ['arm', 'arm64', 'arm64e', 'arm64_32', 'armv7', 'armv7k']: | 
|  | libbtr_path = "/Developer/usr/lib/libBacktraceRecording.dylib" | 
|  |  | 
|  | process = target.LaunchSimple( | 
|  | [], | 
|  | [ | 
|  | 'DYLD_INSERT_LIBRARIES=%s' % (libbtr_path), | 
|  | 'DYLD_LIBRARY_PATH=/usr/lib/system/introspection'], | 
|  | self.get_process_working_directory()) | 
|  |  | 
|  | self.assertTrue(process, PROCESS_IS_VALID) | 
|  |  | 
|  | # The stop reason of the thread should be breakpoint. | 
|  | threads = lldbutil.get_threads_stopped_at_breakpoint(process, break1) | 
|  | if len(threads) != 1: | 
|  | self.fail("Failed to stop at breakpoint 1.") | 
|  |  | 
|  | libbtr_module_filespec = lldb.SBFileSpec("libBacktraceRecording.dylib") | 
|  | libbtr_module = target.FindModule(libbtr_module_filespec) | 
|  | if not libbtr_module.IsValid(): | 
|  | self.skipTest( | 
|  | "Skipped because libBacktraceRecording.dylib was not loaded into the process.") | 
|  |  | 
|  | self.assertTrue( | 
|  | process.GetNumQueues() >= 4, | 
|  | "Found the correct number of queues.") | 
|  |  | 
|  | queue_submittor_1 = lldb.SBQueue() | 
|  | queue_performer_1 = lldb.SBQueue() | 
|  | queue_performer_2 = lldb.SBQueue() | 
|  | queue_performer_3 = lldb.SBQueue() | 
|  | for idx in range(0, process.GetNumQueues()): | 
|  | q = process.GetQueueAtIndex(idx) | 
|  | if q.GetName() == "com.apple.work_submittor_1": | 
|  | queue_submittor_1 = q | 
|  | if q.GetName() == "com.apple.work_performer_1": | 
|  | queue_performer_1 = q | 
|  | if q.GetName() == "com.apple.work_performer_2": | 
|  | queue_performer_2 = q | 
|  | if q.GetName() == "com.apple.work_performer_3": | 
|  | queue_performer_3 = q | 
|  |  | 
|  | self.assertTrue( | 
|  | queue_submittor_1.IsValid() and queue_performer_1.IsValid() and queue_performer_2.IsValid() and queue_performer_3.IsValid(), | 
|  | "Got all four expected queues: %s %s %s %s" % | 
|  | (queue_submittor_1.IsValid(), | 
|  | queue_performer_1.IsValid(), | 
|  | queue_performer_2.IsValid(), | 
|  | queue_performer_3.IsValid())) | 
|  |  | 
|  | self.check_queue_for_valid_queue_id(queue_submittor_1) | 
|  | self.check_queue_for_valid_queue_id(queue_performer_1) | 
|  | self.check_queue_for_valid_queue_id(queue_performer_2) | 
|  | self.check_queue_for_valid_queue_id(queue_performer_3) | 
|  |  | 
|  | self.check_running_and_pending_items_on_queue(queue_submittor_1, 1, 0) | 
|  | self.check_running_and_pending_items_on_queue(queue_performer_1, 1, 3) | 
|  | self.check_running_and_pending_items_on_queue( | 
|  | queue_performer_2, 1, 9999) | 
|  | self.check_running_and_pending_items_on_queue(queue_performer_3, 4, 0) | 
|  |  | 
|  | self.check_number_of_threads_owned_by_queue(queue_submittor_1, 1) | 
|  | self.check_number_of_threads_owned_by_queue(queue_performer_1, 1) | 
|  | self.check_number_of_threads_owned_by_queue(queue_performer_2, 1) | 
|  | self.check_number_of_threads_owned_by_queue(queue_performer_3, 4) | 
|  |  | 
|  | self.check_queue_kind(queue_submittor_1, lldb.eQueueKindSerial) | 
|  | self.check_queue_kind(queue_performer_1, lldb.eQueueKindSerial) | 
|  | self.check_queue_kind(queue_performer_2, lldb.eQueueKindSerial) | 
|  | self.check_queue_kind(queue_performer_3, lldb.eQueueKindConcurrent) | 
|  |  | 
|  | self.check_queues_threads_match_queue(queue_submittor_1) | 
|  | self.check_queues_threads_match_queue(queue_performer_1) | 
|  | self.check_queues_threads_match_queue(queue_performer_2) | 
|  | self.check_queues_threads_match_queue(queue_performer_3) | 
|  |  | 
|  | self.assertTrue(queue_performer_2.GetPendingItemAtIndex( | 
|  | 0).IsValid(), "queue 2's pending item #0 is valid") | 
|  | self.assertTrue(queue_performer_2.GetPendingItemAtIndex(0).GetAddress().GetSymbol( | 
|  | ).GetName() == "doing_the_work_2", "queue 2's pending item #0 should be doing_the_work_2") | 
|  | self.assertTrue( | 
|  | queue_performer_2.GetNumPendingItems() == 9999, | 
|  | "verify that queue 2 still has 9999 pending items") | 
|  | self.assertTrue(queue_performer_2.GetPendingItemAtIndex( | 
|  | 9998).IsValid(), "queue 2's pending item #9998 is valid") | 
|  | self.assertTrue(queue_performer_2.GetPendingItemAtIndex(9998).GetAddress().GetSymbol( | 
|  | ).GetName() == "doing_the_work_2", "queue 2's pending item #0 should be doing_the_work_2") | 
|  | self.assertTrue(queue_performer_2.GetPendingItemAtIndex( | 
|  | 9999).IsValid() == False, "queue 2's pending item #9999 is invalid") |