149 lines · python
1"""2Tests basic ThreadSanitizer support (detecting a data race).3"""4 5import lldb6from lldbsuite.test.lldbtest import *7from lldbsuite.test.decorators import *8import lldbsuite.test.lldbutil as lldbutil9import json10 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 @expectedFailureNetBSD18 @skipIfFreeBSD # llvm.org/pr21136 runtimes not yet available by default19 @skipIfRemote20 @skipUnlessThreadSanitizer21 @no_debug_info_test22 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 enable44 # 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 present60 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 process = self.dbg.GetSelectedTarget().process67 thread = process.GetSelectedThread()68 frame = thread.GetSelectedFrame()69 if self.platformIsDarwin():70 # We should not be stopped in the sanitizer library.71 self.assertIn("f2", frame.GetFunctionName())72 else:73 self.assertIn("__tsan_on_report", frame.GetFunctionName())74 75 # The stopped thread backtrace should contain either line1 or line276 # from main.c.77 found = False78 for i in range(0, thread.GetNumFrames()):79 frame = thread.GetFrameAtIndex(i)80 if frame.GetLineEntry().GetFileSpec().GetFilename() == "main.c":81 if frame.GetLineEntry().GetLine() == self.line_thread1:82 found = True83 if frame.GetLineEntry().GetLine() == self.line_thread2:84 found = True85 self.assertTrue(found)86 87 self.expect(88 "thread info -s",89 "The extended stop info should contain the TSan provided fields",90 substrs=["instrumentation_class", "description", "mops"],91 )92 93 output_lines = self.res.GetOutput().split("\n")94 json_line = "\n".join(output_lines[2:])95 data = json.loads(json_line)96 self.assertEqual(data["instrumentation_class"], "ThreadSanitizer")97 self.assertEqual(data["issue_type"], "data-race")98 self.assertEqual(len(data["mops"]), 2)99 100 backtraces = thread.GetStopReasonExtendedBacktraces(101 lldb.eInstrumentationRuntimeTypeAddressSanitizer102 )103 self.assertEqual(backtraces.GetSize(), 0)104 105 backtraces = thread.GetStopReasonExtendedBacktraces(106 lldb.eInstrumentationRuntimeTypeThreadSanitizer107 )108 self.assertGreaterEqual(backtraces.GetSize(), 2)109 110 # First backtrace is a memory operation111 thread = backtraces.GetThreadAtIndex(0)112 found = False113 for i in range(0, thread.GetNumFrames()):114 frame = thread.GetFrameAtIndex(i)115 if frame.GetLineEntry().GetFileSpec().GetFilename() == "main.c":116 if frame.GetLineEntry().GetLine() == self.line_thread1:117 found = True118 if frame.GetLineEntry().GetLine() == self.line_thread2:119 found = True120 self.assertTrue(found)121 122 # Second backtrace is a memory operation123 thread = backtraces.GetThreadAtIndex(1)124 found = False125 for i in range(0, thread.GetNumFrames()):126 frame = thread.GetFrameAtIndex(i)127 if frame.GetLineEntry().GetFileSpec().GetFilename() == "main.c":128 if frame.GetLineEntry().GetLine() == self.line_thread1:129 found = True130 if frame.GetLineEntry().GetLine() == self.line_thread2:131 found = True132 self.assertTrue(found)133 134 self.runCmd("continue")135 136 # the stop reason of the thread should be a SIGABRT.137 self.expect(138 "thread list",139 "We should be stopped due a SIGABRT",140 substrs=["stopped", "stop reason = signal SIGABRT"],141 )142 143 # test that we're in pthread_kill now (TSan abort the process)144 self.expect(145 "thread list",146 "We should be stopped in pthread_kill",147 substrs=["pthread_kill"],148 )149