241 lines · python
1"""2Test that ASan memory history provider returns correct stack traces3"""4 5import lldb6from lldbsuite.test.decorators import *7from lldbsuite.test.lldbtest import *8from lldbsuite.test import lldbplatform9from lldbsuite.test import lldbutil10from lldbsuite.test_event.build_exception import BuildError11 12 13class MemoryHistoryTestCase(TestBase):14 @skipIfFreeBSD # llvm.org/pr21136 runtimes not yet available by default15 @expectedFailureNetBSD16 @skipUnlessAddressSanitizer17 def test_compiler_rt_asan(self):18 self.build(make_targets=["compiler_rt-asan"])19 self.compiler_rt_asan_tests()20 21 @skipUnlessDarwin22 @skipIf(bugnumber="rdar://109913184&143590169")23 def test_libsanitizers_asan(self):24 try:25 self.build(make_targets=["libsanitizers-asan"])26 except BuildError as e:27 self.skipTest("failed to build with libsanitizers")28 self.libsanitizers_asan_tests()29 30 @skipUnlessDarwin31 @skipIf(macos_version=["<", "15.5"])32 def test_libsanitizers_traces(self):33 self.build(make_targets=["libsanitizers-traces"])34 self.libsanitizers_traces_tests()35 36 def setUp(self):37 # Call super's setUp().38 TestBase.setUp(self)39 self.line_malloc = line_number("main.c", "// malloc line")40 self.line_malloc2 = line_number("main.c", "// malloc2 line")41 self.line_free = line_number("main.c", "// free line")42 self.line_breakpoint = line_number("main.c", "// break line")43 44 def check_traces(self):45 self.expect(46 "memory history 'pointer'",47 substrs=[48 "Memory deallocated by Thread",49 "a.out`f2",50 f"main.c:{self.line_free}",51 "Memory allocated by Thread",52 "a.out`f1",53 f"main.c:{self.line_malloc}",54 ],55 )56 57 # Set breakpoint: after free, but before bug58 def set_breakpoint(self, target):59 bkpt = target.BreakpointCreateByLocation("main.c", self.line_breakpoint)60 self.assertGreater(bkpt.GetNumLocations(), 0, "Set the breakpoint successfully")61 62 def run_to_breakpoint(self, target):63 self.set_breakpoint(target)64 self.runCmd("run")65 self.expect(66 "thread list",67 STOPPED_DUE_TO_BREAKPOINT,68 substrs=["stopped", "stop reason = breakpoint"],69 )70 71 def libsanitizers_traces_tests(self):72 target = self.createTestTarget()73 74 self.runCmd("env SanitizersAllocationTraces=all")75 76 self.run_to_breakpoint(target)77 self.check_traces()78 79 def libsanitizers_asan_tests(self):80 target = self.createTestTarget()81 82 self.runCmd("env SanitizersAddress=1 MallocSanitizerZone=1")83 84 self.run_to_breakpoint(target)85 self.check_traces()86 87 self.runCmd("continue")88 89 # Stop on report90 self.expect(91 "thread list",92 "Process should be stopped due to ASan report",93 substrs=["stopped", "stop reason = Use of deallocated memory"],94 )95 self.check_traces()96 97 if self.platformIsDarwin():98 # Make sure we're not stopped in the sanitizer library but instead at the99 # point of failure in the user-code.100 self.assertEqual(self.frame().GetFunctionName(), "main")101 102 # do the same using SB API103 process = self.dbg.GetSelectedTarget().process104 val = (105 process.GetSelectedThread().GetSelectedFrame().EvaluateExpression("pointer")106 )107 addr = val.GetValueAsUnsigned()108 threads = process.GetHistoryThreads(addr)109 self.assertEqual(threads.GetSize(), 2)110 111 history_thread = threads.GetThreadAtIndex(0)112 self.assertTrue(history_thread.num_frames >= 2)113 self.assertEqual(114 history_thread.frames[1].GetLineEntry().GetFileSpec().GetFilename(),115 "main.c",116 )117 118 history_thread = threads.GetThreadAtIndex(1)119 self.assertTrue(history_thread.num_frames >= 2)120 self.assertEqual(121 history_thread.frames[1].GetLineEntry().GetFileSpec().GetFilename(),122 "main.c",123 )124 125 # let's free the container (SBThreadCollection) and see if the126 # SBThreads still live127 threads = None128 self.assertTrue(history_thread.num_frames >= 2)129 self.assertEqual(130 history_thread.frames[1].GetLineEntry().GetFileSpec().GetFilename(),131 "main.c",132 )133 134 def compiler_rt_asan_tests(self):135 target = self.createTestTarget()136 137 self.registerSanitizerLibrariesWithTarget(target)138 139 self.set_breakpoint(target)140 141 # "memory history" command should not work without a process142 self.expect(143 "memory history 0",144 error=True,145 substrs=["Command requires a current process"],146 )147 148 self.runCmd("run")149 150 stop_reason = (151 self.dbg.GetSelectedTarget().process.GetSelectedThread().GetStopReason()152 )153 if stop_reason == lldb.eStopReasonExec:154 # On OS X 10.10 and older, we need to re-exec to enable155 # interceptors.156 self.runCmd("continue")157 158 # the stop reason of the thread should be breakpoint.159 self.expect(160 "thread list",161 STOPPED_DUE_TO_BREAKPOINT,162 substrs=["stopped", "stop reason = breakpoint"],163 )164 165 # test that the ASan dylib is present166 self.expect(167 "image lookup -n __asan_describe_address",168 "__asan_describe_address should be present",169 substrs=["1 match found"],170 )171 172 self.check_traces()173 174 # do the same using SB API175 process = self.dbg.GetSelectedTarget().process176 val = (177 process.GetSelectedThread().GetSelectedFrame().EvaluateExpression("pointer")178 )179 addr = val.GetValueAsUnsigned()180 threads = process.GetHistoryThreads(addr)181 self.assertEqual(threads.GetSize(), 2)182 183 history_thread = threads.GetThreadAtIndex(0)184 self.assertGreaterEqual(history_thread.num_frames, 2)185 self.assertEqual(186 history_thread.frames[1].GetLineEntry().GetFileSpec().GetFilename(),187 "main.c",188 )189 self.assertEqual(190 history_thread.frames[1].GetLineEntry().GetLine(), self.line_free191 )192 193 history_thread = threads.GetThreadAtIndex(1)194 self.assertGreaterEqual(history_thread.num_frames, 2)195 self.assertEqual(196 history_thread.frames[1].GetLineEntry().GetFileSpec().GetFilename(),197 "main.c",198 )199 self.assertEqual(200 history_thread.frames[1].GetLineEntry().GetLine(), self.line_malloc201 )202 203 # let's free the container (SBThreadCollection) and see if the204 # SBThreads still live205 threads = None206 self.assertGreaterEqual(history_thread.num_frames, 2)207 self.assertEqual(208 history_thread.frames[1].GetLineEntry().GetFileSpec().GetFilename(),209 "main.c",210 )211 self.assertEqual(212 history_thread.frames[1].GetLineEntry().GetLine(), self.line_malloc213 )214 215 # ASan will break when a report occurs and we'll try the API then216 self.runCmd("continue")217 218 self.expect(219 "thread list",220 "Process should be stopped due to ASan report",221 substrs=["stopped", "stop reason = Use of deallocated memory"],222 )223 224 self.check_traces()225 226 if self.platformIsDarwin():227 # Make sure we're not stopped in the sanitizer library but instead at the228 # point of failure in the user-code.229 self.assertEqual(self.frame().GetFunctionName(), "main")230 231 # make sure the 'memory history' command still works even when we're232 # generating a report now233 self.expect(234 "memory history 'another_pointer'",235 substrs=[236 "Memory allocated by Thread",237 "a.out`f1",238 "main.c:%d" % self.line_malloc2,239 ],240 )241