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