1""" 2Tests basic ThreadSanitizer support (detecting a data race). 3""" 4 5import lldb 6from lldbsuite.test.lldbtest import * 7from lldbsuite.test.decorators import * 8import lldbsuite.test.lldbutil as lldbutil 9import json 10 11 12class TsanBasicTestCase(TestBase): 13 @expectedFailureAll( 14 oslist=["linux"], 15 bugnumber="non-core functionality, need to reenable and fix later (DES 2014.11.07)", 16 ) 17 @expectedFailureNetBSD 18 @skipIfFreeBSD # llvm.org/pr21136 runtimes not yet available by default 19 @skipIfRemote 20 @skipUnlessThreadSanitizer 21 @no_debug_info_test 22 def test(self): 23 self.build() 24 self.tsan_tests() 25 26 def setUp(self): 27 # Call super's setUp(). 28 TestBase.setUp(self) 29 self.line_malloc = line_number("main.c", "// malloc line") 30 self.line_thread1 = line_number("main.c", "// thread1 line") 31 self.line_thread2 = line_number("main.c", "// thread2 line") 32 33 def tsan_tests(self): 34 exe = self.getBuildArtifact("a.out") 35 self.expect("file " + exe, patterns=["Current executable set to .*a.out"]) 36 37 self.runCmd("run") 38 39 stop_reason = ( 40 self.dbg.GetSelectedTarget().process.GetSelectedThread().GetStopReason() 41 ) 42 if stop_reason == lldb.eStopReasonExec: 43 # On OS X 10.10 and older, we need to re-exec to enable 44 # interceptors. 45 self.runCmd("continue") 46 47 # the stop reason of the thread should be breakpoint. 48 self.expect( 49 "thread list", 50 "A data race should be detected", 51 substrs=["stopped", "stop reason = Data race detected"], 52 ) 53 54 self.assertEqual( 55 self.dbg.GetSelectedTarget().process.GetSelectedThread().GetStopReason(), 56 lldb.eStopReasonInstrumentation, 57 ) 58 59 # test that the TSan dylib is present 60 self.expect( 61 "image lookup -n __tsan_get_current_report", 62 "__tsan_get_current_report should be present", 63 substrs=["1 match found"], 64 ) 65 66 # We should be stopped in __tsan_on_report 67 process = self.dbg.GetSelectedTarget().process 68 thread = process.GetSelectedThread() 69 frame = thread.GetSelectedFrame() 70 self.assertIn("__tsan_on_report", frame.GetFunctionName()) 71 72 # The stopped thread backtrace should contain either line1 or line2 73 # from main.c. 74 found = False 75 for i in range(0, thread.GetNumFrames()): 76 frame = thread.GetFrameAtIndex(i) 77 if frame.GetLineEntry().GetFileSpec().GetFilename() == "main.c": 78 if frame.GetLineEntry().GetLine() == self.line_thread1: 79 found = True 80 if frame.GetLineEntry().GetLine() == self.line_thread2: 81 found = True 82 self.assertTrue(found) 83 84 self.expect( 85 "thread info -s", 86 "The extended stop info should contain the TSan provided fields", 87 substrs=["instrumentation_class", "description", "mops"], 88 ) 89 90 output_lines = self.res.GetOutput().split("\n") 91 json_line = "\n".join(output_lines[2:]) 92 data = json.loads(json_line) 93 self.assertEqual(data["instrumentation_class"], "ThreadSanitizer") 94 self.assertEqual(data["issue_type"], "data-race") 95 self.assertEqual(len(data["mops"]), 2) 96 97 backtraces = thread.GetStopReasonExtendedBacktraces( 98 lldb.eInstrumentationRuntimeTypeAddressSanitizer 99 ) 100 self.assertEqual(backtraces.GetSize(), 0) 101 102 backtraces = thread.GetStopReasonExtendedBacktraces( 103 lldb.eInstrumentationRuntimeTypeThreadSanitizer 104 ) 105 self.assertGreaterEqual(backtraces.GetSize(), 2) 106 107 # First backtrace is a memory operation 108 thread = backtraces.GetThreadAtIndex(0) 109 found = False 110 for i in range(0, thread.GetNumFrames()): 111 frame = thread.GetFrameAtIndex(i) 112 if frame.GetLineEntry().GetFileSpec().GetFilename() == "main.c": 113 if frame.GetLineEntry().GetLine() == self.line_thread1: 114 found = True 115 if frame.GetLineEntry().GetLine() == self.line_thread2: 116 found = True 117 self.assertTrue(found) 118 119 # Second backtrace is a memory operation 120 thread = backtraces.GetThreadAtIndex(1) 121 found = False 122 for i in range(0, thread.GetNumFrames()): 123 frame = thread.GetFrameAtIndex(i) 124 if frame.GetLineEntry().GetFileSpec().GetFilename() == "main.c": 125 if frame.GetLineEntry().GetLine() == self.line_thread1: 126 found = True 127 if frame.GetLineEntry().GetLine() == self.line_thread2: 128 found = True 129 self.assertTrue(found) 130 131 self.runCmd("continue") 132 133 # the stop reason of the thread should be a SIGABRT. 134 self.expect( 135 "thread list", 136 "We should be stopped due a SIGABRT", 137 substrs=["stopped", "stop reason = signal SIGABRT"], 138 ) 139 140 # test that we're in pthread_kill now (TSan abort the process) 141 self.expect( 142 "thread list", 143 "We should be stopped in pthread_kill", 144 substrs=["pthread_kill"], 145 ) 146