xref: /llvm-project/lldb/test/API/functionalities/archives/TestBSDArchives.py (revision ec009994a06338995dfb6431c943b299f9327fd2)
199451b44SJordan Rupprecht"""Test breaking inside functions defined within a BSD archive file libfoo.a."""
299451b44SJordan Rupprecht
399451b44SJordan Rupprecht
499451b44SJordan Rupprechtimport lldb
599451b44SJordan Rupprechtfrom lldbsuite.test.decorators import *
699451b44SJordan Rupprechtfrom lldbsuite.test.lldbtest import *
799451b44SJordan Rupprechtfrom lldbsuite.test import lldbutil
84763200eSGreg Claytonimport os
94763200eSGreg Claytonimport time
1099451b44SJordan Rupprecht
1199451b44SJordan Rupprecht
122238dcc3SJonas Devlieghereclass BSDArchivesTestCase(TestBase):
134763200eSGreg Clayton    # If your test case doesn't stress debug info, then
144763200eSGreg Clayton    # set this to true.  That way it won't be run once for
154763200eSGreg Clayton    # each debug info format.
164763200eSGreg Clayton    NO_DEBUG_INFO_TESTCASE = True
174763200eSGreg Clayton
1899451b44SJordan Rupprecht    def setUp(self):
1999451b44SJordan Rupprecht        # Call super's setUp().
2099451b44SJordan Rupprecht        TestBase.setUp(self)
2199451b44SJordan Rupprecht        # Find the line number in a(int) to break at.
222238dcc3SJonas Devlieghere        self.line = line_number("a.c", "// Set file and line breakpoint inside a().")
2399451b44SJordan Rupprecht
2499451b44SJordan Rupprecht    def test(self):
2599451b44SJordan Rupprecht        """Break inside a() and b() defined within libfoo.a."""
2699451b44SJordan Rupprecht        self.build()
2799451b44SJordan Rupprecht
2899451b44SJordan Rupprecht        exe = self.getBuildArtifact("a.out")
2999451b44SJordan Rupprecht        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
3099451b44SJordan Rupprecht
3199451b44SJordan Rupprecht        # Break inside a() by file and line first.
3299451b44SJordan Rupprecht        lldbutil.run_break_set_by_file_and_line(
332238dcc3SJonas Devlieghere            self, "a.c", self.line, num_expected_locations=1, loc_exact=True
342238dcc3SJonas Devlieghere        )
3599451b44SJordan Rupprecht
3699451b44SJordan Rupprecht        self.runCmd("run", RUN_SUCCEEDED)
3799451b44SJordan Rupprecht
3899451b44SJordan Rupprecht        # The stop reason of the thread should be breakpoint.
392238dcc3SJonas Devlieghere        self.expect(
402238dcc3SJonas Devlieghere            "thread list",
412238dcc3SJonas Devlieghere            STOPPED_DUE_TO_BREAKPOINT,
422238dcc3SJonas Devlieghere            substrs=["stopped", "stop reason = breakpoint"],
432238dcc3SJonas Devlieghere        )
4499451b44SJordan Rupprecht
4599451b44SJordan Rupprecht        # Break at a(int) first.
462238dcc3SJonas Devlieghere        self.expect(
472238dcc3SJonas Devlieghere            "frame variable", VARIABLES_DISPLAYED_CORRECTLY, substrs=["(int) arg = 1"]
482238dcc3SJonas Devlieghere        )
49d85cc03cSRaphael Isemann        self.expect_var_path("__a_global", type="int", value="1")
5099451b44SJordan Rupprecht
5199451b44SJordan Rupprecht        # Set breakpoint for b() next.
5299451b44SJordan Rupprecht        lldbutil.run_break_set_by_symbol(
532238dcc3SJonas Devlieghere            self, "b", num_expected_locations=1, sym_exact=True
542238dcc3SJonas Devlieghere        )
5599451b44SJordan Rupprecht
5699451b44SJordan Rupprecht        # Continue the program, we should break at b(int) next.
5799451b44SJordan Rupprecht        self.runCmd("continue")
582238dcc3SJonas Devlieghere        self.expect(
592238dcc3SJonas Devlieghere            "thread list",
602238dcc3SJonas Devlieghere            STOPPED_DUE_TO_BREAKPOINT,
612238dcc3SJonas Devlieghere            substrs=["stopped", "stop reason = breakpoint"],
622238dcc3SJonas Devlieghere        )
632238dcc3SJonas Devlieghere        self.expect(
642238dcc3SJonas Devlieghere            "frame variable", VARIABLES_DISPLAYED_CORRECTLY, substrs=["(int) arg = 2"]
652238dcc3SJonas Devlieghere        )
66d85cc03cSRaphael Isemann        self.expect_var_path("__b_global", type="int", value="2")
67dc46ae6dSKaining Zhong
684763200eSGreg Clayton    def check_frame_variable_errors(self, thread, error_strings):
694763200eSGreg Clayton        command_result = lldb.SBCommandReturnObject()
704763200eSGreg Clayton        interp = self.dbg.GetCommandInterpreter()
714763200eSGreg Clayton        result = interp.HandleCommand("frame variable", command_result)
722238dcc3SJonas Devlieghere        self.assertEqual(
732238dcc3SJonas Devlieghere            result, lldb.eReturnStatusFailed, "frame var succeeded unexpectedly"
742238dcc3SJonas Devlieghere        )
754763200eSGreg Clayton        command_error = command_result.GetError()
764763200eSGreg Clayton
774763200eSGreg Clayton        frame = thread.GetFrameAtIndex(0)
784763200eSGreg Clayton        var_list = frame.GetVariables(True, True, False, True)
794763200eSGreg Clayton        self.assertEqual(var_list.GetSize(), 0)
804763200eSGreg Clayton        api_error = var_list.GetError().GetCString()
814763200eSGreg Clayton
824763200eSGreg Clayton        for s in error_strings:
83*9c246882SJordan Rupprecht            self.assertIn(
84*9c246882SJordan Rupprecht                s,
85*9c246882SJordan Rupprecht                command_error,
862238dcc3SJonas Devlieghere                'Make sure "%s" exists in the command error "%s"' % (s, command_error),
872238dcc3SJonas Devlieghere            )
884763200eSGreg Clayton        for s in error_strings:
89*9c246882SJordan Rupprecht            self.assertIn(
90*9c246882SJordan Rupprecht                s,
91*9c246882SJordan Rupprecht                api_error,
922238dcc3SJonas Devlieghere                'Make sure "%s" exists in the API error "%s"' % (s, api_error),
932238dcc3SJonas Devlieghere            )
944763200eSGreg Clayton
954763200eSGreg Clayton    @skipIfRemote
964763200eSGreg Clayton    @skipUnlessDarwin
974763200eSGreg Clayton    def test_frame_var_errors_when_archive_missing(self):
984763200eSGreg Clayton        """
994763200eSGreg Clayton        Break inside a() and remove libfoo.a to make sure we can't load
1004763200eSGreg Clayton        the debug information and report an appropriate error when doing
1014763200eSGreg Clayton        'frame variable'.
1024763200eSGreg Clayton        """
1034763200eSGreg Clayton        self.build()
1044763200eSGreg Clayton        exe = self.getBuildArtifact("a.out")
1054763200eSGreg Clayton        libfoo_path = self.getBuildArtifact("libfoo.a")
1064763200eSGreg Clayton        # Delete the main.o file that contains the debug info so we force an
1074763200eSGreg Clayton        # error when we run to main and try to get variables for the a()
1084763200eSGreg Clayton        # function. Since the libfoo.a is missing, the debug info won't be
1094763200eSGreg Clayton        # loaded and we should see an error when trying to read varibles.
1104763200eSGreg Clayton        os.unlink(libfoo_path)
1114763200eSGreg Clayton
1124763200eSGreg Clayton        (target, process, thread, bkpt) = lldbutil.run_to_name_breakpoint(
1132238dcc3SJonas Devlieghere            self, "a", bkpt_module=exe
1142238dcc3SJonas Devlieghere        )
1154763200eSGreg Clayton
1164763200eSGreg Clayton        error_strings = [
1174763200eSGreg Clayton            'debug map object file "',
1182238dcc3SJonas Devlieghere            'libfoo.a(a.o)" containing debug info does not exist, debug info will not be loaded',
1194763200eSGreg Clayton        ]
1204763200eSGreg Clayton        self.check_frame_variable_errors(thread, error_strings)
1214763200eSGreg Clayton
1224763200eSGreg Clayton    @skipIfRemote
12315c8a762SAdrian Prantl    @skipIf(compiler="clang", compiler_version=["<", "12.0"])
124d4a141efSGreg Clayton    def test_archive_specifications(self):
125d4a141efSGreg Clayton        """
126d4a141efSGreg Clayton        Create archives and make sure the information we get when retrieving
127d4a141efSGreg Clayton        the modules specifications is correct.
128d4a141efSGreg Clayton        """
129d4a141efSGreg Clayton        self.build()
130d4a141efSGreg Clayton        libbar_path = self.getBuildArtifact("libbar.a")
131d4a141efSGreg Clayton        libfoo_path = self.getBuildArtifact("libfoo.a")
132d4a141efSGreg Clayton        libfoothin_path = self.getBuildArtifact("libfoo-thin.a")
133d4a141efSGreg Clayton        objfile_a = self.getBuildArtifact("a.o")
134d4a141efSGreg Clayton        objfile_b = self.getBuildArtifact("b.o")
135d4a141efSGreg Clayton        objfile_c = self.getBuildArtifact("c.o")
136d4a141efSGreg Clayton        size_a = os.path.getsize(objfile_a)
137d4a141efSGreg Clayton        size_b = os.path.getsize(objfile_b)
138d4a141efSGreg Clayton        size_c = os.path.getsize(objfile_c)
139d4a141efSGreg Clayton
140d4a141efSGreg Clayton        # Test loading normal archives
141d4a141efSGreg Clayton        module_specs = lldb.SBModuleSpecList.GetModuleSpecifications(libfoo_path)
142d4a141efSGreg Clayton        num_specs = module_specs.GetSize()
143d4a141efSGreg Clayton        self.assertEqual(num_specs, 2)
144d4a141efSGreg Clayton        spec = module_specs.GetSpecAtIndex(0)
145d4a141efSGreg Clayton        self.assertEqual(spec.GetObjectName(), "a.o")
146d4a141efSGreg Clayton        self.assertEqual(spec.GetObjectSize(), size_a)
147d4a141efSGreg Clayton        spec = module_specs.GetSpecAtIndex(1)
148d4a141efSGreg Clayton        self.assertEqual(spec.GetObjectName(), "b.o")
149d4a141efSGreg Clayton        self.assertEqual(spec.GetObjectSize(), size_b)
150d4a141efSGreg Clayton
151d4a141efSGreg Clayton        # Test loading thin archives
152d4a141efSGreg Clayton        module_specs = lldb.SBModuleSpecList.GetModuleSpecifications(libbar_path)
153d4a141efSGreg Clayton        num_specs = module_specs.GetSize()
154d4a141efSGreg Clayton        self.assertEqual(num_specs, 1)
155d4a141efSGreg Clayton        spec = module_specs.GetSpecAtIndex(0)
156d4a141efSGreg Clayton        self.assertEqual(spec.GetObjectName(), "c.o")
157d4a141efSGreg Clayton        self.assertEqual(spec.GetObjectSize(), size_c)
158d4a141efSGreg Clayton
159d4a141efSGreg Clayton        module_specs = lldb.SBModuleSpecList.GetModuleSpecifications(libfoothin_path)
160d4a141efSGreg Clayton        num_specs = module_specs.GetSize()
161d4a141efSGreg Clayton        self.assertEqual(num_specs, 2)
162d4a141efSGreg Clayton        spec = module_specs.GetSpecAtIndex(0)
163d4a141efSGreg Clayton        self.assertEqual(spec.GetObjectName(), "a.o")
164d4a141efSGreg Clayton        self.assertEqual(spec.GetObjectSize(), size_a)
165d4a141efSGreg Clayton        spec = module_specs.GetSpecAtIndex(1)
166d4a141efSGreg Clayton        self.assertEqual(spec.GetObjectName(), "b.o")
167d4a141efSGreg Clayton        self.assertEqual(spec.GetObjectSize(), size_b, libfoothin_path)
168d4a141efSGreg Clayton
169d4a141efSGreg Clayton    @skipIfRemote
1704763200eSGreg Clayton    @skipUnlessDarwin
1714b9eed9cSWanyi Ye    def test_frame_var_errors_when_thin_archive_malformed(self):
1724b9eed9cSWanyi Ye        """
1734b9eed9cSWanyi Ye        Create thin archive libfoo.a and make it malformed to make sure
1744b9eed9cSWanyi Ye        we don't crash and report an appropriate error when resolving
1754b9eed9cSWanyi Ye        breakpoint using debug map.
1764b9eed9cSWanyi Ye        """
1774b9eed9cSWanyi Ye        self.build()
1784b9eed9cSWanyi Ye        exe = self.getBuildArtifact("a.out")
1794b9eed9cSWanyi Ye        libfoo_path = self.getBuildArtifact("libfoo.a")
1804b9eed9cSWanyi Ye        libthin_path = self.getBuildArtifact("libfoo-thin.a")
1814b9eed9cSWanyi Ye        objfile_a = self.getBuildArtifact("a.o")
182d4a141efSGreg Clayton        objfile_b = self.getBuildArtifact("b.o")
183d4a141efSGreg Clayton        objfile_c = self.getBuildArtifact("c.o")
1844b9eed9cSWanyi Ye        # Replace the libfoo.a file with a thin archive containing the same
1854b9eed9cSWanyi Ye        # debug information (a.o, b.o). Then remove a.o from the file system
1864b9eed9cSWanyi Ye        # so we force an error when we set a breakpoint on a() function.
1874b9eed9cSWanyi Ye        # Since the a.o is missing, the debug info won't be loaded and we
1884b9eed9cSWanyi Ye        # should see an error when trying to break into a().
1894b9eed9cSWanyi Ye        os.remove(libfoo_path)
1904b9eed9cSWanyi Ye        shutil.copyfile(libthin_path, libfoo_path)
1914b9eed9cSWanyi Ye        os.remove(objfile_a)
1924b9eed9cSWanyi Ye
1934b9eed9cSWanyi Ye        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
1944b9eed9cSWanyi Ye        # We won't be able to see source file
1954b9eed9cSWanyi Ye        self.expect(
1964b9eed9cSWanyi Ye            "b a",
1974b9eed9cSWanyi Ye            substrs=["Breakpoint 1: where = a.out`a, address ="],
1984b9eed9cSWanyi Ye        )
1994b9eed9cSWanyi Ye        # Break at a() should fail
2004b9eed9cSWanyi Ye        (target, process, thread, bkpt) = lldbutil.run_to_name_breakpoint(
2014b9eed9cSWanyi Ye            self, "a", bkpt_module=exe
2024b9eed9cSWanyi Ye        )
2034b9eed9cSWanyi Ye        error_strings = [
2044b9eed9cSWanyi Ye            '"a.o" object from the "',
2054b9eed9cSWanyi Ye            "libfoo.a\" archive: either the .o file doesn't exist in the archive or the modification time (0x",
2064b9eed9cSWanyi Ye            ") of the .o file doesn't match",
2074b9eed9cSWanyi Ye        ]
2084b9eed9cSWanyi Ye        self.check_frame_variable_errors(thread, error_strings)
209ca762819SWanyi Ye
210ca762819SWanyi Ye        # Break at b() should succeed
211ca762819SWanyi Ye        (target, process, thread, bkpt) = lldbutil.run_to_name_breakpoint(
212ca762819SWanyi Ye            self, "b", bkpt_module=exe
213ca762819SWanyi Ye        )
214ca762819SWanyi Ye        self.expect(
215ca762819SWanyi Ye            "thread list",
216ca762819SWanyi Ye            STOPPED_DUE_TO_BREAKPOINT,
217ca762819SWanyi Ye            substrs=["stopped", "stop reason = breakpoint"],
218ca762819SWanyi Ye        )
219ca762819SWanyi Ye        self.expect(
220ca762819SWanyi Ye            "frame variable", VARIABLES_DISPLAYED_CORRECTLY, substrs=["(int) arg = 2"]
221ca762819SWanyi Ye        )
2224b9eed9cSWanyi Ye
2234b9eed9cSWanyi Ye    @skipIfRemote
2244b9eed9cSWanyi Ye    @skipUnlessDarwin
2254763200eSGreg Clayton    def test_frame_var_errors_when_mtime_mistmatch_for_object_in_archive(self):
2264763200eSGreg Clayton        """
2274763200eSGreg Clayton        Break inside a() and modify the modification time for "a.o" within
2284763200eSGreg Clayton        libfoo.a to make sure we can't load the debug information and
2294763200eSGreg Clayton        report an appropriate error when doing 'frame variable'.
2304763200eSGreg Clayton        """
2314763200eSGreg Clayton        self.build()
2324763200eSGreg Clayton        exe = self.getBuildArtifact("a.out")
2334763200eSGreg Clayton        a_path = self.getBuildArtifact("a.o")
2344763200eSGreg Clayton
2354763200eSGreg Clayton        # Change the modification time of the a.o object file after sleeping for
2364763200eSGreg Clayton        # 2 seconds to ensure the modification time is different. The rebuild
2374763200eSGreg Clayton        # only the "libfoo.a" target. This means the modification time of the
2384763200eSGreg Clayton        # a.o within libfoo.a will not match the debug map's modification time
2394763200eSGreg Clayton        # in a.out and will cause the debug information to not be loaded and we
2404763200eSGreg Clayton        # should get an appropriate error when reading variables.
2414763200eSGreg Clayton        time.sleep(2)
2424763200eSGreg Clayton        os.utime(a_path, None)
2434763200eSGreg Clayton        self.build(make_targets=["libfoo.a"])
2444763200eSGreg Clayton
2454763200eSGreg Clayton        (target, process, thread, bkpt) = lldbutil.run_to_name_breakpoint(
2462238dcc3SJonas Devlieghere            self, "a", bkpt_module=exe
2472238dcc3SJonas Devlieghere        )
2484763200eSGreg Clayton
2494763200eSGreg Clayton        error_strings = [
2504763200eSGreg Clayton            '"a.o" object from the "',
2512238dcc3SJonas Devlieghere            "libfoo.a\" archive: either the .o file doesn't exist in the archive or the modification time (0x",
2522238dcc3SJonas Devlieghere            ") of the .o file doesn't match",
2534763200eSGreg Clayton        ]
2544763200eSGreg Clayton        self.check_frame_variable_errors(thread, error_strings)
255