xref: /llvm-project/lldb/test/API/functionalities/asan/TestMemoryHistory.py (revision 988ffd06722e7e056b239efe497345ac97be33db)
1"""
2Test that ASan memory history provider returns correct stack traces
3"""
4
5
6import lldb
7from lldbsuite.test.decorators import *
8from lldbsuite.test.lldbtest import *
9from lldbsuite.test import lldbplatform
10from lldbsuite.test import lldbutil
11from lldbsuite.test_event.build_exception import BuildError
12
13class AsanTestCase(TestBase):
14    @skipIfFreeBSD  # llvm.org/pr21136 runtimes not yet available by default
15    @expectedFailureNetBSD
16    @skipUnlessAddressSanitizer
17    def test(self):
18        self.build(make_targets=["asan"])
19        self.asan_tests()
20
21    @skipIf(oslist=no_match(["macosx"]))
22    def test_libsanitizers_asan(self):
23        try:
24            self.build(make_targets=["libsanitizers"])
25        except BuildError as e:
26            self.skipTest("failed to build with libsanitizers")
27        self.libsanitizer_tests()
28
29    def setUp(self):
30        # Call super's setUp().
31        TestBase.setUp(self)
32        self.line_malloc = line_number("main.c", "// malloc line")
33        self.line_malloc2 = line_number("main.c", "// malloc2 line")
34        self.line_free = line_number("main.c", "// free line")
35        self.line_breakpoint = line_number("main.c", "// break line")
36
37    # Test line numbers: rdar://126237493
38    def libsanitizer_tests(self):
39        target = self.createTestTarget()
40
41        self.runCmd(
42            "env SanitizersAddress=1 MallocSanitizerZone=1 MallocSecureAllocator=0"
43        )
44
45        self.runCmd("run")
46
47        # In libsanitizers, memory history is not supported until a report has been generated
48        self.expect(
49            "thread list",
50            "Process should be stopped due to ASan report",
51            substrs=["stopped", "stop reason = Use of deallocated memory"],
52        )
53
54        # test the 'memory history' command
55        self.expect(
56            "memory history 'pointer'",
57            substrs=[
58                "Memory deallocated by Thread",
59                "a.out`f2",
60                "main.c",
61                "Memory allocated by Thread",
62                "a.out`f1",
63                "main.c",
64            ],
65        )
66
67        # do the same using SB API
68        process = self.dbg.GetSelectedTarget().process
69        val = (
70            process.GetSelectedThread().GetSelectedFrame().EvaluateExpression("pointer")
71        )
72        addr = val.GetValueAsUnsigned()
73        threads = process.GetHistoryThreads(addr)
74        self.assertEqual(threads.GetSize(), 2)
75
76        history_thread = threads.GetThreadAtIndex(0)
77        self.assertTrue(history_thread.num_frames >= 2)
78        self.assertEqual(
79            history_thread.frames[1].GetLineEntry().GetFileSpec().GetFilename(),
80            "main.c",
81        )
82
83        history_thread = threads.GetThreadAtIndex(1)
84        self.assertTrue(history_thread.num_frames >= 2)
85        self.assertEqual(
86            history_thread.frames[1].GetLineEntry().GetFileSpec().GetFilename(),
87            "main.c",
88        )
89
90        # let's free the container (SBThreadCollection) and see if the
91        # SBThreads still live
92        threads = None
93        self.assertTrue(history_thread.num_frames >= 2)
94        self.assertEqual(
95            history_thread.frames[1].GetLineEntry().GetFileSpec().GetFilename(),
96            "main.c",
97        )
98
99    def asan_tests(self):
100        target = self.createTestTarget()
101
102        self.registerSanitizerLibrariesWithTarget(target)
103
104        self.runCmd("breakpoint set -f main.c -l %d" % self.line_breakpoint)
105
106        # "memory history" command should not work without a process
107        self.expect(
108            "memory history 0",
109            error=True,
110            substrs=["Command requires a current process"],
111        )
112
113        self.runCmd("run")
114
115        stop_reason = (
116            self.dbg.GetSelectedTarget().process.GetSelectedThread().GetStopReason()
117        )
118        if stop_reason == lldb.eStopReasonExec:
119            # On OS X 10.10 and older, we need to re-exec to enable
120            # interceptors.
121            self.runCmd("continue")
122
123        # the stop reason of the thread should be breakpoint.
124        self.expect(
125            "thread list",
126            STOPPED_DUE_TO_BREAKPOINT,
127            substrs=["stopped", "stop reason = breakpoint"],
128        )
129
130        # test that the ASan dylib is present
131        self.expect(
132            "image lookup -n __asan_describe_address",
133            "__asan_describe_address should be present",
134            substrs=["1 match found"],
135        )
136
137        # test the 'memory history' command
138        self.expect(
139            "memory history 'pointer'",
140            substrs=[
141                "Memory deallocated by Thread",
142                "a.out`f2",
143                "main.c:%d" % self.line_free,
144                "Memory allocated by Thread",
145                "a.out`f1",
146                "main.c:%d" % self.line_malloc,
147            ],
148        )
149
150        # do the same using SB API
151        process = self.dbg.GetSelectedTarget().process
152        val = (
153            process.GetSelectedThread().GetSelectedFrame().EvaluateExpression("pointer")
154        )
155        addr = val.GetValueAsUnsigned()
156        threads = process.GetHistoryThreads(addr)
157        self.assertEqual(threads.GetSize(), 2)
158
159        history_thread = threads.GetThreadAtIndex(0)
160        self.assertGreaterEqual(history_thread.num_frames, 2)
161        self.assertEqual(
162            history_thread.frames[1].GetLineEntry().GetFileSpec().GetFilename(),
163            "main.c",
164        )
165        self.assertEqual(
166            history_thread.frames[1].GetLineEntry().GetLine(), self.line_free
167        )
168
169        history_thread = threads.GetThreadAtIndex(1)
170        self.assertGreaterEqual(history_thread.num_frames, 2)
171        self.assertEqual(
172            history_thread.frames[1].GetLineEntry().GetFileSpec().GetFilename(),
173            "main.c",
174        )
175        self.assertEqual(
176            history_thread.frames[1].GetLineEntry().GetLine(), self.line_malloc
177        )
178
179        # let's free the container (SBThreadCollection) and see if the
180        # SBThreads still live
181        threads = None
182        self.assertGreaterEqual(history_thread.num_frames, 2)
183        self.assertEqual(
184            history_thread.frames[1].GetLineEntry().GetFileSpec().GetFilename(),
185            "main.c",
186        )
187        self.assertEqual(
188            history_thread.frames[1].GetLineEntry().GetLine(), self.line_malloc
189        )
190
191        # ASan will break when a report occurs and we'll try the API then
192        self.runCmd("continue")
193
194        self.expect(
195            "thread list",
196            "Process should be stopped due to ASan report",
197            substrs=["stopped", "stop reason = Use of deallocated memory"],
198        )
199
200        # make sure the 'memory history' command still works even when we're
201        # generating a report now
202        self.expect(
203            "memory history 'another_pointer'",
204            substrs=[
205                "Memory allocated by Thread",
206                "a.out`f1",
207                "main.c:%d" % self.line_malloc2,
208            ],
209        )
210