1"""Test that lldb can create a skinny corefile, and load all available libraries correctly.""" 2 3 4import os 5import re 6import subprocess 7 8import lldb 9from lldbsuite.test.decorators import * 10from lldbsuite.test.lldbtest import * 11from lldbsuite.test import lldbutil 12 13 14class TestSkinnyCorefile(TestBase): 15 @skipIfOutOfTreeDebugserver # newer debugserver required for these qMemoryRegionInfo types 16 @skipIf( 17 debug_info=no_match(["dsym"]), 18 bugnumber="This test is looking explicitly for a dSYM", 19 ) 20 @skipUnlessDarwin 21 @skipIfRemote 22 def test_lc_note(self): 23 self.build() 24 self.aout_exe = self.getBuildArtifact("a.out") 25 self.aout_dsym = self.getBuildArtifact("a.out.dSYM") 26 self.to_be_removed_dylib = self.getBuildArtifact("libto-be-removed.dylib") 27 self.to_be_removed_dsym = self.getBuildArtifact("libto-be-removed.dylib.dSYM") 28 self.corefile = self.getBuildArtifact("process.core") 29 self.dsym_for_uuid = self.getBuildArtifact("dsym-for-uuid.sh") 30 31 # After the corefile is created, we'll move a.out and a.out.dSYM 32 # into hide.noindex and lldb will have to use the 33 # LLDB_APPLE_DSYMFORUUID_EXECUTABLE script to find them. 34 self.hide_dir = self.getBuildArtifact("hide.noindex") 35 lldbutil.mkdir_p(self.hide_dir) 36 self.hide_aout_exe = self.getBuildArtifact("hide.noindex/a.out") 37 self.hide_aout_dsym = self.getBuildArtifact("hide.noindex/a.out.dSYM") 38 39 # We can hook in our dsym-for-uuid shell script to lldb with 40 # this env var instead of requiring a defaults write. 41 os.environ["LLDB_APPLE_DSYMFORUUID_EXECUTABLE"] = self.dsym_for_uuid 42 self.addTearDownHook( 43 lambda: os.environ.pop("LLDB_APPLE_DSYMFORUUID_EXECUTABLE", None) 44 ) 45 46 dwarfdump_uuid_regex = re.compile("UUID: ([-0-9a-fA-F]+) \(([^\(]+)\) .*") 47 dwarfdump_cmd_output = subprocess.check_output( 48 ('/usr/bin/dwarfdump --uuid "%s"' % self.aout_exe), shell=True 49 ).decode("utf-8") 50 aout_uuid = None 51 for line in dwarfdump_cmd_output.splitlines(): 52 match = dwarfdump_uuid_regex.search(line) 53 if match: 54 aout_uuid = match.group(1) 55 self.assertNotEqual(aout_uuid, None, "Could not get uuid of built a.out") 56 57 ### Create our dsym-for-uuid shell script which returns self.hide_aout_exe. 58 shell_cmds = [ 59 "#! /bin/sh", 60 "# the last argument is the uuid", 61 "while [ $# -gt 1 ]", 62 "do", 63 " shift", 64 "done", 65 "ret=0", 66 'echo "<?xml version=\\"1.0\\" encoding=\\"UTF-8\\"?>"', 67 'echo "<!DOCTYPE plist PUBLIC \\"-//Apple//DTD PLIST 1.0//EN\\" \\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\\">"', 68 'echo "<plist version=\\"1.0\\">"', 69 "", 70 'if [ "$1" = "%s" ]' % aout_uuid, 71 "then", 72 " uuid=%s" % aout_uuid, 73 " bin=%s" % self.hide_aout_exe, 74 " dsym=%s.dSYM/Contents/Resources/DWARF/%s" 75 % (self.hide_aout_exe, os.path.basename(self.hide_aout_exe)), 76 "fi", 77 'if [ -z "$uuid" -o -z "$bin" -o ! -f "$bin" ]', 78 "then", 79 ' echo "<key>DBGError</key><string>not found</string>"', 80 ' echo "</plist>"', 81 " exit 1", 82 "fi", 83 'echo "<dict><key>$uuid</key><dict>"', 84 "", 85 'echo "<key>DBGArchitecture</key><string>x86_64</string>"', 86 'echo "<key>DBGDSYMPath</key><string>$dsym</string>"', 87 'echo "<key>DBGSymbolRichExecutable</key><string>$bin</string>"', 88 'echo "</dict></dict></plist>"', 89 "exit $ret", 90 ] 91 92 with open(self.dsym_for_uuid, "w") as writer: 93 for l in shell_cmds: 94 writer.write(l + "\n") 95 96 os.chmod(self.dsym_for_uuid, 0o755) 97 98 # Launch a live process with a.out, libto-be-removed.dylib, 99 # libpresent.dylib all in their original locations, create 100 # a corefile at the breakpoint. 101 (target, process, t, bp) = lldbutil.run_to_source_breakpoint( 102 self, "break here", lldb.SBFileSpec("present.c") 103 ) 104 105 self.assertTrue(process.IsValid()) 106 107 if self.TraceOn(): 108 self.runCmd("bt") 109 self.runCmd("image list") 110 111 self.runCmd("process save-core " + self.corefile) 112 process.Kill() 113 target.Clear() 114 115 # Move the main binary and its dSYM into the hide.noindex 116 # directory. Now the only way lldb can find them is with 117 # the LLDB_APPLE_DSYMFORUUID_EXECUTABLE shell script - 118 # so we're testing that this dSYM discovery method works. 119 os.rename(self.aout_exe, self.hide_aout_exe) 120 os.rename(self.aout_dsym, self.hide_aout_dsym) 121 122 # Completely remove the libto-be-removed.dylib, so we're 123 # testing that lldb handles an unavailable binary correctly, 124 # and non-dirty memory from this binary (e.g. the executing 125 # instructions) are NOT included in the corefile. 126 os.unlink(self.to_be_removed_dylib) 127 shutil.rmtree(self.to_be_removed_dsym) 128 129 # Now load the corefile 130 self.target = self.dbg.CreateTarget("") 131 self.process = self.target.LoadCore(self.corefile) 132 self.assertTrue(self.process.IsValid()) 133 if self.TraceOn(): 134 self.runCmd("image list") 135 self.runCmd("bt") 136 137 self.assertTrue(self.process.IsValid()) 138 self.assertTrue(self.process.GetSelectedThread().IsValid()) 139 140 # f0 is present() in libpresent.dylib 141 f0 = self.process.GetSelectedThread().GetFrameAtIndex(0) 142 to_be_removed_dirty_data = f0.FindVariable("to_be_removed_dirty_data") 143 self.assertEqual(to_be_removed_dirty_data.GetValueAsUnsigned(), 20) 144 145 present_heap_buf = f0.FindVariable("present_heap_buf") 146 self.assertIn("have ints 5 20 20 5", present_heap_buf.GetSummary()) 147 148 # f1 is to_be_removed() in libto-be-removed.dylib 149 # it has been removed since the corefile was created, 150 # and the instructions for this frame should NOT be included 151 # in the corefile. They were not dirty pages. 152 f1 = self.process.GetSelectedThread().GetFrameAtIndex(1) 153 err = lldb.SBError() 154 uint = self.process.ReadUnsignedFromMemory(f1.GetPC(), 4, err) 155 self.assertTrue(err.Fail()) 156 157 # TODO Future testing could check that read-only constant data 158 # (main_const_data, present_const_data) can be read both as an 159 # SBValue and in an expression -- which means lldb needs to read 160 # them out of the binaries, they are not present in the corefile. 161 # And checking file-scope dirty data (main_dirty_data, 162 # present_dirty_data) the same way would be good, instead of just 163 # checking the heap and stack like are being done right now. 164