1"""Test queues inspection SB APIs.""" 2 3import os 4import lldb 5from lldbsuite.test.decorators import * 6from lldbsuite.test.lldbtest import * 7from lldbsuite.test import lldbutil 8 9 10class TestQueues(TestBase): 11 @skipUnlessDarwin 12 @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 @skipUnlessDarwin 19 @add_test_categories(["pyapi"]) 20 def test_queue_specific_breakpoints(self): 21 self.build() 22 self.queue_specific_breakpoints() 23 24 @skipUnlessDarwin 25 @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_pending 47 ): 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 = process 183 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 = q 192 if q.GetName() == "com.apple.work_performer_1": 193 queue_performer_1 = q 194 if q.GetName() == "com.apple.work_performer_2": 195 queue_performer_2 = q 196 if q.GetName() == "com.apple.work_performer_3": 197 queue_performer_3 = q 198 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 service 234 # levels - find those threads and check that we can get the correct 235 # 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 = th 244 if th.GetName() == "user interactive QoS": 245 user_interactive_thread = th 246 if th.GetName() == "utility QoS": 247 utility_thread = th 248 if th.GetName() == "background QoS": 249 background_thread = th 250 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", stream 264 ), 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", stream 276 ), 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", stream 288 ), 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", stream 298 ), 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://50379398 306 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 = process 359 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 = q 381 if q.GetName() == "com.apple.work_performer_1": 382 queue_performer_1 = q 383 if q.GetName() == "com.apple.work_performer_2": 384 queue_performer_2 = q 385 if q.GetName() == "com.apple.work_performer_3": 386 queue_performer_3 = q 387 if q.GetName() == "com.apple.main-thread": 388 if q.GetNumThreads() == 0: 389 print("Cannot get thread <=> queue associations") 390 return 391 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 breakpoint 467 # that's created from that. Then set the queue name of the breakpoint 468 # to be the name of the main thread 469 ( 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 queue 478 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 = q 483 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_breakpoint 492 ) 493