1"""
2Test that ASan memory history provider returns correct stack traces
3"""
4
5
6
7import lldb
8from lldbsuite.test.decorators import *
9from lldbsuite.test.lldbtest import *
10from lldbsuite.test import lldbplatform
11from lldbsuite.test import lldbutil
12
13
14class AsanTestCase(TestBase):
15
16    @skipIfFreeBSD  # llvm.org/pr21136 runtimes not yet available by default
17    @expectedFailureNetBSD
18    @skipUnlessAddressSanitizer
19    def test(self):
20        self.build()
21        self.asan_tests()
22
23    def setUp(self):
24        # Call super's setUp().
25        TestBase.setUp(self)
26        self.line_malloc = line_number('main.c', '// malloc line')
27        self.line_malloc2 = line_number('main.c', '// malloc2 line')
28        self.line_free = line_number('main.c', '// free line')
29        self.line_breakpoint = line_number('main.c', '// break line')
30
31    def asan_tests(self):
32        target = self.createTestTarget()
33
34        self.registerSanitizerLibrariesWithTarget(target)
35
36        self.runCmd("breakpoint set -f main.c -l %d" % self.line_breakpoint)
37
38        # "memory history" command should not work without a process
39        self.expect("memory history 0",
40                    error=True,
41                    substrs=["Command requires a current process"])
42
43        self.runCmd("run")
44
45        stop_reason = self.dbg.GetSelectedTarget().process.GetSelectedThread().GetStopReason()
46        if stop_reason == lldb.eStopReasonExec:
47            # On OS X 10.10 and older, we need to re-exec to enable
48            # interceptors.
49            self.runCmd("continue")
50
51        # the stop reason of the thread should be breakpoint.
52        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
53                    substrs=['stopped', 'stop reason = breakpoint'])
54
55        # test that the ASan dylib is present
56        self.expect(
57            "image lookup -n __asan_describe_address",
58            "__asan_describe_address should be present",
59            substrs=['1 match found'])
60
61        # test the 'memory history' command
62        self.expect(
63            "memory history 'pointer'",
64            substrs=[
65                'Memory deallocated by Thread',
66                'a.out`f2',
67                'main.c:%d' % self.line_free,
68                'Memory allocated by Thread',
69                'a.out`f1',
70                'main.c:%d' % self.line_malloc,
71            ])
72
73        # do the same using SB API
74        process = self.dbg.GetSelectedTarget().process
75        val = process.GetSelectedThread().GetSelectedFrame().EvaluateExpression("pointer")
76        addr = val.GetValueAsUnsigned()
77        threads = process.GetHistoryThreads(addr)
78        self.assertEqual(threads.GetSize(), 2)
79
80        history_thread = threads.GetThreadAtIndex(0)
81        self.assertTrue(history_thread.num_frames >= 2)
82        self.assertEqual(history_thread.frames[1].GetLineEntry(
83        ).GetFileSpec().GetFilename(), "main.c")
84        self.assertEqual(
85            history_thread.frames[1].GetLineEntry().GetLine(),
86            self.line_free)
87
88        history_thread = threads.GetThreadAtIndex(1)
89        self.assertTrue(history_thread.num_frames >= 2)
90        self.assertEqual(history_thread.frames[1].GetLineEntry(
91        ).GetFileSpec().GetFilename(), "main.c")
92        self.assertEqual(
93            history_thread.frames[1].GetLineEntry().GetLine(),
94            self.line_malloc)
95
96        # let's free the container (SBThreadCollection) and see if the
97        # SBThreads still live
98        threads = None
99        self.assertTrue(history_thread.num_frames >= 2)
100        self.assertEqual(history_thread.frames[1].GetLineEntry(
101        ).GetFileSpec().GetFilename(), "main.c")
102        self.assertEqual(
103            history_thread.frames[1].GetLineEntry().GetLine(),
104            self.line_malloc)
105
106        # ASan will break when a report occurs and we'll try the API then
107        self.runCmd("continue")
108
109        self.expect(
110            "thread list",
111            "Process should be stopped due to ASan report",
112            substrs=[
113                'stopped',
114                'stop reason = Use of deallocated memory'])
115
116        # make sure the 'memory history' command still works even when we're
117        # generating a report now
118        self.expect(
119            "memory history 'another_pointer'",
120            substrs=[
121                'Memory allocated by Thread',
122                'a.out`f1',
123                'main.c:%d' %
124                self.line_malloc2])
125