125 lines · python
1"""2Test that re-running a process from within the same target3after rebuilding the a dynamic library flushes the scratch4TypeSystems tied to that process.5"""6 7import lldb8from lldbsuite.test.lldbtest import *9from lldbsuite.test import lldbutil10from 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 "Ubuntu 18.04 is not supported."24 25 return None26 27 28class TestRerunExprDylib(TestBase):29 @skipTestIfFn(isUbuntu18_04, bugnumber="rdar://103831050")30 @skipIfWindows31 @skipIfRemote32 def test(self):33 """34 Tests whether re-launching a process without destroying35 the owning target keeps invalid ASTContexts in the36 scratch AST's importer.37 38 We test this by:39 1. Evaluating an expression to import 'struct Foo' into40 the scratch AST41 2. Change the definition of 'struct Foo' and rebuild the dylib42 3. Re-launch the process43 4. Evaluate the same expression in (1). We expect to have only44 the latest definition of 'struct Foo' in the scratch AST.45 """46 47 DYLIB_NAME = "foo"48 FULL_DYLIB_NAME = "libfoo.dylib" if self.platformIsDarwin() else "libfoo.so"49 50 # Build libfoo.dylib51 self.build(52 dictionary={53 "DYLIB_CXX_SOURCES": "lib.cpp",54 "DYLIB_ONLY": "YES",55 "DYLIB_NAME": DYLIB_NAME,56 "USE_LIBDL": "1",57 "LD_EXTRAS": "-L.",58 }59 )60 61 # Build a.out62 self.build(63 dictionary={64 "EXE": "a.out",65 "CXX_SOURCES": "main.cpp",66 "USE_LIBDL": "1",67 "CXXFLAGS_EXTRAS": f'-DLIB_NAME=\\"{FULL_DYLIB_NAME}\\"',68 "LD_EXTRAS": "-L.",69 }70 )71 72 exe = self.getBuildArtifact("a.out")73 target = self.dbg.CreateTarget(exe)74 target.BreakpointCreateBySourceRegex("dlclose", lldb.SBFileSpec("main.cpp"))75 target.BreakpointCreateBySourceRegex("return", lldb.SBFileSpec("main.cpp"))76 process = target.LaunchSimple(None, None, self.get_process_working_directory())77 78 self.expect_expr(79 "*foo",80 result_type="Foo",81 result_children=[ValueCheck(name="m_val", value="42")],82 )83 84 # Delete the dylib to force make to rebuild it.85 remove_file(self.getBuildArtifact(FULL_DYLIB_NAME))86 87 # Re-build libfoo.dylib88 self.build(89 dictionary={90 "DYLIB_CXX_SOURCES": "rebuild.cpp",91 "DYLIB_ONLY": "YES",92 "DYLIB_NAME": DYLIB_NAME,93 "USE_LIBDL": "1",94 "LD_EXTRAS": "-L.",95 }96 )97 98 # Rerun program within the same target99 process.Continue()100 process.Destroy()101 process = target.LaunchSimple(None, None, self.get_process_working_directory())102 103 self.expect_expr(104 "*foo",105 result_type="Foo",106 result_children=[107 ValueCheck(108 name="Base", children=[ValueCheck(name="m_base_val", value="42")]109 ),110 ValueCheck(name="m_derived_val", value="137"),111 ],112 )113 114 self.filecheck("target module dump ast", __file__)115 116 # The new definition 'struct Foo' is in the scratch AST117 # CHECK: |-CXXRecordDecl {{.*}} struct Foo definition118 # CHECK: | |-public 'Base'119 # CHECK-NEXT: | `-FieldDecl {{.*}} m_derived_val 'int'120 # CHECK-NEXT: `-CXXRecordDecl {{.*}} struct Base definition121 # CHECK: `-FieldDecl {{.*}} m_base_val 'int'122 123 # ...but the original definition of 'struct Foo' is not in the scratch AST anymore124 # CHECK-NOT: FieldDecl {{.*}} m_val 'int'125