reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
"""
Tests basic ThreadSanitizer support (detecting a data race).
"""

import lldb
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
import lldbsuite.test.lldbutil as lldbutil
import json


class TsanBasicTestCase(TestBase):

    mydir = TestBase.compute_mydir(__file__)

    @expectedFailureAll(
        oslist=["linux"],
        bugnumber="non-core functionality, need to reenable and fix later (DES 2014.11.07)")
    @expectedFailureNetBSD
    @skipIfFreeBSD  # llvm.org/pr21136 runtimes not yet available by default
    @skipIfRemote
    @skipUnlessThreadSanitizer
    def test(self):
        self.build()
        self.tsan_tests()

    def setUp(self):
        # Call super's setUp().
        TestBase.setUp(self)
        self.line_malloc = line_number('main.c', '// malloc line')
        self.line_thread1 = line_number('main.c', '// thread1 line')
        self.line_thread2 = line_number('main.c', '// thread2 line')

    def tsan_tests(self):
        exe = self.getBuildArtifact("a.out")
        self.expect(
            "file " + exe,
            patterns=["Current executable set to .*a.out"])

        self.runCmd("run")

        stop_reason = self.dbg.GetSelectedTarget().process.GetSelectedThread().GetStopReason()
        if stop_reason == lldb.eStopReasonExec:
            # On OS X 10.10 and older, we need to re-exec to enable
            # interceptors.
            self.runCmd("continue")

        # the stop reason of the thread should be breakpoint.
        self.expect("thread list", "A data race should be detected",
                    substrs=['stopped', 'stop reason = Data race detected'])

        self.assertEqual(
            self.dbg.GetSelectedTarget().process.GetSelectedThread().GetStopReason(),
            lldb.eStopReasonInstrumentation)

        # test that the TSan dylib is present
        self.expect(
            "image lookup -n __tsan_get_current_report",
            "__tsan_get_current_report should be present",
            substrs=['1 match found'])

        # We should be stopped in __tsan_on_report
        process = self.dbg.GetSelectedTarget().process
        thread = process.GetSelectedThread()
        frame = thread.GetSelectedFrame()
        self.assertTrue("__tsan_on_report" in frame.GetFunctionName())

        # The stopped thread backtrace should contain either line1 or line2
        # from main.c.
        found = False
        for i in range(0, thread.GetNumFrames()):
            frame = thread.GetFrameAtIndex(i)
            if frame.GetLineEntry().GetFileSpec().GetFilename() == "main.c":
                if frame.GetLineEntry().GetLine() == self.line_thread1:
                    found = True
                if frame.GetLineEntry().GetLine() == self.line_thread2:
                    found = True
        self.assertTrue(found)

        self.expect(
            "thread info -s",
            "The extended stop info should contain the TSan provided fields",
            substrs=[
                "instrumentation_class",
                "description",
                "mops"])

        output_lines = self.res.GetOutput().split('\n')
        json_line = '\n'.join(output_lines[2:])
        data = json.loads(json_line)
        self.assertEqual(data["instrumentation_class"], "ThreadSanitizer")
        self.assertEqual(data["issue_type"], "data-race")
        self.assertEqual(len(data["mops"]), 2)

        backtraces = thread.GetStopReasonExtendedBacktraces(
            lldb.eInstrumentationRuntimeTypeAddressSanitizer)
        self.assertEqual(backtraces.GetSize(), 0)

        backtraces = thread.GetStopReasonExtendedBacktraces(
            lldb.eInstrumentationRuntimeTypeThreadSanitizer)
        self.assertTrue(backtraces.GetSize() >= 2)

        # First backtrace is a memory operation
        thread = backtraces.GetThreadAtIndex(0)
        found = False
        for i in range(0, thread.GetNumFrames()):
            frame = thread.GetFrameAtIndex(i)
            if frame.GetLineEntry().GetFileSpec().GetFilename() == "main.c":
                if frame.GetLineEntry().GetLine() == self.line_thread1:
                    found = True
                if frame.GetLineEntry().GetLine() == self.line_thread2:
                    found = True
        self.assertTrue(found)

        # Second backtrace is a memory operation
        thread = backtraces.GetThreadAtIndex(1)
        found = False
        for i in range(0, thread.GetNumFrames()):
            frame = thread.GetFrameAtIndex(i)
            if frame.GetLineEntry().GetFileSpec().GetFilename() == "main.c":
                if frame.GetLineEntry().GetLine() == self.line_thread1:
                    found = True
                if frame.GetLineEntry().GetLine() == self.line_thread2:
                    found = True
        self.assertTrue(found)

        self.runCmd("continue")

        # the stop reason of the thread should be a SIGABRT.
        self.expect("thread list", "We should be stopped due a SIGABRT",
                    substrs=['stopped', 'stop reason = signal SIGABRT'])

        # test that we're in pthread_kill now (TSan abort the process)
        self.expect("thread list", "We should be stopped in pthread_kill",
                    substrs=['pthread_kill'])