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 background_thread = lldb.SBThread() 196 for th in process.threads: 197 if th.GetName() == "user initiated QoS": 198 user_initiated_thread = th 199 if th.GetName() == "user interactive QoS": 200 user_interactive_thread = th 201 if th.GetName() == "utility QoS": 202 utility_thread = th 203 if th.GetName() == "background QoS": 204 background_thread = th 205 206 self.assertTrue( 207 user_initiated_thread.IsValid(), 208 "Found user initiated QoS thread") 209 self.assertTrue( 210 user_interactive_thread.IsValid(), 211 "Found user interactive QoS thread") 212 self.assertTrue(utility_thread.IsValid(), "Found utility QoS thread") 213 self.assertTrue( 214 background_thread.IsValid(), 215 "Found background QoS thread") 216 217 stream = lldb.SBStream() 218 self.assertTrue( 219 user_initiated_thread.GetInfoItemByPathAsString( 220 "requested_qos.printable_name", 221 stream), 222 "Get QoS printable string for user initiated QoS thread") 223 self.assertEqual( 224 stream.GetData(), "User Initiated", 225 "user initiated QoS thread name is valid") 226 stream.Clear() 227 self.assertTrue( 228 user_interactive_thread.GetInfoItemByPathAsString( 229 "requested_qos.printable_name", 230 stream), 231 "Get QoS printable string for user interactive QoS thread") 232 self.assertEqual( 233 stream.GetData(), "User Interactive", 234 "user interactive QoS thread name is valid") 235 stream.Clear() 236 self.assertTrue( 237 utility_thread.GetInfoItemByPathAsString( 238 "requested_qos.printable_name", 239 stream), 240 "Get QoS printable string for utility QoS thread") 241 self.assertEqual( 242 stream.GetData(), "Utility", 243 "utility QoS thread name is valid") 244 stream.Clear() 245 self.assertTrue( 246 background_thread.GetInfoItemByPathAsString( 247 "requested_qos.printable_name", 248 stream), 249 "Get QoS printable string for background QoS thread") 250 self.assertEqual( 251 stream.GetData(), "Background", 252 "background QoS thread name is valid") 253 254 @skipIfDarwin # rdar://50379398 255 def queues_with_libBacktraceRecording(self): 256 """Test queues inspection SB APIs with libBacktraceRecording present.""" 257 exe = self.getBuildArtifact("a.out") 258 259 if not os.path.isfile( 260 '/Applications/Xcode.app/Contents/Developer/usr/lib/libBacktraceRecording.dylib'): 261 self.skipTest( 262 "Skipped because libBacktraceRecording.dylib was present on the system.") 263 264 if not os.path.isfile( 265 '/usr/lib/system/introspection/libdispatch.dylib'): 266 self.skipTest( 267 "Skipped because introspection libdispatch dylib is not present.") 268 269 target = self.dbg.CreateTarget(exe) 270 self.assertTrue(target, VALID_TARGET) 271 272 self.main_source_spec = lldb.SBFileSpec(self.main_source) 273 274 break1 = target.BreakpointCreateByName("stopper", 'a.out') 275 self.assertTrue(break1, VALID_BREAKPOINT) 276 277 # Now launch the process, and do not stop at entry point. 278 libbtr_path = "/Applications/Xcode.app/Contents/Developer/usr/lib/libBacktraceRecording.dylib" 279 if self.getArchitecture() in ['arm', 'arm64', 'arm64e', 'arm64_32', 'armv7', 'armv7k']: 280 libbtr_path = "/Developer/usr/lib/libBacktraceRecording.dylib" 281 282 process = target.LaunchSimple( 283 [], 284 [ 285 'DYLD_INSERT_LIBRARIES=%s' % (libbtr_path), 286 'DYLD_LIBRARY_PATH=/usr/lib/system/introspection'], 287 self.get_process_working_directory()) 288 289 self.assertTrue(process, PROCESS_IS_VALID) 290 291 # The stop reason of the thread should be breakpoint. 292 threads = lldbutil.get_threads_stopped_at_breakpoint(process, break1) 293 if len(threads) != 1: 294 self.fail("Failed to stop at breakpoint 1.") 295 296 self.inferior_process = process 297 298 libbtr_module_filespec = lldb.SBFileSpec("libBacktraceRecording.dylib") 299 libbtr_module = target.FindModule(libbtr_module_filespec) 300 if not libbtr_module.IsValid(): 301 self.skipTest( 302 "Skipped because libBacktraceRecording.dylib was not loaded into the process.") 303 304 self.assertTrue( 305 process.GetNumQueues() >= 4, 306 "Found the correct number of queues.") 307 308 queue_submittor_1 = lldb.SBQueue() 309 queue_performer_1 = lldb.SBQueue() 310 queue_performer_2 = lldb.SBQueue() 311 queue_performer_3 = lldb.SBQueue() 312 for idx in range(0, process.GetNumQueues()): 313 q = process.GetQueueAtIndex(idx) 314 if "LLDB_COMMAND_TRACE" in os.environ: 315 print("Queue with id %s has name %s" % (q.GetQueueID(), q.GetName())) 316 if q.GetName() == "com.apple.work_submittor_1": 317 queue_submittor_1 = q 318 if q.GetName() == "com.apple.work_performer_1": 319 queue_performer_1 = q 320 if q.GetName() == "com.apple.work_performer_2": 321 queue_performer_2 = q 322 if q.GetName() == "com.apple.work_performer_3": 323 queue_performer_3 = q 324 if q.GetName() == "com.apple.main-thread": 325 if q.GetNumThreads() == 0: 326 print("Cannot get thread <=> queue associations") 327 return 328 329 self.assertTrue( 330 queue_submittor_1.IsValid() and queue_performer_1.IsValid() and queue_performer_2.IsValid() and queue_performer_3.IsValid(), 331 "Got all four expected queues: %s %s %s %s" % 332 (queue_submittor_1.IsValid(), 333 queue_performer_1.IsValid(), 334 queue_performer_2.IsValid(), 335 queue_performer_3.IsValid())) 336 337 self.check_queue_for_valid_queue_id(queue_submittor_1) 338 self.check_queue_for_valid_queue_id(queue_performer_1) 339 self.check_queue_for_valid_queue_id(queue_performer_2) 340 self.check_queue_for_valid_queue_id(queue_performer_3) 341 342 self.check_running_and_pending_items_on_queue(queue_submittor_1, 1, 0) 343 self.check_running_and_pending_items_on_queue(queue_performer_1, 1, 3) 344 self.check_running_and_pending_items_on_queue( 345 queue_performer_2, 1, 9999) 346 self.check_running_and_pending_items_on_queue(queue_performer_3, 4, 0) 347 348 self.check_number_of_threads_owned_by_queue(queue_submittor_1, 1) 349 self.check_number_of_threads_owned_by_queue(queue_performer_1, 1) 350 self.check_number_of_threads_owned_by_queue(queue_performer_2, 1) 351 self.check_number_of_threads_owned_by_queue(queue_performer_3, 4) 352 353 self.check_queue_kind(queue_submittor_1, lldb.eQueueKindSerial) 354 self.check_queue_kind(queue_performer_1, lldb.eQueueKindSerial) 355 self.check_queue_kind(queue_performer_2, lldb.eQueueKindSerial) 356 self.check_queue_kind(queue_performer_3, lldb.eQueueKindConcurrent) 357 358 self.check_queues_threads_match_queue(queue_submittor_1) 359 self.check_queues_threads_match_queue(queue_performer_1) 360 self.check_queues_threads_match_queue(queue_performer_2) 361 self.check_queues_threads_match_queue(queue_performer_3) 362 363 self.assertTrue(queue_performer_2.GetPendingItemAtIndex( 364 0).IsValid(), "queue 2's pending item #0 is valid") 365 self.assertTrue(queue_performer_2.GetPendingItemAtIndex(0).GetAddress().GetSymbol( 366 ).GetName() == "doing_the_work_2", "queue 2's pending item #0 should be doing_the_work_2") 367 self.assertTrue( 368 queue_performer_2.GetNumPendingItems() == 9999, 369 "verify that queue 2 still has 9999 pending items") 370 self.assertTrue(queue_performer_2.GetPendingItemAtIndex( 371 9998).IsValid(), "queue 2's pending item #9998 is valid") 372 self.assertTrue(queue_performer_2.GetPendingItemAtIndex(9998).GetAddress().GetSymbol( 373 ).GetName() == "doing_the_work_2", "queue 2's pending item #0 should be doing_the_work_2") 374 self.assertTrue(queue_performer_2.GetPendingItemAtIndex( 375 9999).IsValid() == False, "queue 2's pending item #9999 is invalid") 376