1"""Test queues inspection SB APIs.""" 2 3from __future__ import print_function 4 5 6import unittest2 7import os 8import lldb 9from lldbsuite.test.decorators import * 10from lldbsuite.test.lldbtest import * 11from lldbsuite.test import lldbutil 12 13 14class TestQueues(TestBase): 15 16 mydir = TestBase.compute_mydir(__file__) 17 18 @skipUnlessDarwin 19 @add_test_categories(['pyapi']) 20 def test_with_python_api_queues(self): 21 """Test queues inspection SB APIs.""" 22 self.build() 23 self.queues() 24 25 @skipUnlessDarwin 26 @add_test_categories(['pyapi']) 27 def test_with_python_api_queues_with_backtrace(self): 28 """Test queues inspection SB APIs.""" 29 self.build() 30 self.queues_with_libBacktraceRecording() 31 32 def setUp(self): 33 # Call super's setUp(). 34 TestBase.setUp(self) 35 # Find the line numbers that we will step to in main: 36 self.main_source = "main.c" 37 38 def check_queue_for_valid_queue_id(self, queue): 39 self.assertTrue( 40 queue.GetQueueID() != 0, "Check queue %s for valid QueueID (got 0x%x)" % 41 (queue.GetName(), queue.GetQueueID())) 42 43 def check_running_and_pending_items_on_queue( 44 self, queue, expected_running, expected_pending): 45 self.assertTrue( 46 queue.GetNumPendingItems() == expected_pending, 47 "queue %s should have %d pending items, instead has %d pending items" % 48 (queue.GetName(), 49 expected_pending, 50 (queue.GetNumPendingItems()))) 51 self.assertTrue( 52 queue.GetNumRunningItems() == expected_running, 53 "queue %s should have %d running items, instead has %d running items" % 54 (queue.GetName(), 55 expected_running, 56 (queue.GetNumRunningItems()))) 57 58 def describe_threads(self): 59 desc = [] 60 for x in self.inferior_process: 61 id = x.GetIndexID() 62 reason_str = lldbutil.stop_reason_to_str(x.GetStopReason()) 63 64 location = "\t".join([lldbutil.get_description( 65 x.GetFrameAtIndex(i)) for i in range(x.GetNumFrames())]) 66 desc.append( 67 "thread %d: %s (queue id: %s) at\n\t%s" % 68 (id, reason_str, x.GetQueueID(), location)) 69 print('\n'.join(desc)) 70 71 def check_number_of_threads_owned_by_queue(self, queue, number_threads): 72 if (queue.GetNumThreads() != number_threads): 73 self.describe_threads() 74 75 self.assertTrue( 76 queue.GetNumThreads() == number_threads, 77 "queue %s should have %d thread executing, but has %d" % 78 (queue.GetName(), 79 number_threads, 80 queue.GetNumThreads())) 81 82 def check_queue_kind(self, queue, kind): 83 expected_kind_string = "Unknown" 84 if kind == lldb.eQueueKindSerial: 85 expected_kind_string = "Serial queue" 86 if kind == lldb.eQueueKindConcurrent: 87 expected_kind_string = "Concurrent queue" 88 actual_kind_string = "Unknown" 89 if queue.GetKind() == lldb.eQueueKindSerial: 90 actual_kind_string = "Serial queue" 91 if queue.GetKind() == lldb.eQueueKindConcurrent: 92 actual_kind_string = "Concurrent queue" 93 self.assertTrue( 94 queue.GetKind() == kind, 95 "queue %s is expected to be a %s but it is actually a %s" % 96 (queue.GetName(), 97 expected_kind_string, 98 actual_kind_string)) 99 100 def check_queues_threads_match_queue(self, queue): 101 for idx in range(0, queue.GetNumThreads()): 102 t = queue.GetThreadAtIndex(idx) 103 self.assertTrue( 104 t.IsValid(), "Queue %s's thread #%d must be valid" % 105 (queue.GetName(), idx)) 106 self.assertTrue( 107 t.GetQueueID() == queue.GetQueueID(), 108 "Queue %s has a QueueID of %d but its thread #%d has a QueueID of %d" % 109 (queue.GetName(), 110 queue.GetQueueID(), 111 idx, 112 t.GetQueueID())) 113 self.assertTrue( 114 t.GetQueueName() == queue.GetName(), 115 "Queue %s has a QueueName of %s but its thread #%d has a QueueName of %s" % 116 (queue.GetName(), 117 queue.GetName(), 118 idx, 119 t.GetQueueName())) 120 self.assertTrue( 121 t.GetQueue().GetQueueID() == queue.GetQueueID(), 122 "Thread #%d's Queue's QueueID of %d is not the same as the QueueID of its owning queue %d" % 123 (idx, 124 t.GetQueue().GetQueueID(), 125 queue.GetQueueID())) 126 127 def queues(self): 128 """Test queues inspection SB APIs without libBacktraceRecording.""" 129 exe = self.getBuildArtifact("a.out") 130 131 target = self.dbg.CreateTarget(exe) 132 self.assertTrue(target, VALID_TARGET) 133 self.main_source_spec = lldb.SBFileSpec(self.main_source) 134 break1 = target.BreakpointCreateByName("stopper", 'a.out') 135 self.assertTrue(break1, VALID_BREAKPOINT) 136 process = target.LaunchSimple( 137 [], None, self.get_process_working_directory()) 138 self.assertTrue(process, PROCESS_IS_VALID) 139 threads = lldbutil.get_threads_stopped_at_breakpoint(process, break1) 140 if len(threads) != 1: 141 self.fail("Failed to stop at breakpoint 1.") 142 143 self.inferior_process = process 144 145 queue_submittor_1 = lldb.SBQueue() 146 queue_performer_1 = lldb.SBQueue() 147 queue_performer_2 = lldb.SBQueue() 148 queue_performer_3 = lldb.SBQueue() 149 for idx in range(0, process.GetNumQueues()): 150 q = process.GetQueueAtIndex(idx) 151 if q.GetName() == "com.apple.work_submittor_1": 152 queue_submittor_1 = q 153 if q.GetName() == "com.apple.work_performer_1": 154 queue_performer_1 = q 155 if q.GetName() == "com.apple.work_performer_2": 156 queue_performer_2 = q 157 if q.GetName() == "com.apple.work_performer_3": 158 queue_performer_3 = q 159 160 self.assertTrue( 161 queue_submittor_1.IsValid() and queue_performer_1.IsValid() and queue_performer_2.IsValid() and queue_performer_3.IsValid(), 162 "Got all four expected queues: %s %s %s %s" % 163 (queue_submittor_1.IsValid(), 164 queue_performer_1.IsValid(), 165 queue_performer_2.IsValid(), 166 queue_performer_3.IsValid())) 167 168 self.check_queue_for_valid_queue_id(queue_submittor_1) 169 self.check_queue_for_valid_queue_id(queue_performer_1) 170 self.check_queue_for_valid_queue_id(queue_performer_2) 171 self.check_queue_for_valid_queue_id(queue_performer_3) 172 173 self.check_number_of_threads_owned_by_queue(queue_submittor_1, 1) 174 self.check_number_of_threads_owned_by_queue(queue_performer_1, 1) 175 self.check_number_of_threads_owned_by_queue(queue_performer_2, 1) 176 self.check_number_of_threads_owned_by_queue(queue_performer_3, 4) 177 178 self.check_queue_kind(queue_submittor_1, lldb.eQueueKindSerial) 179 self.check_queue_kind(queue_performer_1, lldb.eQueueKindSerial) 180 self.check_queue_kind(queue_performer_2, lldb.eQueueKindSerial) 181 self.check_queue_kind(queue_performer_3, lldb.eQueueKindConcurrent) 182 183 self.check_queues_threads_match_queue(queue_submittor_1) 184 self.check_queues_threads_match_queue(queue_performer_1) 185 self.check_queues_threads_match_queue(queue_performer_2) 186 self.check_queues_threads_match_queue(queue_performer_3) 187 188 # We have threads running with all the different dispatch QoS service 189 # levels - find those threads and check that we can get the correct 190 # QoS name for each of them. 191 192 user_initiated_thread = lldb.SBThread() 193 user_interactive_thread = lldb.SBThread() 194 utility_thread = lldb.SBThread() 195 unspecified_thread = lldb.SBThread() 196 background_thread = lldb.SBThread() 197 for th in process.threads: 198 if th.GetName() == "user initiated QoS": 199 user_initiated_thread = th 200 if th.GetName() == "user interactive QoS": 201 user_interactive_thread = th 202 if th.GetName() == "utility QoS": 203 utility_thread = th 204 if th.GetName() == "unspecified QoS": 205 unspecified_thread = th 206 if th.GetName() == "background QoS": 207 background_thread = th 208 209 self.assertTrue( 210 user_initiated_thread.IsValid(), 211 "Found user initiated QoS thread") 212 self.assertTrue( 213 user_interactive_thread.IsValid(), 214 "Found user interactive QoS thread") 215 self.assertTrue(utility_thread.IsValid(), "Found utility QoS thread") 216 self.assertTrue( 217 unspecified_thread.IsValid(), 218 "Found unspecified QoS thread") 219 self.assertTrue( 220 background_thread.IsValid(), 221 "Found background QoS thread") 222 223 stream = lldb.SBStream() 224 self.assertTrue( 225 user_initiated_thread.GetInfoItemByPathAsString( 226 "requested_qos.printable_name", 227 stream), 228 "Get QoS printable string for user initiated QoS thread") 229 self.assertEqual( 230 stream.GetData(), "User Initiated", 231 "user initiated QoS thread name is valid") 232 stream.Clear() 233 self.assertTrue( 234 user_interactive_thread.GetInfoItemByPathAsString( 235 "requested_qos.printable_name", 236 stream), 237 "Get QoS printable string for user interactive QoS thread") 238 self.assertEqual( 239 stream.GetData(), "User Interactive", 240 "user interactive QoS thread name is valid") 241 stream.Clear() 242 self.assertTrue( 243 utility_thread.GetInfoItemByPathAsString( 244 "requested_qos.printable_name", 245 stream), 246 "Get QoS printable string for utility QoS thread") 247 self.assertEqual( 248 stream.GetData(), "Utility", 249 "utility QoS thread name is valid") 250 stream.Clear() 251 self.assertTrue( 252 unspecified_thread.GetInfoItemByPathAsString( 253 "requested_qos.printable_name", 254 stream), 255 "Get QoS printable string for unspecified QoS thread") 256 qosName = stream.GetData() 257 self.assertTrue( 258 qosName == "User Initiated" or qosName == "Default", 259 "unspecified QoS thread name is valid: " + str(qosName)) 260 stream.Clear() 261 self.assertTrue( 262 background_thread.GetInfoItemByPathAsString( 263 "requested_qos.printable_name", 264 stream), 265 "Get QoS printable string for background QoS thread") 266 self.assertEqual( 267 stream.GetData(), "Background", 268 "background QoS thread name is valid") 269 270 @skipIfDarwin # rdar://50379398 271 def queues_with_libBacktraceRecording(self): 272 """Test queues inspection SB APIs with libBacktraceRecording present.""" 273 exe = self.getBuildArtifact("a.out") 274 275 if not os.path.isfile( 276 '/Applications/Xcode.app/Contents/Developer/usr/lib/libBacktraceRecording.dylib'): 277 self.skipTest( 278 "Skipped because libBacktraceRecording.dylib was present on the system.") 279 280 if not os.path.isfile( 281 '/usr/lib/system/introspection/libdispatch.dylib'): 282 self.skipTest( 283 "Skipped because introspection libdispatch dylib is not present.") 284 285 target = self.dbg.CreateTarget(exe) 286 self.assertTrue(target, VALID_TARGET) 287 288 self.main_source_spec = lldb.SBFileSpec(self.main_source) 289 290 break1 = target.BreakpointCreateByName("stopper", 'a.out') 291 self.assertTrue(break1, VALID_BREAKPOINT) 292 293 # Now launch the process, and do not stop at entry point. 294 libbtr_path = "/Applications/Xcode.app/Contents/Developer/usr/lib/libBacktraceRecording.dylib" 295 if self.getArchitecture() in ['arm', 'arm64', 'arm64e', 'arm64_32', 'armv7', 'armv7k']: 296 libbtr_path = "/Developer/usr/lib/libBacktraceRecording.dylib" 297 298 process = target.LaunchSimple( 299 [], 300 [ 301 'DYLD_INSERT_LIBRARIES=%s' % (libbtr_path), 302 'DYLD_LIBRARY_PATH=/usr/lib/system/introspection'], 303 self.get_process_working_directory()) 304 305 self.assertTrue(process, PROCESS_IS_VALID) 306 307 # The stop reason of the thread should be breakpoint. 308 threads = lldbutil.get_threads_stopped_at_breakpoint(process, break1) 309 if len(threads) != 1: 310 self.fail("Failed to stop at breakpoint 1.") 311 312 self.inferior_process = process 313 314 libbtr_module_filespec = lldb.SBFileSpec("libBacktraceRecording.dylib") 315 libbtr_module = target.FindModule(libbtr_module_filespec) 316 if not libbtr_module.IsValid(): 317 self.skipTest( 318 "Skipped because libBacktraceRecording.dylib was not loaded into the process.") 319 320 self.assertTrue( 321 process.GetNumQueues() >= 4, 322 "Found the correct number of queues.") 323 324 queue_submittor_1 = lldb.SBQueue() 325 queue_performer_1 = lldb.SBQueue() 326 queue_performer_2 = lldb.SBQueue() 327 queue_performer_3 = lldb.SBQueue() 328 for idx in range(0, process.GetNumQueues()): 329 q = process.GetQueueAtIndex(idx) 330 if "LLDB_COMMAND_TRACE" in os.environ: 331 print("Queue with id %s has name %s" % (q.GetQueueID(), q.GetName())) 332 if q.GetName() == "com.apple.work_submittor_1": 333 queue_submittor_1 = q 334 if q.GetName() == "com.apple.work_performer_1": 335 queue_performer_1 = q 336 if q.GetName() == "com.apple.work_performer_2": 337 queue_performer_2 = q 338 if q.GetName() == "com.apple.work_performer_3": 339 queue_performer_3 = q 340 if q.GetName() == "com.apple.main-thread": 341 if q.GetNumThreads() == 0: 342 print("Cannot get thread <=> queue associations") 343 return 344 345 self.assertTrue( 346 queue_submittor_1.IsValid() and queue_performer_1.IsValid() and queue_performer_2.IsValid() and queue_performer_3.IsValid(), 347 "Got all four expected queues: %s %s %s %s" % 348 (queue_submittor_1.IsValid(), 349 queue_performer_1.IsValid(), 350 queue_performer_2.IsValid(), 351 queue_performer_3.IsValid())) 352 353 self.check_queue_for_valid_queue_id(queue_submittor_1) 354 self.check_queue_for_valid_queue_id(queue_performer_1) 355 self.check_queue_for_valid_queue_id(queue_performer_2) 356 self.check_queue_for_valid_queue_id(queue_performer_3) 357 358 self.check_running_and_pending_items_on_queue(queue_submittor_1, 1, 0) 359 self.check_running_and_pending_items_on_queue(queue_performer_1, 1, 3) 360 self.check_running_and_pending_items_on_queue( 361 queue_performer_2, 1, 9999) 362 self.check_running_and_pending_items_on_queue(queue_performer_3, 4, 0) 363 364 self.check_number_of_threads_owned_by_queue(queue_submittor_1, 1) 365 self.check_number_of_threads_owned_by_queue(queue_performer_1, 1) 366 self.check_number_of_threads_owned_by_queue(queue_performer_2, 1) 367 self.check_number_of_threads_owned_by_queue(queue_performer_3, 4) 368 369 self.check_queue_kind(queue_submittor_1, lldb.eQueueKindSerial) 370 self.check_queue_kind(queue_performer_1, lldb.eQueueKindSerial) 371 self.check_queue_kind(queue_performer_2, lldb.eQueueKindSerial) 372 self.check_queue_kind(queue_performer_3, lldb.eQueueKindConcurrent) 373 374 self.check_queues_threads_match_queue(queue_submittor_1) 375 self.check_queues_threads_match_queue(queue_performer_1) 376 self.check_queues_threads_match_queue(queue_performer_2) 377 self.check_queues_threads_match_queue(queue_performer_3) 378 379 self.assertTrue(queue_performer_2.GetPendingItemAtIndex( 380 0).IsValid(), "queue 2's pending item #0 is valid") 381 self.assertTrue(queue_performer_2.GetPendingItemAtIndex(0).GetAddress().GetSymbol( 382 ).GetName() == "doing_the_work_2", "queue 2's pending item #0 should be doing_the_work_2") 383 self.assertTrue( 384 queue_performer_2.GetNumPendingItems() == 9999, 385 "verify that queue 2 still has 9999 pending items") 386 self.assertTrue(queue_performer_2.GetPendingItemAtIndex( 387 9998).IsValid(), "queue 2's pending item #9998 is valid") 388 self.assertTrue(queue_performer_2.GetPendingItemAtIndex(9998).GetAddress().GetSymbol( 389 ).GetName() == "doing_the_work_2", "queue 2's pending item #0 should be doing_the_work_2") 390 self.assertTrue(queue_performer_2.GetPendingItemAtIndex( 391 9999).IsValid() == False, "queue 2's pending item #9999 is invalid") 392