199451b44SJordan Rupprecht"""
299451b44SJordan RupprechtTest lldb core component: SourceManager.
399451b44SJordan Rupprecht
499451b44SJordan RupprechtTest cases:
599451b44SJordan Rupprecht
699451b44SJordan Rupprechto test_display_source_python:
799451b44SJordan Rupprecht  Test display of source using the SBSourceManager API.
899451b44SJordan Rupprechto test_modify_source_file_while_debugging:
999451b44SJordan Rupprecht  Test the caching mechanism of the source manager.
1099451b44SJordan Rupprecht"""
1199451b44SJordan Rupprecht
1299451b44SJordan Rupprechtfrom __future__ import print_function
1399451b44SJordan Rupprecht
1499451b44SJordan Rupprechtimport lldb
1599451b44SJordan Rupprechtfrom lldbsuite.test.decorators import *
1699451b44SJordan Rupprechtfrom lldbsuite.test.lldbtest import *
1799451b44SJordan Rupprechtfrom lldbsuite.test import lldbutil
1899451b44SJordan Rupprecht
1999451b44SJordan Rupprecht
2099451b44SJordan Rupprechtdef ansi_underline_surround_regex(inner_regex_text):
2199451b44SJordan Rupprecht    # return re.compile(r"\[4m%s\[0m" % inner_regex_text)
2299451b44SJordan Rupprecht    return "4.+\033\\[4m%s\033\\[0m" % inner_regex_text
2399451b44SJordan Rupprecht
2499451b44SJordan Rupprechtdef ansi_color_surround_regex(inner_regex_text):
2599451b44SJordan Rupprecht    return "\033\\[3[0-7]m%s\033\\[0m" % inner_regex_text
2699451b44SJordan Rupprecht
2799451b44SJordan Rupprechtclass SourceManagerTestCase(TestBase):
2899451b44SJordan Rupprecht
2999451b44SJordan Rupprecht    NO_DEBUG_INFO_TESTCASE = True
3099451b44SJordan Rupprecht
3199451b44SJordan Rupprecht    def setUp(self):
3299451b44SJordan Rupprecht        # Call super's setUp().
3399451b44SJordan Rupprecht        TestBase.setUp(self)
3499451b44SJordan Rupprecht        # Find the line number to break inside main().
3599451b44SJordan Rupprecht        self.file = self.getBuildArtifact("main-copy.c")
3699451b44SJordan Rupprecht        self.line = line_number("main.c", '// Set break point at this line.')
3799451b44SJordan Rupprecht
3899451b44SJordan Rupprecht    def get_expected_stop_column_number(self):
3999451b44SJordan Rupprecht        """Return the 1-based column number of the first non-whitespace
4099451b44SJordan Rupprecht        character in the breakpoint source line."""
4199451b44SJordan Rupprecht        stop_line = get_line(self.file, self.line)
4299451b44SJordan Rupprecht        # The number of spaces that must be skipped to get to the first non-
4399451b44SJordan Rupprecht        # whitespace character --- where we expect the debugger breakpoint
4499451b44SJordan Rupprecht        # column to be --- is equal to the number of characters that get
4599451b44SJordan Rupprecht        # stripped off the front when we lstrip it, plus one to specify
4699451b44SJordan Rupprecht        # the character column after the initial whitespace.
4799451b44SJordan Rupprecht        return len(stop_line) - len(stop_line.lstrip()) + 1
4899451b44SJordan Rupprecht
4999451b44SJordan Rupprecht    def do_display_source_python_api(self, use_color, needle_regex, highlight_source=False):
5099451b44SJordan Rupprecht        self.build()
5199451b44SJordan Rupprecht        exe = self.getBuildArtifact("a.out")
5299451b44SJordan Rupprecht        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
5399451b44SJordan Rupprecht
5499451b44SJordan Rupprecht        target = self.dbg.CreateTarget(exe)
5599451b44SJordan Rupprecht        self.assertTrue(target, VALID_TARGET)
5699451b44SJordan Rupprecht
5799451b44SJordan Rupprecht        # Launch the process, and do not stop at the entry point.
5899451b44SJordan Rupprecht        args = None
5999451b44SJordan Rupprecht        envp = None
6099451b44SJordan Rupprecht        process = target.LaunchSimple(
6199451b44SJordan Rupprecht            args, envp, self.get_process_working_directory())
6299451b44SJordan Rupprecht        self.assertIsNotNone(process)
6399451b44SJordan Rupprecht
6499451b44SJordan Rupprecht        #
6599451b44SJordan Rupprecht        # Exercise Python APIs to display source lines.
6699451b44SJordan Rupprecht        #
6799451b44SJordan Rupprecht
6899451b44SJordan Rupprecht        # Setup whether we should use ansi escape sequences, including color
6999451b44SJordan Rupprecht        # and styles such as underline.
7099451b44SJordan Rupprecht        self.dbg.SetUseColor(use_color)
7199451b44SJordan Rupprecht        # Disable syntax highlighting if needed.
7299451b44SJordan Rupprecht
7399451b44SJordan Rupprecht        self.runCmd("settings set highlight-source " + str(highlight_source).lower())
7499451b44SJordan Rupprecht
7599451b44SJordan Rupprecht        filespec = lldb.SBFileSpec(self.file, False)
7699451b44SJordan Rupprecht        source_mgr = self.dbg.GetSourceManager()
7799451b44SJordan Rupprecht        # Use a string stream as the destination.
7899451b44SJordan Rupprecht        stream = lldb.SBStream()
7999451b44SJordan Rupprecht        column = self.get_expected_stop_column_number()
8099451b44SJordan Rupprecht        context_before = 2
8199451b44SJordan Rupprecht        context_after = 2
8299451b44SJordan Rupprecht        current_line_prefix = "=>"
8399451b44SJordan Rupprecht        source_mgr.DisplaySourceLinesWithLineNumbersAndColumn(
8499451b44SJordan Rupprecht            filespec, self.line, column, context_before, context_after,
8599451b44SJordan Rupprecht            current_line_prefix, stream)
8699451b44SJordan Rupprecht
8799451b44SJordan Rupprecht        #    2
8899451b44SJordan Rupprecht        #    3    int main(int argc, char const *argv[]) {
8999451b44SJordan Rupprecht        # => 4        printf("Hello world.\n"); // Set break point at this line.
9099451b44SJordan Rupprecht        #    5        return 0;
9199451b44SJordan Rupprecht        #    6    }
9299451b44SJordan Rupprecht        self.expect(stream.GetData(), "Source code displayed correctly:\n" + stream.GetData(),
9399451b44SJordan Rupprecht                    exe=False,
94841be985SJonas Devlieghere                    patterns=['=>', '%d.*Hello world' % self.line,
9599451b44SJordan Rupprecht                              needle_regex])
9699451b44SJordan Rupprecht
9799451b44SJordan Rupprecht        # Boundary condition testings for SBStream().  LLDB should not crash!
9899451b44SJordan Rupprecht        stream.Print(None)
9999451b44SJordan Rupprecht        stream.RedirectToFile(None, True)
10099451b44SJordan Rupprecht
10199451b44SJordan Rupprecht    @add_test_categories(['pyapi'])
10299451b44SJordan Rupprecht    def test_display_source_python_dumb_terminal(self):
10399451b44SJordan Rupprecht        """Test display of source using the SBSourceManager API, using a
10499451b44SJordan Rupprecht        dumb terminal and thus no color support (the default)."""
10599451b44SJordan Rupprecht        use_color = False
10699451b44SJordan Rupprecht        self.do_display_source_python_api(use_color, r"\s+\^")
10799451b44SJordan Rupprecht
10899451b44SJordan Rupprecht    @add_test_categories(['pyapi'])
10999451b44SJordan Rupprecht    def test_display_source_python_ansi_terminal(self):
11099451b44SJordan Rupprecht        """Test display of source using the SBSourceManager API, using a
11199451b44SJordan Rupprecht        dumb terminal and thus no color support (the default)."""
11299451b44SJordan Rupprecht        use_color = True
11399451b44SJordan Rupprecht        underline_regex = ansi_underline_surround_regex(r"printf")
11499451b44SJordan Rupprecht        self.do_display_source_python_api(use_color, underline_regex)
11599451b44SJordan Rupprecht
11699451b44SJordan Rupprecht    @add_test_categories(['pyapi'])
11799451b44SJordan Rupprecht    def test_display_source_python_ansi_terminal_syntax_highlighting(self):
11899451b44SJordan Rupprecht        """Test display of source using the SBSourceManager API and check for
11999451b44SJordan Rupprecht        the syntax highlighted output"""
12099451b44SJordan Rupprecht        use_color = True
12199451b44SJordan Rupprecht        syntax_highlighting = True;
12299451b44SJordan Rupprecht
12399451b44SJordan Rupprecht        # Just pick 'int' as something that should be colored.
12499451b44SJordan Rupprecht        color_regex = ansi_color_surround_regex("int")
12599451b44SJordan Rupprecht        self.do_display_source_python_api(use_color, color_regex, syntax_highlighting)
12699451b44SJordan Rupprecht
12799451b44SJordan Rupprecht        # Same for 'char'.
12899451b44SJordan Rupprecht        color_regex = ansi_color_surround_regex("char")
12999451b44SJordan Rupprecht        self.do_display_source_python_api(use_color, color_regex, syntax_highlighting)
13099451b44SJordan Rupprecht
13199451b44SJordan Rupprecht        # Test that we didn't color unrelated identifiers.
13299451b44SJordan Rupprecht        self.do_display_source_python_api(use_color, r" main\(", syntax_highlighting)
13399451b44SJordan Rupprecht        self.do_display_source_python_api(use_color, r"\);", syntax_highlighting)
13499451b44SJordan Rupprecht
13599451b44SJordan Rupprecht    def test_move_and_then_display_source(self):
13699451b44SJordan Rupprecht        """Test that target.source-map settings work by moving main.c to hidden/main.c."""
13799451b44SJordan Rupprecht        self.build()
13899451b44SJordan Rupprecht        exe = self.getBuildArtifact("a.out")
13999451b44SJordan Rupprecht        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
14099451b44SJordan Rupprecht
14199451b44SJordan Rupprecht        # Move main.c to hidden/main.c.
14299451b44SJordan Rupprecht        hidden = self.getBuildArtifact("hidden")
14399451b44SJordan Rupprecht        lldbutil.mkdir_p(hidden)
14499451b44SJordan Rupprecht        main_c_hidden = os.path.join(hidden, "main-copy.c")
14599451b44SJordan Rupprecht        os.rename(self.file, main_c_hidden)
14699451b44SJordan Rupprecht
14799451b44SJordan Rupprecht        # Set source remapping with invalid replace path and verify we get an
14899451b44SJordan Rupprecht        # error
14999451b44SJordan Rupprecht        self.expect(
15099451b44SJordan Rupprecht            "settings set target.source-map /a/b/c/d/e /q/r/s/t/u",
15199451b44SJordan Rupprecht            error=True,
15299451b44SJordan Rupprecht            substrs=['''error: the replacement path doesn't exist: "/q/r/s/t/u"'''])
15399451b44SJordan Rupprecht
15499451b44SJordan Rupprecht        # 'make -C' has resolved current directory to its realpath form.
15599451b44SJordan Rupprecht        builddir_real = os.path.realpath(self.getBuildDir())
15699451b44SJordan Rupprecht        hidden_real = os.path.realpath(hidden)
15799451b44SJordan Rupprecht        # Set target.source-map settings.
15899451b44SJordan Rupprecht        self.runCmd("settings set target.source-map %s %s" %
15999451b44SJordan Rupprecht                    (builddir_real, hidden_real))
16099451b44SJordan Rupprecht        # And verify that the settings work.
16199451b44SJordan Rupprecht        self.expect("settings show target.source-map",
16299451b44SJordan Rupprecht                    substrs=[builddir_real, hidden_real])
16399451b44SJordan Rupprecht
16499451b44SJordan Rupprecht        # Display main() and verify that the source mapping has been kicked in.
16599451b44SJordan Rupprecht        self.expect("source list -n main", SOURCE_DISPLAYED_CORRECTLY,
16699451b44SJordan Rupprecht                    substrs=['Hello world'])
16799451b44SJordan Rupprecht
16899451b44SJordan Rupprecht    @skipIf(oslist=["windows"], bugnumber="llvm.org/pr44431")
16999451b44SJordan Rupprecht    def test_modify_source_file_while_debugging(self):
17099451b44SJordan Rupprecht        """Modify a source file while debugging the executable."""
17199451b44SJordan Rupprecht        self.build()
17299451b44SJordan Rupprecht        exe = self.getBuildArtifact("a.out")
17399451b44SJordan Rupprecht        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
17499451b44SJordan Rupprecht
17599451b44SJordan Rupprecht        lldbutil.run_break_set_by_file_and_line(
17699451b44SJordan Rupprecht            self, "main-copy.c", self.line, num_expected_locations=1, loc_exact=True)
17799451b44SJordan Rupprecht
17899451b44SJordan Rupprecht        self.runCmd("run", RUN_SUCCEEDED)
17999451b44SJordan Rupprecht
18099451b44SJordan Rupprecht        # The stop reason of the thread should be breakpoint.
18199451b44SJordan Rupprecht        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
18299451b44SJordan Rupprecht                    substrs=['stopped',
18399451b44SJordan Rupprecht                             'main-copy.c:%d' % self.line,
18499451b44SJordan Rupprecht                             'stop reason = breakpoint'])
18599451b44SJordan Rupprecht
18699451b44SJordan Rupprecht        # Display some source code.
18799451b44SJordan Rupprecht        self.expect(
18899451b44SJordan Rupprecht            "source list -f main-copy.c -l %d" %
18999451b44SJordan Rupprecht            self.line,
19099451b44SJordan Rupprecht            SOURCE_DISPLAYED_CORRECTLY,
19199451b44SJordan Rupprecht            substrs=['Hello world'])
19299451b44SJordan Rupprecht
193bc0a9a17SJim Ingham        # Do the same thing with a file & line spec:
194bc0a9a17SJim Ingham        self.expect(
195bc0a9a17SJim Ingham            "source list -y main-copy.c:%d" %
196bc0a9a17SJim Ingham            self.line,
197bc0a9a17SJim Ingham            SOURCE_DISPLAYED_CORRECTLY,
198bc0a9a17SJim Ingham            substrs=['Hello world'])
199bc0a9a17SJim Ingham
20099451b44SJordan Rupprecht        # The '-b' option shows the line table locations from the debug information
20199451b44SJordan Rupprecht        # that indicates valid places to set source level breakpoints.
20299451b44SJordan Rupprecht
20399451b44SJordan Rupprecht        # The file to display is implicit in this case.
20499451b44SJordan Rupprecht        self.runCmd("source list -l %d -c 3 -b" % self.line)
20599451b44SJordan Rupprecht        output = self.res.GetOutput().splitlines()[0]
20699451b44SJordan Rupprecht
20799451b44SJordan Rupprecht        # If the breakpoint set command succeeded, we should expect a positive number
20899451b44SJordan Rupprecht        # of breakpoints for the current line, i.e., self.line.
20999451b44SJordan Rupprecht        import re
21099451b44SJordan Rupprecht        m = re.search('^\[(\d+)\].*// Set break point at this line.', output)
21199451b44SJordan Rupprecht        if not m:
21299451b44SJordan Rupprecht            self.fail("Fail to display source level breakpoints")
21399451b44SJordan Rupprecht        self.assertTrue(int(m.group(1)) > 0)
21499451b44SJordan Rupprecht
21599451b44SJordan Rupprecht        # Read the main.c file content.
21699451b44SJordan Rupprecht        with io.open(self.file, 'r', newline='\n') as f:
21799451b44SJordan Rupprecht            original_content = f.read()
21899451b44SJordan Rupprecht            if self.TraceOn():
21999451b44SJordan Rupprecht                print("original content:", original_content)
22099451b44SJordan Rupprecht
22199451b44SJordan Rupprecht        # Modify the in-memory copy of the original source code.
22299451b44SJordan Rupprecht        new_content = original_content.replace('Hello world', 'Hello lldb', 1)
22399451b44SJordan Rupprecht
22499451b44SJordan Rupprecht        # Modify the source code file.
22599451b44SJordan Rupprecht        with io.open(self.file, 'w', newline='\n') as f:
22699451b44SJordan Rupprecht            time.sleep(1)
22799451b44SJordan Rupprecht            f.write(new_content)
22899451b44SJordan Rupprecht            if self.TraceOn():
22999451b44SJordan Rupprecht                print("new content:", new_content)
23099451b44SJordan Rupprecht                print(
23199451b44SJordan Rupprecht                    "os.path.getmtime() after writing new content:",
23299451b44SJordan Rupprecht                    os.path.getmtime(self.file))
23399451b44SJordan Rupprecht
23499451b44SJordan Rupprecht        # Display the source code again.  We should see the updated line.
23599451b44SJordan Rupprecht        self.expect(
23699451b44SJordan Rupprecht            "source list -f main-copy.c -l %d" %
23799451b44SJordan Rupprecht            self.line,
23899451b44SJordan Rupprecht            SOURCE_DISPLAYED_CORRECTLY,
23999451b44SJordan Rupprecht            substrs=['Hello lldb'])
24099451b44SJordan Rupprecht
24199451b44SJordan Rupprecht    def test_set_breakpoint_with_absolute_path(self):
24299451b44SJordan Rupprecht        self.build()
24399451b44SJordan Rupprecht        hidden = self.getBuildArtifact("hidden")
24499451b44SJordan Rupprecht        lldbutil.mkdir_p(hidden)
24599451b44SJordan Rupprecht        # 'make -C' has resolved current directory to its realpath form.
24699451b44SJordan Rupprecht        builddir_real = os.path.realpath(self.getBuildDir())
24799451b44SJordan Rupprecht        hidden_real = os.path.realpath(hidden)
24899451b44SJordan Rupprecht        self.runCmd("settings set target.source-map %s %s" %
24999451b44SJordan Rupprecht                    (builddir_real, hidden_real))
25099451b44SJordan Rupprecht
25199451b44SJordan Rupprecht        exe = self.getBuildArtifact("a.out")
25299451b44SJordan Rupprecht        main = os.path.join(builddir_real, "hidden", "main-copy.c")
25399451b44SJordan Rupprecht        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
25499451b44SJordan Rupprecht
25599451b44SJordan Rupprecht        lldbutil.run_break_set_by_file_and_line(
25699451b44SJordan Rupprecht            self, main, self.line, num_expected_locations=1, loc_exact=False)
25799451b44SJordan Rupprecht
25899451b44SJordan Rupprecht        self.runCmd("run", RUN_SUCCEEDED)
25999451b44SJordan Rupprecht
26099451b44SJordan Rupprecht        # The stop reason of the thread should be breakpoint.
26199451b44SJordan Rupprecht        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
26299451b44SJordan Rupprecht                    substrs=['stopped',
26399451b44SJordan Rupprecht                             'main-copy.c:%d' % self.line,
26499451b44SJordan Rupprecht                             'stop reason = breakpoint'])
265edf410e4SMed Ismail Bennani
266edf410e4SMed Ismail Bennani    def test_artificial_source_location(self):
267edf410e4SMed Ismail Bennani        src_file = 'artificial_location.c'
268edf410e4SMed Ismail Bennani        d = {'C_SOURCES': src_file }
269edf410e4SMed Ismail Bennani        self.build(dictionary=d)
270edf410e4SMed Ismail Bennani
271edf410e4SMed Ismail Bennani        lldbutil.run_to_source_breakpoint(
272edf410e4SMed Ismail Bennani            self, 'main',
273edf410e4SMed Ismail Bennani            lldb.SBFileSpec(src_file, False))
274edf410e4SMed Ismail Bennani
275edf410e4SMed Ismail Bennani        self.expect("run", RUN_SUCCEEDED,
276edf410e4SMed Ismail Bennani                    substrs=['stop reason = breakpoint', '%s:%d' % (src_file,0),
277*20db8e07SMed Ismail Bennani                             'Note: this address is compiler-generated code in '
278*20db8e07SMed Ismail Bennani                             'function', 'that has no source code associated '
279*20db8e07SMed Ismail Bennani                             'with it.'])
280edf410e4SMed Ismail Bennani
281