xref: /llvm-project/lldb/test/API/functionalities/rerun_and_expr_dylib/TestRerunAndExprDylib.py (revision 2238dcc39358353cac21df75c3c3286ab20b8f53)
1"""
2Test that re-running a process from within the same target
3after rebuilding the a dynamic library flushes the scratch
4TypeSystems tied to that process.
5"""
6
7import lldb
8from lldbsuite.test.lldbtest import *
9from lldbsuite.test import lldbutil
10from lldbsuite.test.decorators import *
11
12
13def isUbuntu18_04():
14    """
15    Check if the host OS is Ubuntu 18.04.
16    Derived from `platform.freedesktop_os_release` in Python 3.10.
17    """
18    for path in ("/etc/os-release", "/usr/lib/os-release"):
19        if os.path.exists(path):
20            with open(path) as f:
21                contents = f.read()
22            if "Ubuntu 18.04" in contents:
23                return True
24
25    return False
26
27
28class TestRerunExprDylib(TestBase):
29    @skipTestIfFn(isUbuntu18_04, bugnumber="rdar://103831050")
30    @skipIfWindows
31    def test(self):
32        """
33        Tests whether re-launching a process without destroying
34        the owning target keeps invalid ASTContexts in the
35        scratch AST's importer.
36
37        We test this by:
38        1. Evaluating an expression to import 'struct Foo' into
39           the scratch AST
40        2. Change the definition of 'struct Foo' and rebuild the dylib
41        3. Re-launch the process
42        4. Evaluate the same expression in (1). We expect to have only
43           the latest definition of 'struct Foo' in the scratch AST.
44        """
45
46        DYLIB_NAME = "foo"
47        FULL_DYLIB_NAME = "libfoo.dylib" if self.platformIsDarwin() else "libfoo.so"
48
49        # Build libfoo.dylib
50        self.build(
51            dictionary={
52                "DYLIB_CXX_SOURCES": "lib.cpp",
53                "DYLIB_ONLY": "YES",
54                "DYLIB_NAME": DYLIB_NAME,
55                "USE_LIBDL": "1",
56                "LD_EXTRAS": "-L.",
57            }
58        )
59
60        # Build a.out
61        self.build(
62            dictionary={
63                "EXE": "a.out",
64                "CXX_SOURCES": "main.cpp",
65                "USE_LIBDL": "1",
66                "CXXFLAGS_EXTRAS": f'-DLIB_NAME=\\"{FULL_DYLIB_NAME}\\"',
67                "LD_EXTRAS": "-L.",
68            }
69        )
70
71        exe = self.getBuildArtifact("a.out")
72        target = self.dbg.CreateTarget(exe)
73        target.BreakpointCreateBySourceRegex("dlclose", lldb.SBFileSpec("main.cpp"))
74        target.BreakpointCreateBySourceRegex("return", lldb.SBFileSpec("main.cpp"))
75        process = target.LaunchSimple(None, None, self.get_process_working_directory())
76
77        self.expect_expr(
78            "*foo",
79            result_type="Foo",
80            result_children=[ValueCheck(name="m_val", value="42")],
81        )
82
83        # Delete the dylib to force make to rebuild it.
84        remove_file(self.getBuildArtifact(FULL_DYLIB_NAME))
85
86        # Re-build libfoo.dylib
87        self.build(
88            dictionary={
89                "DYLIB_CXX_SOURCES": "rebuild.cpp",
90                "DYLIB_ONLY": "YES",
91                "DYLIB_NAME": DYLIB_NAME,
92                "USE_LIBDL": "1",
93                "LD_EXTRAS": "-L.",
94            }
95        )
96
97        # Rerun program within the same target
98        process.Continue()
99        process.Destroy()
100        process = target.LaunchSimple(None, None, self.get_process_working_directory())
101
102        self.expect_expr(
103            "*foo",
104            result_type="Foo",
105            result_children=[
106                ValueCheck(
107                    name="Base", children=[ValueCheck(name="m_base_val", value="42")]
108                ),
109                ValueCheck(name="m_derived_val", value="137"),
110            ],
111        )
112
113        self.filecheck("target module dump ast", __file__)
114
115        # The new definition 'struct Foo' is in the scratch AST
116        # CHECK:      |-CXXRecordDecl {{.*}} struct Foo definition
117        # CHECK:      | |-public 'Base'
118        # CHECK-NEXT: | `-FieldDecl {{.*}} m_derived_val 'int'
119        # CHECK-NEXT: `-CXXRecordDecl {{.*}} struct Base definition
120        # CHECK:        `-FieldDecl {{.*}} m_base_val 'int'
121
122        # ...but the original definition of 'struct Foo' is not in the scratch AST anymore
123        # CHECK-NOT: FieldDecl {{.*}} m_val 'int'
124