493 lines · python
1"""Test queues inspection SB APIs."""2 3import os4import lldb5from lldbsuite.test.decorators import *6from lldbsuite.test.lldbtest import *7from lldbsuite.test import lldbutil8 9 10class TestQueues(TestBase):11 @skipUnlessDarwin12 @add_test_categories(["pyapi"])13 def test_with_python_api_queues(self):14 """Test queues inspection SB APIs."""15 self.build()16 self.queues()17 18 @skipUnlessDarwin19 @add_test_categories(["pyapi"])20 def test_queue_specific_breakpoints(self):21 self.build()22 self.queue_specific_breakpoints()23 24 @skipUnlessDarwin25 @add_test_categories(["pyapi"])26 def test_with_python_api_queues_with_backtrace(self):27 """Test queues inspection SB APIs."""28 self.build()29 self.queues_with_libBacktraceRecording()30 31 def setUp(self):32 # Call super's setUp().33 TestBase.setUp(self)34 # Find the line numbers that we will step to in main:35 self.main_source = "main.c"36 37 def check_queue_for_valid_queue_id(self, queue):38 self.assertNotEqual(39 queue.GetQueueID(),40 0,41 "Check queue %s for valid QueueID (got 0x%x)"42 % (queue.GetName(), queue.GetQueueID()),43 )44 45 def check_running_and_pending_items_on_queue(46 self, queue, expected_running, expected_pending47 ):48 self.assertEqual(49 queue.GetNumPendingItems(),50 expected_pending,51 "queue %s should have %d pending items, instead has %d pending items"52 % (queue.GetName(), expected_pending, (queue.GetNumPendingItems())),53 )54 self.assertEqual(55 queue.GetNumRunningItems(),56 expected_running,57 "queue %s should have %d running items, instead has %d running items"58 % (queue.GetName(), expected_running, (queue.GetNumRunningItems())),59 )60 61 def describe_threads(self):62 desc = []63 for x in self.inferior_process:64 id = x.GetIndexID()65 reason_str = lldbutil.stop_reason_to_str(x.GetStopReason())66 67 location = "\t".join(68 [69 lldbutil.get_description(x.GetFrameAtIndex(i))70 for i in range(x.GetNumFrames())71 ]72 )73 desc.append(74 "thread %d: %s (queue id: %s) at\n\t%s"75 % (id, reason_str, x.GetQueueID(), location)76 )77 print("\n".join(desc))78 79 def check_number_of_threads_owned_by_queue(self, queue, number_threads):80 if queue.GetNumThreads() != number_threads:81 self.describe_threads()82 83 self.assertEqual(84 queue.GetNumThreads(),85 number_threads,86 "queue %s should have %d thread executing, but has %d"87 % (queue.GetName(), number_threads, queue.GetNumThreads()),88 )89 90 def check_queue_kind(self, queue, kind):91 expected_kind_string = "Unknown"92 if kind == lldb.eQueueKindSerial:93 expected_kind_string = "Serial queue"94 if kind == lldb.eQueueKindConcurrent:95 expected_kind_string = "Concurrent queue"96 actual_kind_string = "Unknown"97 if queue.GetKind() == lldb.eQueueKindSerial:98 actual_kind_string = "Serial queue"99 if queue.GetKind() == lldb.eQueueKindConcurrent:100 actual_kind_string = "Concurrent queue"101 self.assertEqual(102 queue.GetKind(),103 kind,104 "queue %s is expected to be a %s but it is actually a %s"105 % (queue.GetName(), expected_kind_string, actual_kind_string),106 )107 108 def check_queues_threads_match_queue(self, queue):109 for idx in range(0, queue.GetNumThreads()):110 t = queue.GetThreadAtIndex(idx)111 self.assertTrue(112 t.IsValid(),113 "Queue %s's thread #%d must be valid" % (queue.GetName(), idx),114 )115 self.assertEqual(116 t.GetQueueID(),117 queue.GetQueueID(),118 "Queue %s has a QueueID of %d but its thread #%d has a QueueID of %d"119 % (queue.GetName(), queue.GetQueueID(), idx, t.GetQueueID()),120 )121 self.assertEqual(122 t.GetQueueName(),123 queue.GetName(),124 "Queue %s has a QueueName of %s but its thread #%d has a QueueName of %s"125 % (queue.GetName(), queue.GetName(), idx, t.GetQueueName()),126 )127 self.assertEqual(128 t.GetQueue().GetQueueID(),129 queue.GetQueueID(),130 "Thread #%d's Queue's QueueID of %d is not the same as the QueueID of its owning queue %d"131 % (idx, t.GetQueue().GetQueueID(), queue.GetQueueID()),132 )133 134 def check_queue_breakpoints(self, queue1, queue2, queue_breakpoint):135 queue1_thread = queue1.GetThreadAtIndex(0)136 queue2_thread = queue2.GetThreadAtIndex(0)137 138 self.assertEqual(139 queue_breakpoint.GetQueueName(),140 queue1.GetName(),141 "The breakpoint was set for queue %s, but the breakpoint's queue name is %s"142 % (queue_breakpoint.GetQueueName(), queue1.GetName()),143 )144 self.assertEqual(145 queue_breakpoint.GetHitCount(),146 1,147 "The breakpoint for queue %s has not been hit"148 % (queue_breakpoint.GetQueueName()),149 )150 self.assertStopReason(151 queue1_thread.GetStopReason(),152 lldb.eStopReasonBreakpoint,153 "Queue %s is not stopped at breakpoint %d"154 % (queue1.GetName(), queue_breakpoint.GetID()),155 )156 self.assertNotEqual(157 queue2_thread.GetStopReason(),158 lldb.eStopReasonBreakpoint,159 "Queue %s is stopped at breakpoint %d, but this breakpoint should only be hit for queue %s"160 % (161 queue2.GetName(),162 queue_breakpoint.GetID(),163 queue_breakpoint.GetQueueName(),164 ),165 )166 167 def queues(self):168 """Test queues inspection SB APIs without libBacktraceRecording."""169 exe = self.getBuildArtifact("a.out")170 171 target = self.dbg.CreateTarget(exe)172 self.assertTrue(target, VALID_TARGET)173 self.main_source_spec = lldb.SBFileSpec(self.main_source)174 break1 = target.BreakpointCreateByName("stopper", "a.out")175 self.assertTrue(break1, VALID_BREAKPOINT)176 process = target.LaunchSimple([], None, self.get_process_working_directory())177 self.assertTrue(process, PROCESS_IS_VALID)178 threads = lldbutil.get_threads_stopped_at_breakpoint(process, break1)179 if len(threads) != 1:180 self.fail("Failed to stop at breakpoint 1.")181 182 self.inferior_process = process183 184 queue_submittor_1 = lldb.SBQueue()185 queue_performer_1 = lldb.SBQueue()186 queue_performer_2 = lldb.SBQueue()187 queue_performer_3 = lldb.SBQueue()188 for idx in range(0, process.GetNumQueues()):189 q = process.GetQueueAtIndex(idx)190 if q.GetName() == "com.apple.work_submittor_1":191 queue_submittor_1 = q192 if q.GetName() == "com.apple.work_performer_1":193 queue_performer_1 = q194 if q.GetName() == "com.apple.work_performer_2":195 queue_performer_2 = q196 if q.GetName() == "com.apple.work_performer_3":197 queue_performer_3 = q198 199 self.assertTrue(200 queue_submittor_1.IsValid()201 and queue_performer_1.IsValid()202 and queue_performer_2.IsValid()203 and queue_performer_3.IsValid(),204 "Got all four expected queues: %s %s %s %s"205 % (206 queue_submittor_1.IsValid(),207 queue_performer_1.IsValid(),208 queue_performer_2.IsValid(),209 queue_performer_3.IsValid(),210 ),211 )212 213 self.check_queue_for_valid_queue_id(queue_submittor_1)214 self.check_queue_for_valid_queue_id(queue_performer_1)215 self.check_queue_for_valid_queue_id(queue_performer_2)216 self.check_queue_for_valid_queue_id(queue_performer_3)217 218 self.check_number_of_threads_owned_by_queue(queue_submittor_1, 1)219 self.check_number_of_threads_owned_by_queue(queue_performer_1, 1)220 self.check_number_of_threads_owned_by_queue(queue_performer_2, 1)221 self.check_number_of_threads_owned_by_queue(queue_performer_3, 4)222 223 self.check_queue_kind(queue_submittor_1, lldb.eQueueKindSerial)224 self.check_queue_kind(queue_performer_1, lldb.eQueueKindSerial)225 self.check_queue_kind(queue_performer_2, lldb.eQueueKindSerial)226 self.check_queue_kind(queue_performer_3, lldb.eQueueKindConcurrent)227 228 self.check_queues_threads_match_queue(queue_submittor_1)229 self.check_queues_threads_match_queue(queue_performer_1)230 self.check_queues_threads_match_queue(queue_performer_2)231 self.check_queues_threads_match_queue(queue_performer_3)232 233 # We have threads running with all the different dispatch QoS service234 # levels - find those threads and check that we can get the correct235 # QoS name for each of them.236 237 user_initiated_thread = lldb.SBThread()238 user_interactive_thread = lldb.SBThread()239 utility_thread = lldb.SBThread()240 background_thread = lldb.SBThread()241 for th in process.threads:242 if th.GetName() == "user initiated QoS":243 user_initiated_thread = th244 if th.GetName() == "user interactive QoS":245 user_interactive_thread = th246 if th.GetName() == "utility QoS":247 utility_thread = th248 if th.GetName() == "background QoS":249 background_thread = th250 251 self.assertTrue(252 user_initiated_thread.IsValid(), "Found user initiated QoS thread"253 )254 self.assertTrue(255 user_interactive_thread.IsValid(), "Found user interactive QoS thread"256 )257 self.assertTrue(utility_thread.IsValid(), "Found utility QoS thread")258 self.assertTrue(background_thread.IsValid(), "Found background QoS thread")259 260 stream = lldb.SBStream()261 self.assertTrue(262 user_initiated_thread.GetInfoItemByPathAsString(263 "requested_qos.printable_name", stream264 ),265 "Get QoS printable string for user initiated QoS thread",266 )267 self.assertEqual(268 stream.GetData(),269 "User Initiated",270 "user initiated QoS thread name is valid",271 )272 stream.Clear()273 self.assertTrue(274 user_interactive_thread.GetInfoItemByPathAsString(275 "requested_qos.printable_name", stream276 ),277 "Get QoS printable string for user interactive QoS thread",278 )279 self.assertEqual(280 stream.GetData(),281 "User Interactive",282 "user interactive QoS thread name is valid",283 )284 stream.Clear()285 self.assertTrue(286 utility_thread.GetInfoItemByPathAsString(287 "requested_qos.printable_name", stream288 ),289 "Get QoS printable string for utility QoS thread",290 )291 self.assertEqual(292 stream.GetData(), "Utility", "utility QoS thread name is valid"293 )294 stream.Clear()295 self.assertTrue(296 background_thread.GetInfoItemByPathAsString(297 "requested_qos.printable_name", stream298 ),299 "Get QoS printable string for background QoS thread",300 )301 self.assertEqual(302 stream.GetData(), "Background", "background QoS thread name is valid"303 )304 305 @skipIfDarwin # rdar://50379398306 def queues_with_libBacktraceRecording(self):307 """Test queues inspection SB APIs with libBacktraceRecording present."""308 exe = self.getBuildArtifact("a.out")309 310 if not os.path.isfile(311 "/Applications/Xcode.app/Contents/Developer/usr/lib/libBacktraceRecording.dylib"312 ):313 self.skipTest(314 "Skipped because libBacktraceRecording.dylib was present on the system."315 )316 317 if not os.path.isfile("/usr/lib/system/introspection/libdispatch.dylib"):318 self.skipTest(319 "Skipped because introspection libdispatch dylib is not present."320 )321 322 target = self.dbg.CreateTarget(exe)323 self.assertTrue(target, VALID_TARGET)324 325 self.main_source_spec = lldb.SBFileSpec(self.main_source)326 327 break1 = target.BreakpointCreateByName("stopper", "a.out")328 self.assertTrue(break1, VALID_BREAKPOINT)329 330 # Now launch the process, and do not stop at entry point.331 libbtr_path = "/Applications/Xcode.app/Contents/Developer/usr/lib/libBacktraceRecording.dylib"332 if self.getArchitecture() in [333 "arm",334 "arm64",335 "arm64e",336 "arm64_32",337 "armv7",338 "armv7k",339 ]:340 libbtr_path = "/Developer/usr/lib/libBacktraceRecording.dylib"341 342 process = target.LaunchSimple(343 [],344 [345 "DYLD_INSERT_LIBRARIES=%s" % (libbtr_path),346 "DYLD_LIBRARY_PATH=/usr/lib/system/introspection",347 ],348 self.get_process_working_directory(),349 )350 351 self.assertTrue(process, PROCESS_IS_VALID)352 353 # The stop reason of the thread should be breakpoint.354 threads = lldbutil.get_threads_stopped_at_breakpoint(process, break1)355 if len(threads) != 1:356 self.fail("Failed to stop at breakpoint 1.")357 358 self.inferior_process = process359 360 libbtr_module_filespec = lldb.SBFileSpec("libBacktraceRecording.dylib")361 libbtr_module = target.FindModule(libbtr_module_filespec)362 if not libbtr_module.IsValid():363 self.skipTest(364 "Skipped because libBacktraceRecording.dylib was not loaded into the process."365 )366 367 self.assertGreaterEqual(368 process.GetNumQueues(), 4, "Found the correct number of queues."369 )370 371 queue_submittor_1 = lldb.SBQueue()372 queue_performer_1 = lldb.SBQueue()373 queue_performer_2 = lldb.SBQueue()374 queue_performer_3 = lldb.SBQueue()375 for idx in range(0, process.GetNumQueues()):376 q = process.GetQueueAtIndex(idx)377 if "LLDB_COMMAND_TRACE" in os.environ:378 print("Queue with id %s has name %s" % (q.GetQueueID(), q.GetName()))379 if q.GetName() == "com.apple.work_submittor_1":380 queue_submittor_1 = q381 if q.GetName() == "com.apple.work_performer_1":382 queue_performer_1 = q383 if q.GetName() == "com.apple.work_performer_2":384 queue_performer_2 = q385 if q.GetName() == "com.apple.work_performer_3":386 queue_performer_3 = q387 if q.GetName() == "com.apple.main-thread":388 if q.GetNumThreads() == 0:389 print("Cannot get thread <=> queue associations")390 return391 392 self.assertTrue(393 queue_submittor_1.IsValid()394 and queue_performer_1.IsValid()395 and queue_performer_2.IsValid()396 and queue_performer_3.IsValid(),397 "Got all four expected queues: %s %s %s %s"398 % (399 queue_submittor_1.IsValid(),400 queue_performer_1.IsValid(),401 queue_performer_2.IsValid(),402 queue_performer_3.IsValid(),403 ),404 )405 406 self.check_queue_for_valid_queue_id(queue_submittor_1)407 self.check_queue_for_valid_queue_id(queue_performer_1)408 self.check_queue_for_valid_queue_id(queue_performer_2)409 self.check_queue_for_valid_queue_id(queue_performer_3)410 411 self.check_running_and_pending_items_on_queue(queue_submittor_1, 1, 0)412 self.check_running_and_pending_items_on_queue(queue_performer_1, 1, 3)413 self.check_running_and_pending_items_on_queue(queue_performer_2, 1, 9999)414 self.check_running_and_pending_items_on_queue(queue_performer_3, 4, 0)415 416 self.check_number_of_threads_owned_by_queue(queue_submittor_1, 1)417 self.check_number_of_threads_owned_by_queue(queue_performer_1, 1)418 self.check_number_of_threads_owned_by_queue(queue_performer_2, 1)419 self.check_number_of_threads_owned_by_queue(queue_performer_3, 4)420 421 self.check_queue_kind(queue_submittor_1, lldb.eQueueKindSerial)422 self.check_queue_kind(queue_performer_1, lldb.eQueueKindSerial)423 self.check_queue_kind(queue_performer_2, lldb.eQueueKindSerial)424 self.check_queue_kind(queue_performer_3, lldb.eQueueKindConcurrent)425 426 self.check_queues_threads_match_queue(queue_submittor_1)427 self.check_queues_threads_match_queue(queue_performer_1)428 self.check_queues_threads_match_queue(queue_performer_2)429 self.check_queues_threads_match_queue(queue_performer_3)430 431 self.assertTrue(432 queue_performer_2.GetPendingItemAtIndex(0).IsValid(),433 "queue 2's pending item #0 is valid",434 )435 self.assertEqual(436 queue_performer_2.GetPendingItemAtIndex(0)437 .GetAddress()438 .GetSymbol()439 .GetName(),440 "doing_the_work_2",441 "queue 2's pending item #0 should be doing_the_work_2",442 )443 self.assertEqual(444 queue_performer_2.GetNumPendingItems(),445 9999,446 "verify that queue 2 still has 9999 pending items",447 )448 self.assertTrue(449 queue_performer_2.GetPendingItemAtIndex(9998).IsValid(),450 "queue 2's pending item #9998 is valid",451 )452 self.assertEqual(453 queue_performer_2.GetPendingItemAtIndex(9998)454 .GetAddress()455 .GetSymbol()456 .GetName(),457 "doing_the_work_2",458 "queue 2's pending item #0 should be doing_the_work_2",459 )460 self.assertFalse(461 queue_performer_2.GetPendingItemAtIndex(9999).IsValid(),462 "queue 2's pending item #9999 is invalid",463 )464 465 def queue_specific_breakpoints(self):466 # Run the executable until the stopper function and get the breakpoint467 # that's created from that. Then set the queue name of the breakpoint468 # to be the name of the main thread469 (470 target,471 process,472 main_thread,473 queue_breakpoint,474 ) = lldbutil.run_to_name_breakpoint(self, "stopper", only_one_thread=False)475 queue_breakpoint.SetQueueName(main_thread.GetQueue().GetName())476 477 # Create a submittor queue478 queue_submittor_1 = lldb.SBQueue()479 for idx in range(0, process.GetNumQueues()):480 q = process.GetQueueAtIndex(idx)481 if q.GetName() == "com.apple.work_submittor_1":482 queue_submittor_1 = q483 484 self.assertTrue(485 queue_submittor_1.IsValid(),486 "Unable to get expected queue com.apple.work_submittor_1, instead got queue %s"487 % (queue_submittor_1.GetName()),488 )489 490 self.check_queue_breakpoints(491 main_thread.GetQueue(), queue_submittor_1, queue_breakpoint492 )493