1061da546Spatrick""" 2061da546SpatrickA stress-test of sorts for LLDB's handling of threads in the inferior. 3061da546Spatrick 4061da546SpatrickThis test sets a breakpoint in the main thread where test parameters (numbers of 5061da546Spatrickthreads) can be adjusted, runs the inferior to that point, and modifies the 6061da546Spatricklocals that control the event thread counts. This test also sets a breakpoint in 7061da546Spatrickbreakpoint_func (the function executed by each 'breakpoint' thread) and a 8061da546Spatrickwatchpoint on a global modified in watchpoint_func. The inferior is continued 9061da546Spatrickuntil exit or a crash takes place, and the number of events seen by LLDB is 10061da546Spatrickverified to match the expected number of events. 11061da546Spatrick""" 12061da546Spatrick 13061da546Spatrick 14061da546Spatrick 15061da546Spatrickimport unittest2 16061da546Spatrickimport lldb 17061da546Spatrickfrom lldbsuite.test.decorators import * 18061da546Spatrickfrom lldbsuite.test.lldbtest import * 19061da546Spatrickfrom lldbsuite.test import lldbutil 20061da546Spatrick 21061da546Spatrick 22061da546Spatrickclass ConcurrentEventsBase(TestBase): 23061da546Spatrick 24061da546Spatrick # Concurrency is the primary test factor here, not debug info variants. 25061da546Spatrick NO_DEBUG_INFO_TESTCASE = True 26061da546Spatrick 27061da546Spatrick def setUp(self): 28061da546Spatrick # Call super's setUp(). 29061da546Spatrick super(ConcurrentEventsBase, self).setUp() 30061da546Spatrick # Find the line number for our breakpoint. 31061da546Spatrick self.filename = 'main.cpp' 32061da546Spatrick self.thread_breakpoint_line = line_number( 33061da546Spatrick self.filename, '// Set breakpoint here') 34061da546Spatrick self.setup_breakpoint_line = line_number( 35061da546Spatrick self.filename, '// Break here and adjust num') 36061da546Spatrick self.finish_breakpoint_line = line_number( 37061da546Spatrick self.filename, '// Break here and verify one thread is active') 38061da546Spatrick 39061da546Spatrick def describe_threads(self): 40061da546Spatrick ret = [] 41061da546Spatrick for x in self.inferior_process: 42061da546Spatrick id = x.GetIndexID() 43061da546Spatrick reason = x.GetStopReason() 44061da546Spatrick status = "stopped" if x.IsStopped() else "running" 45061da546Spatrick reason_str = lldbutil.stop_reason_to_str(reason) 46061da546Spatrick if reason == lldb.eStopReasonBreakpoint: 47061da546Spatrick bpid = x.GetStopReasonDataAtIndex(0) 48061da546Spatrick bp = self.inferior_target.FindBreakpointByID(bpid) 49061da546Spatrick reason_str = "%s hit %d times" % ( 50061da546Spatrick lldbutil.get_description(bp), bp.GetHitCount()) 51061da546Spatrick elif reason == lldb.eStopReasonWatchpoint: 52061da546Spatrick watchid = x.GetStopReasonDataAtIndex(0) 53061da546Spatrick watch = self.inferior_target.FindWatchpointByID(watchid) 54061da546Spatrick reason_str = "%s hit %d times" % ( 55061da546Spatrick lldbutil.get_description(watch), watch.GetHitCount()) 56061da546Spatrick elif reason == lldb.eStopReasonSignal: 57061da546Spatrick signals = self.inferior_process.GetUnixSignals() 58061da546Spatrick signal_name = signals.GetSignalAsCString( 59061da546Spatrick x.GetStopReasonDataAtIndex(0)) 60061da546Spatrick reason_str = "signal %s" % signal_name 61061da546Spatrick 62061da546Spatrick location = "\t".join([lldbutil.get_description( 63061da546Spatrick x.GetFrameAtIndex(i)) for i in range(x.GetNumFrames())]) 64061da546Spatrick ret.append( 65061da546Spatrick "thread %d %s due to %s at\n\t%s" % 66061da546Spatrick (id, status, reason_str, location)) 67061da546Spatrick return ret 68061da546Spatrick 69061da546Spatrick def add_breakpoint(self, line, descriptions): 70061da546Spatrick """ Adds a breakpoint at self.filename:line and appends its description to descriptions, and 71061da546Spatrick returns the LLDB SBBreakpoint object. 72061da546Spatrick """ 73061da546Spatrick 74061da546Spatrick bpno = lldbutil.run_break_set_by_file_and_line( 75061da546Spatrick self, self.filename, line, num_expected_locations=-1) 76061da546Spatrick bp = self.inferior_target.FindBreakpointByID(bpno) 77*f6aab3d8Srobert descriptions.append(": file = 'main.cpp', line = %d" % line) 78061da546Spatrick return bp 79061da546Spatrick 80061da546Spatrick def inferior_done(self): 81061da546Spatrick """ Returns true if the inferior is done executing all the event threads (and is stopped at self.finish_breakpoint, 82061da546Spatrick or has terminated execution. 83061da546Spatrick """ 84061da546Spatrick return self.finish_breakpoint.GetHitCount() > 0 or \ 85061da546Spatrick self.crash_count > 0 or \ 86061da546Spatrick self.inferior_process.GetState() == lldb.eStateExited 87061da546Spatrick 88061da546Spatrick def count_signaled_threads(self): 89061da546Spatrick count = 0 90061da546Spatrick for thread in self.inferior_process: 91061da546Spatrick if thread.GetStopReason() == lldb.eStopReasonSignal and thread.GetStopReasonDataAtIndex( 92061da546Spatrick 0) == self.inferior_process.GetUnixSignals().GetSignalNumberFromName('SIGUSR1'): 93061da546Spatrick count += 1 94061da546Spatrick return count 95061da546Spatrick 96061da546Spatrick def do_thread_actions(self, 97061da546Spatrick num_breakpoint_threads=0, 98061da546Spatrick num_signal_threads=0, 99061da546Spatrick num_watchpoint_threads=0, 100061da546Spatrick num_crash_threads=0, 101061da546Spatrick num_delay_breakpoint_threads=0, 102061da546Spatrick num_delay_signal_threads=0, 103061da546Spatrick num_delay_watchpoint_threads=0, 104061da546Spatrick num_delay_crash_threads=0): 105061da546Spatrick """ Sets a breakpoint in the main thread where test parameters (numbers of threads) can be adjusted, runs the inferior 106061da546Spatrick to that point, and modifies the locals that control the event thread counts. Also sets a breakpoint in 107061da546Spatrick breakpoint_func (the function executed by each 'breakpoint' thread) and a watchpoint on a global modified in 108061da546Spatrick watchpoint_func. The inferior is continued until exit or a crash takes place, and the number of events seen by LLDB 109061da546Spatrick is verified to match the expected number of events. 110061da546Spatrick """ 111061da546Spatrick exe = self.getBuildArtifact("a.out") 112061da546Spatrick self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) 113061da546Spatrick 114061da546Spatrick # Get the target 115061da546Spatrick self.inferior_target = self.dbg.GetSelectedTarget() 116061da546Spatrick 117061da546Spatrick expected_bps = [] 118061da546Spatrick 119061da546Spatrick # Initialize all the breakpoints (main thread/aux thread) 120061da546Spatrick self.setup_breakpoint = self.add_breakpoint( 121061da546Spatrick self.setup_breakpoint_line, expected_bps) 122061da546Spatrick self.finish_breakpoint = self.add_breakpoint( 123061da546Spatrick self.finish_breakpoint_line, expected_bps) 124061da546Spatrick 125061da546Spatrick # Set the thread breakpoint 126061da546Spatrick if num_breakpoint_threads + num_delay_breakpoint_threads > 0: 127061da546Spatrick self.thread_breakpoint = self.add_breakpoint( 128061da546Spatrick self.thread_breakpoint_line, expected_bps) 129061da546Spatrick 130061da546Spatrick # Verify breakpoints 131061da546Spatrick self.expect( 132061da546Spatrick "breakpoint list -f", 133061da546Spatrick "Breakpoint locations shown correctly", 134061da546Spatrick substrs=expected_bps) 135061da546Spatrick 136061da546Spatrick # Run the program. 137061da546Spatrick self.runCmd("run", RUN_SUCCEEDED) 138061da546Spatrick 139061da546Spatrick # Check we are at line self.setup_breakpoint 140061da546Spatrick self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT, 141061da546Spatrick substrs=["stop reason = breakpoint 1."]) 142061da546Spatrick 143061da546Spatrick # Initialize the (single) watchpoint on the global variable (g_watchme) 144061da546Spatrick if num_watchpoint_threads + num_delay_watchpoint_threads > 0: 145061da546Spatrick self.runCmd("watchpoint set variable g_watchme") 146061da546Spatrick for w in self.inferior_target.watchpoint_iter(): 147061da546Spatrick self.thread_watchpoint = w 148061da546Spatrick self.assertTrue( 149061da546Spatrick "g_watchme" in str( 150061da546Spatrick self.thread_watchpoint), 151061da546Spatrick "Watchpoint location not shown correctly") 152061da546Spatrick 153061da546Spatrick # Get the process 154061da546Spatrick self.inferior_process = self.inferior_target.GetProcess() 155061da546Spatrick 156061da546Spatrick # We should be stopped at the setup site where we can set the number of 157061da546Spatrick # threads doing each action (break/crash/signal/watch) 158061da546Spatrick self.assertEqual( 159061da546Spatrick self.inferior_process.GetNumThreads(), 160061da546Spatrick 1, 161061da546Spatrick 'Expected to stop before any additional threads are spawned.') 162061da546Spatrick 163061da546Spatrick self.runCmd("expr num_breakpoint_threads=%d" % num_breakpoint_threads) 164061da546Spatrick self.runCmd("expr num_crash_threads=%d" % num_crash_threads) 165061da546Spatrick self.runCmd("expr num_signal_threads=%d" % num_signal_threads) 166061da546Spatrick self.runCmd("expr num_watchpoint_threads=%d" % num_watchpoint_threads) 167061da546Spatrick 168061da546Spatrick self.runCmd( 169061da546Spatrick "expr num_delay_breakpoint_threads=%d" % 170061da546Spatrick num_delay_breakpoint_threads) 171061da546Spatrick self.runCmd( 172061da546Spatrick "expr num_delay_crash_threads=%d" % 173061da546Spatrick num_delay_crash_threads) 174061da546Spatrick self.runCmd( 175061da546Spatrick "expr num_delay_signal_threads=%d" % 176061da546Spatrick num_delay_signal_threads) 177061da546Spatrick self.runCmd( 178061da546Spatrick "expr num_delay_watchpoint_threads=%d" % 179061da546Spatrick num_delay_watchpoint_threads) 180061da546Spatrick 181061da546Spatrick # Continue the inferior so threads are spawned 182061da546Spatrick self.runCmd("continue") 183061da546Spatrick 184061da546Spatrick # Make sure we see all the threads. The inferior program's threads all synchronize with a pseudo-barrier; that is, 185061da546Spatrick # the inferior program ensures all threads are started and running 186061da546Spatrick # before any thread triggers its 'event'. 187061da546Spatrick num_threads = self.inferior_process.GetNumThreads() 188061da546Spatrick expected_num_threads = num_breakpoint_threads + num_delay_breakpoint_threads \ 189061da546Spatrick + num_signal_threads + num_delay_signal_threads \ 190061da546Spatrick + num_watchpoint_threads + num_delay_watchpoint_threads \ 191061da546Spatrick + num_crash_threads + num_delay_crash_threads + 1 192061da546Spatrick self.assertEqual( 193061da546Spatrick num_threads, 194061da546Spatrick expected_num_threads, 195061da546Spatrick 'Expected to see %d threads, but seeing %d. Details:\n%s' % 196061da546Spatrick (expected_num_threads, 197061da546Spatrick num_threads, 198061da546Spatrick "\n\t".join( 199061da546Spatrick self.describe_threads()))) 200061da546Spatrick 201061da546Spatrick self.signal_count = self.count_signaled_threads() 202061da546Spatrick self.crash_count = len( 203061da546Spatrick lldbutil.get_crashed_threads( 204061da546Spatrick self, self.inferior_process)) 205061da546Spatrick 206061da546Spatrick # Run to completion (or crash) 207061da546Spatrick while not self.inferior_done(): 208061da546Spatrick if self.TraceOn(): 209061da546Spatrick self.runCmd("thread backtrace all") 210061da546Spatrick self.runCmd("continue") 211061da546Spatrick self.signal_count += self.count_signaled_threads() 212061da546Spatrick self.crash_count += len( 213061da546Spatrick lldbutil.get_crashed_threads( 214061da546Spatrick self, self.inferior_process)) 215061da546Spatrick 216061da546Spatrick if num_crash_threads > 0 or num_delay_crash_threads > 0: 217061da546Spatrick # Expecting a crash 218061da546Spatrick self.assertTrue( 219061da546Spatrick self.crash_count > 0, 220061da546Spatrick "Expecting at least one thread to crash. Details: %s" % 221061da546Spatrick "\t\n".join( 222061da546Spatrick self.describe_threads())) 223061da546Spatrick 224061da546Spatrick # Ensure the zombie process is reaped 225061da546Spatrick self.runCmd("process kill") 226061da546Spatrick 227061da546Spatrick elif num_crash_threads == 0 and num_delay_crash_threads == 0: 228061da546Spatrick # There should be a single active thread (the main one) which hit 229061da546Spatrick # the breakpoint after joining 230061da546Spatrick self.assertEqual( 231061da546Spatrick 1, 232061da546Spatrick self.finish_breakpoint.GetHitCount(), 233061da546Spatrick "Expected main thread (finish) breakpoint to be hit once") 234061da546Spatrick 235061da546Spatrick num_threads = self.inferior_process.GetNumThreads() 236061da546Spatrick self.assertEqual( 237061da546Spatrick 1, 238061da546Spatrick num_threads, 239061da546Spatrick "Expecting 1 thread but seeing %d. Details:%s" % 240061da546Spatrick (num_threads, 241061da546Spatrick "\n\t".join( 242061da546Spatrick self.describe_threads()))) 243061da546Spatrick self.runCmd("continue") 244061da546Spatrick 245061da546Spatrick # The inferior process should have exited without crashing 246061da546Spatrick self.assertEqual( 247061da546Spatrick 0, 248061da546Spatrick self.crash_count, 249061da546Spatrick "Unexpected thread(s) in crashed state") 250061da546Spatrick self.assertEqual( 251061da546Spatrick self.inferior_process.GetState(), 252061da546Spatrick lldb.eStateExited, 253061da546Spatrick PROCESS_EXITED) 254061da546Spatrick 255061da546Spatrick # Verify the number of actions took place matches expected numbers 256061da546Spatrick expected_breakpoint_threads = num_delay_breakpoint_threads + num_breakpoint_threads 257061da546Spatrick breakpoint_hit_count = self.thread_breakpoint.GetHitCount( 258061da546Spatrick ) if expected_breakpoint_threads > 0 else 0 259061da546Spatrick self.assertEqual( 260061da546Spatrick expected_breakpoint_threads, 261061da546Spatrick breakpoint_hit_count, 262061da546Spatrick "Expected %d breakpoint hits, but got %d" % 263061da546Spatrick (expected_breakpoint_threads, 264061da546Spatrick breakpoint_hit_count)) 265061da546Spatrick 266061da546Spatrick expected_signal_threads = num_delay_signal_threads + num_signal_threads 267061da546Spatrick self.assertEqual( 268061da546Spatrick expected_signal_threads, 269061da546Spatrick self.signal_count, 270061da546Spatrick "Expected %d stops due to signal delivery, but got %d" % 271061da546Spatrick (expected_signal_threads, 272061da546Spatrick self.signal_count)) 273061da546Spatrick 274061da546Spatrick expected_watchpoint_threads = num_delay_watchpoint_threads + num_watchpoint_threads 275061da546Spatrick watchpoint_hit_count = self.thread_watchpoint.GetHitCount( 276061da546Spatrick ) if expected_watchpoint_threads > 0 else 0 277061da546Spatrick self.assertEqual( 278061da546Spatrick expected_watchpoint_threads, 279061da546Spatrick watchpoint_hit_count, 280061da546Spatrick "Expected %d watchpoint hits, got %d" % 281061da546Spatrick (expected_watchpoint_threads, 282061da546Spatrick watchpoint_hit_count)) 283