brintos

brintos / llvm-project-archived public Read only

0
0
Text · 14.5 KiB · 3500dde Raw
404 lines · python
1"""2Test lldb core component: SourceManager.3 4Test cases:5 6o test_display_source_python:7  Test display of source using the SBSourceManager API.8o test_modify_source_file_while_debugging:9  Test the caching mechanism of the source manager.10"""11 12import os13import io14import stat15 16import lldb17from lldbsuite.test.decorators import *18from lldbsuite.test.lldbtest import *19from lldbsuite.test import lldbutil20 21 22def ansi_underline_surround_regex(inner_regex_text):23    # return re.compile(r"\[4m%s\[0m" % inner_regex_text)24    return "4.+\033\\[4m%s\033\\[0m" % inner_regex_text25 26 27def ansi_color_surround_regex(inner_regex_text):28    return "\033\\[3[0-7]m%s\033\\[0m" % inner_regex_text29 30 31class SourceManagerTestCase(TestBase):32    NO_DEBUG_INFO_TESTCASE = True33 34    def setUp(self):35        # Call super's setUp().36        TestBase.setUp(self)37        # Find the line number to break inside main().38        self.file = os.path.realpath(self.getBuildArtifact("main-copy.c"))39        self.line = line_number("main.c", "// Set break point at this line.")40 41    def modify_content(self):42        # Read the main.c file content.43        with io.open(self.file, "r", newline="\n") as f:44            original_content = f.read()45            if self.TraceOn():46                print("original content:", original_content)47 48        # Modify the in-memory copy of the original source code.49        new_content = original_content.replace("Hello world", "Hello lldb", 1)50 51        # Modify the source code file.52        # If the source was read only, the copy will also be read only.53        # Run "chmod u+w" on it first so we can modify it.54        statinfo = os.stat(self.file)55        os.chmod(self.file, statinfo.st_mode | stat.S_IWUSR)56 57        with io.open(self.file, "w", newline="\n") as f:58            time.sleep(1)59            f.write(new_content)60            if self.TraceOn():61                print("new content:", new_content)62                print(63                    "os.path.getmtime() after writing new content:",64                    os.path.getmtime(self.file),65                )66 67    def get_expected_stop_column_number(self):68        """Return the 1-based column number of the first non-whitespace69        character in the breakpoint source line."""70        stop_line = get_line(self.file, self.line)71        # The number of spaces that must be skipped to get to the first non-72        # whitespace character --- where we expect the debugger breakpoint73        # column to be --- is equal to the number of characters that get74        # stripped off the front when we lstrip it, plus one to specify75        # the character column after the initial whitespace.76        return len(stop_line) - len(stop_line.lstrip()) + 177 78    def do_display_source_python_api(79        self, use_color, needle_regex, highlight_source=False80    ):81        self.build()82        exe = self.getBuildArtifact("a.out")83        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)84 85        target = self.dbg.CreateTarget(exe)86        self.assertTrue(target, VALID_TARGET)87 88        # Launch the process, and do not stop at the entry point.89        args = None90        envp = None91        process = target.LaunchSimple(args, envp, self.get_process_working_directory())92        self.assertIsNotNone(process)93 94        #95        # Exercise Python APIs to display source lines.96        #97 98        # Setup whether we should use ansi escape sequences, including color99        # and styles such as underline.100        self.dbg.SetUseColor(use_color)101        # Disable syntax highlighting if needed.102 103        self.runCmd("settings set highlight-source " + str(highlight_source).lower())104 105        filespec = lldb.SBFileSpec(self.file, False)106        source_mgr = self.dbg.GetSourceManager()107        # Use a string stream as the destination.108        stream = lldb.SBStream()109        column = self.get_expected_stop_column_number()110        context_before = 2111        context_after = 2112        current_line_prefix = "=>"113        source_mgr.DisplaySourceLinesWithLineNumbersAndColumn(114            filespec,115            self.line,116            column,117            context_before,118            context_after,119            current_line_prefix,120            stream,121        )122 123        #    2124        #    3    int main(int argc, char const *argv[]) {125        # => 4        printf("Hello world.\n"); // Set break point at this line.126        #    5        return 0;127        #    6    }128        self.expect(129            stream.GetData(),130            "Source code displayed correctly:\n" + stream.GetData(),131            exe=False,132            ordered=False,133            patterns=["=>", "%d.*Hello world" % self.line, needle_regex],134        )135 136        # Boundary condition testings for SBStream().  LLDB should not crash!137        stream.Print(None)138        stream.RedirectToFile(None, True)139 140    @add_test_categories(["pyapi"])141    def test_display_source_python_dumb_terminal(self):142        """Test display of source using the SBSourceManager API, using a143        dumb terminal and thus no color support (the default)."""144        use_color = False145        self.do_display_source_python_api(use_color, r"\s+\^")146 147    @add_test_categories(["pyapi"])148    def test_display_source_python_ansi_terminal(self):149        """Test display of source using the SBSourceManager API, using a150        dumb terminal and thus no color support (the default)."""151        use_color = True152        underline_regex = ansi_underline_surround_regex(r"printf")153        self.do_display_source_python_api(use_color, underline_regex)154 155    @add_test_categories(["pyapi"])156    def test_display_source_python_ansi_terminal_syntax_highlighting(self):157        """Test display of source using the SBSourceManager API and check for158        the syntax highlighted output"""159        use_color = True160        syntax_highlighting = True161 162        # Just pick 'int' as something that should be colored.163        color_regex = ansi_color_surround_regex("int")164        self.do_display_source_python_api(use_color, color_regex, syntax_highlighting)165 166        # Same for 'char'.167        color_regex = ansi_color_surround_regex("char")168        self.do_display_source_python_api(use_color, color_regex, syntax_highlighting)169 170        # Test that we didn't color unrelated identifiers.171        self.do_display_source_python_api(use_color, r" main\(", syntax_highlighting)172        self.do_display_source_python_api(use_color, r"\);", syntax_highlighting)173 174    def test_move_and_then_display_source(self):175        """Test that target.source-map settings work by moving main.c to hidden/main.c."""176        self.build()177        exe = self.getBuildArtifact("a.out")178        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)179 180        # Move main.c to hidden/main.c.181        hidden = self.getBuildArtifact("hidden")182        lldbutil.mkdir_p(hidden)183        main_c_hidden = os.path.join(hidden, "main-copy.c")184        os.rename(self.file, main_c_hidden)185 186        # Set source remapping with invalid replace path and verify we get an187        # error188        self.expect(189            "settings set target.source-map /a/b/c/d/e /q/r/s/t/u",190            error=True,191            substrs=['''error: the replacement path doesn't exist: "/q/r/s/t/u"'''],192        )193 194        # 'make -C' has resolved current directory to its realpath form.195        builddir_real = os.path.realpath(self.getBuildDir())196        hidden_real = os.path.realpath(hidden)197        # Set target.source-map settings.198        self.runCmd(199            "settings set target.source-map %s %s" % (builddir_real, hidden_real)200        )201        # And verify that the settings work.202        self.expect(203            "settings show target.source-map", substrs=[builddir_real, hidden_real]204        )205 206        # Display main() and verify that the source mapping has been kicked in.207        self.expect(208            "source list -n main", SOURCE_DISPLAYED_CORRECTLY, substrs=["Hello world"]209        )210 211    @skipIf(oslist=["windows"], bugnumber="llvm.org/pr44431")212    def test_modify_source_file_while_debugging(self):213        """Modify a source file while debugging the executable."""214        self.build()215        exe = self.getBuildArtifact("a.out")216        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)217 218        lldbutil.run_break_set_by_file_and_line(219            self, "main-copy.c", self.line, num_expected_locations=1, loc_exact=True220        )221 222        self.runCmd("run", RUN_SUCCEEDED)223 224        # The stop reason of the thread should be breakpoint.225        self.expect(226            "thread list",227            STOPPED_DUE_TO_BREAKPOINT,228            substrs=[229                "stopped",230                "main-copy.c:%d" % self.line,231                "stop reason = breakpoint",232            ],233        )234 235        # Display some source code.236        self.expect(237            "source list -f main-copy.c -l %d" % self.line,238            SOURCE_DISPLAYED_CORRECTLY,239            substrs=["Hello world"],240        )241 242        # Do the same thing with a file & line spec:243        self.expect(244            "source list -y main-copy.c:%d" % self.line,245            SOURCE_DISPLAYED_CORRECTLY,246            substrs=["Hello world"],247        )248 249        # The '-b' option shows the line table locations from the debug information250        # that indicates valid places to set source level breakpoints.251 252        # The file to display is implicit in this case.253        self.runCmd("source list -l %d -c 3 -b" % self.line)254        output = self.res.GetOutput().splitlines()[0]255 256        # If the breakpoint set command succeeded, we should expect a positive number257        # of breakpoints for the current line, i.e., self.line.258        import re259 260        m = re.search(r"^\[(\d+)\].*// Set break point at this line.", output)261        if not m:262            self.fail("Fail to display source level breakpoints")263        self.assertGreater(int(m.group(1)), 0)264 265        # Modify content266        self.modify_content()267 268        # Display the source code again. We should not see the updated line.269        self.expect(270            "source list -f main-copy.c -l %d" % self.line,271            SOURCE_DISPLAYED_CORRECTLY,272            substrs=["Hello world"],273        )274 275        # clear the source cache.276        self.runCmd("source cache clear")277 278        # Display the source code again. Now we should see the updated line.279        self.expect(280            "source list -f main-copy.c -l %d" % self.line,281            SOURCE_DISPLAYED_CORRECTLY,282            substrs=["Hello lldb"],283        )284 285    def test_set_breakpoint_with_absolute_path(self):286        self.build()287        hidden = self.getBuildArtifact("hidden")288        lldbutil.mkdir_p(hidden)289        # 'make -C' has resolved current directory to its realpath form.290        builddir_real = os.path.realpath(self.getBuildDir())291        hidden_real = os.path.realpath(hidden)292        self.runCmd(293            "settings set target.source-map %s %s" % (builddir_real, hidden_real)294        )295 296        exe = self.getBuildArtifact("a.out")297        main = os.path.join(builddir_real, "hidden", "main-copy.c")298        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)299 300        lldbutil.run_break_set_by_file_and_line(301            self, main, self.line, num_expected_locations=1, loc_exact=False302        )303 304        self.runCmd("run", RUN_SUCCEEDED)305 306        # The stop reason of the thread should be breakpoint.307        self.expect(308            "thread list",309            STOPPED_DUE_TO_BREAKPOINT,310            substrs=[311                "stopped",312                "main-copy.c:%d" % self.line,313                "stop reason = breakpoint",314            ],315        )316 317    def test_artificial_source_location(self):318        src_file = "artificial_location.cpp"319        d = {"C_SOURCES": "", "CXX_SOURCES": src_file}320        self.build(dictionary=d)321 322        target = lldbutil.run_to_breakpoint_make_target(self)323 324        # Find the instruction with line=0 and put a breakpoint there.325        sc_list = target.FindFunctions("A::foo")326        self.assertEqual(len(sc_list), 1)327        insns = sc_list[0].function.GetInstructions(target)328        insn0 = next(filter(lambda insn: insn.addr.line_entry.line == 0, insns))329        bkpt = target.BreakpointCreateBySBAddress(insn0.addr)330        self.assertGreater(bkpt.GetNumLocations(), 0)331 332        lldbutil.run_to_breakpoint_do_run(self, target, bkpt)333 334        self.expect(335            "process status",336            substrs=[337                "stop reason = breakpoint",338                f"{src_file}:0",339                "static int foo();",340                "note: This address is not associated with a specific line "341                "of code. This may be due to compiler optimizations.",342            ],343        )344 345    def test_source_cache_dump_and_clear(self):346        self.build()347        exe = self.getBuildArtifact("a.out")348        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)349        lldbutil.run_break_set_by_file_and_line(350            self, self.file, self.line, num_expected_locations=1, loc_exact=True351        )352        self.runCmd("run", RUN_SUCCEEDED)353 354        # Make sure the main source file is in the source cache.355        self.expect(356            "source cache dump",357            substrs=["Modification time", "Lines", "Path", " 7", self.file],358        )359 360        # Clear the cache.361        self.expect("source cache clear")362 363        # Make sure the main source file is no longer in the source cache.364        self.expect("source cache dump", matching=False, substrs=[self.file])365 366    def test_source_cache_interactions(self):367        self.build()368        exe = self.getBuildArtifact("a.out")369 370        # Create a first target.371        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)372        lldbutil.run_break_set_by_symbol(self, "main", num_expected_locations=1)373        self.expect("run", RUN_SUCCEEDED, substrs=["Hello world"])374 375        # Create a second target.376        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)377        lldbutil.run_break_set_by_symbol(self, "main", num_expected_locations=1)378        self.expect("run", RUN_SUCCEEDED, substrs=["Hello world"])379 380        # Modify the source file content.381        self.modify_content()382 383        # Clear the source cache. This will wipe the debugger and the process384        # cache for the second process.385        self.runCmd("source cache clear")386 387        # Make sure we're seeing the new content from the clean process cache.388        self.expect(389            "next",390            SOURCE_DISPLAYED_CORRECTLY,391            substrs=["Hello lldb"],392        )393 394        # Switch back to the first target.395        self.runCmd("target select 0")396 397        # Make sure we're seeing the old content from the first target's398        # process cache.399        self.expect(400            "next",401            SOURCE_DISPLAYED_CORRECTLY,402            substrs=["Hello world"],403        )404