1""" 2Make sure the frame variable -g, -a, and -l flags work. 3""" 4 5 6import lldb 7import lldbsuite.test.lldbutil as lldbutil 8from lldbsuite.test.decorators import * 9from lldbsuite.test.lldbtest import * 10import os 11import shutil 12import time 13 14 15class TestFrameVar(TestBase): 16 # If your test case doesn't stress debug info, then 17 # set this to true. That way it won't be run once for 18 # each debug info format. 19 NO_DEBUG_INFO_TESTCASE = True 20 21 def test_frame_var(self): 22 self.build() 23 self.do_test() 24 25 def do_test(self): 26 target = self.createTestTarget() 27 28 # Now create a breakpoint in main.c at the source matching 29 # "Set a breakpoint here" 30 breakpoint = target.BreakpointCreateBySourceRegex( 31 "Set a breakpoint here", lldb.SBFileSpec("main.c") 32 ) 33 self.assertTrue( 34 breakpoint and breakpoint.GetNumLocations() >= 1, VALID_BREAKPOINT 35 ) 36 37 error = lldb.SBError() 38 # This is the launch info. If you want to launch with arguments or 39 # environment variables, add them using SetArguments or 40 # SetEnvironmentEntries 41 42 launch_info = target.GetLaunchInfo() 43 process = target.Launch(launch_info, error) 44 self.assertTrue(process, PROCESS_IS_VALID) 45 46 # Did we hit our breakpoint? 47 from lldbsuite.test.lldbutil import get_threads_stopped_at_breakpoint 48 49 threads = get_threads_stopped_at_breakpoint(process, breakpoint) 50 self.assertEqual( 51 len(threads), 1, "There should be a thread stopped at our breakpoint" 52 ) 53 54 # The hit count for the breakpoint should be 1. 55 self.assertEqual(breakpoint.GetHitCount(), 1) 56 57 frame = threads[0].GetFrameAtIndex(0) 58 command_result = lldb.SBCommandReturnObject() 59 interp = self.dbg.GetCommandInterpreter() 60 61 # Ensure --regex can find globals if it is the very first frame var command. 62 self.expect("frame var --regex g_", substrs=["g_var"]) 63 64 # Ensure the requested scope is respected: 65 self.expect( 66 "frame var --regex argc --no-args", 67 error=True, 68 substrs=["no variables matched the regular expression 'argc'"], 69 ) 70 71 # Just get args: 72 result = interp.HandleCommand("frame var -l", command_result) 73 self.assertEqual( 74 result, lldb.eReturnStatusSuccessFinishResult, "frame var -a didn't succeed" 75 ) 76 output = command_result.GetOutput() 77 self.assertIn("argc", output, "Args didn't find argc") 78 self.assertIn("argv", output, "Args didn't find argv") 79 self.assertNotIn("test_var", output, "Args found a local") 80 self.assertNotIn("g_var", output, "Args found a global") 81 82 # Just get locals: 83 result = interp.HandleCommand("frame var -a", command_result) 84 self.assertEqual( 85 result, lldb.eReturnStatusSuccessFinishResult, "frame var -a didn't succeed" 86 ) 87 output = command_result.GetOutput() 88 self.assertNotIn("argc", output, "Locals found argc") 89 self.assertNotIn("argv", output, "Locals found argv") 90 self.assertIn("test_var", output, "Locals didn't find test_var") 91 self.assertNotIn("g_var", output, "Locals found a global") 92 93 # Get the file statics: 94 result = interp.HandleCommand("frame var -l -a -g", command_result) 95 self.assertEqual( 96 result, lldb.eReturnStatusSuccessFinishResult, "frame var -a didn't succeed" 97 ) 98 output = command_result.GetOutput() 99 self.assertNotIn("argc", output, "Globals found argc") 100 self.assertNotIn("argv", output, "Globals found argv") 101 self.assertNotIn("test_var", output, "Globals found test_var") 102 self.assertIn("g_var", output, "Globals didn't find g_var") 103 104 def check_frame_variable_errors(self, thread, error_strings): 105 command_result = lldb.SBCommandReturnObject() 106 interp = self.dbg.GetCommandInterpreter() 107 result = interp.HandleCommand("frame variable", command_result) 108 self.assertEqual( 109 result, lldb.eReturnStatusFailed, "frame var succeeded unexpectedly" 110 ) 111 command_error = command_result.GetError() 112 113 frame = thread.GetFrameAtIndex(0) 114 var_list = frame.GetVariables(True, True, False, True) 115 self.assertEqual(var_list.GetSize(), 0) 116 api_error = var_list.GetError() 117 api_error_str = api_error.GetCString() 118 119 for s in error_strings: 120 self.assertIn(s, command_error) 121 for s in error_strings: 122 self.assertIn(s, api_error_str) 123 124 # Check the structured error data. 125 data = api_error.GetErrorData() 126 version = data.GetValueForKey("version") 127 self.assertEqual(version.GetIntegerValue(), 1) 128 err_ty = data.GetValueForKey("type") 129 self.assertEqual(err_ty.GetIntegerValue(), lldb.eErrorTypeGeneric) 130 message = str(data.GetValueForKey("errors").GetItemAtIndex(0)) 131 for s in error_strings: 132 self.assertIn(s, message) 133 134 @skipIfRemote 135 @skipUnlessDarwin 136 def test_darwin_dwarf_missing_obj(self): 137 """ 138 Test that if we build a binary with DWARF in .o files and we remove 139 the .o file for main.cpp, that we get an appropriate error when we 140 do 'frame variable' that explains why we aren't seeing variables. 141 """ 142 self.build(debug_info="dwarf") 143 exe = self.getBuildArtifact("a.out") 144 main_obj = self.getBuildArtifact("main.o") 145 # Delete the main.o file that contains the debug info so we force an 146 # error when we run to main and try to get variables 147 os.unlink(main_obj) 148 149 # We have to set a named breakpoint because we don't have any debug info 150 # because we deleted the main.o file. 151 (target, process, thread, bkpt) = lldbutil.run_to_name_breakpoint(self, "main") 152 error_strings = [ 153 'debug map object file "', 154 'main.o" containing debug info does not exist, debug info will not be loaded', 155 ] 156 self.check_frame_variable_errors(thread, error_strings) 157 158 @skipIfRemote 159 @skipUnlessDarwin 160 def test_darwin_dwarf_obj_mod_time_mismatch(self): 161 """ 162 Test that if we build a binary with DWARF in .o files and we update 163 the mod time of the .o file for main.cpp, that we get an appropriate 164 error when we do 'frame variable' that explains why we aren't seeing 165 variables. 166 """ 167 self.build(debug_info="dwarf") 168 exe = self.getBuildArtifact("a.out") 169 main_obj = self.getBuildArtifact("main.o") 170 171 # Set the modification time for main.o file to the current time after 172 # sleeping for 2 seconds. This ensures the modification time will have 173 # changed and will not match the modification time in the debug map and 174 # force an error when we run to main and try to get variables 175 time.sleep(2) 176 os.utime(main_obj, None) 177 178 # We have to set a named breakpoint because we don't have any debug info 179 # because we deleted the main.o file since the mod times don't match 180 # and debug info won't be loaded 181 (target, process, thread, bkpt) = lldbutil.run_to_name_breakpoint(self, "main") 182 183 error_strings = [ 184 'debug map object file "', 185 'main.o" changed (actual: 0x', 186 ", debug map: 0x", 187 ") since this executable was linked, debug info will not be loaded", 188 ] 189 self.check_frame_variable_errors(thread, error_strings) 190 191 @skipIfRemote 192 @skipIfWindows # Windows can't set breakpoints by name 'main' in this case. 193 def test_gline_tables_only(self): 194 """ 195 Test that if we build a binary with "-gline-tables-only" that we can 196 set a file and line breakpoint successfully, and get an error 197 letting us know that this build option was enabled when trying to 198 read variables. 199 """ 200 self.build(dictionary={"CFLAGS_EXTRAS": "-gline-tables-only"}) 201 exe = self.getBuildArtifact("a.out") 202 203 # We have to set a named breakpoint because we don't have any debug info 204 # because we deleted the main.o file since the mod times don't match 205 # and debug info won't be loaded 206 (target, process, thread, bkpt) = lldbutil.run_to_name_breakpoint(self, "main") 207 error_strings = [ 208 "no variable information is available in debug info for this compile unit" 209 ] 210 self.check_frame_variable_errors(thread, error_strings) 211 212 @skipUnlessPlatform(["linux", "freebsd"]) 213 @add_test_categories(["dwo"]) 214 def test_fission_missing_dwo(self): 215 """ 216 Test that if we build a binary with "-gsplit-dwarf" that we can 217 set a file and line breakpoint successfully, and get an error 218 letting us know we were unable to load the .dwo file. 219 """ 220 self.build(dictionary={"CFLAGS_EXTRAS": "-gsplit-dwarf"}) 221 exe = self.getBuildArtifact("a.out") 222 main_dwo = self.getBuildArtifact("main.dwo") 223 224 self.assertTrue( 225 os.path.exists(main_dwo), 'Make sure "%s" file exists' % (main_dwo) 226 ) 227 # Delete the main.dwo file that contains the debug info so we force an 228 # error when we run to main and try to get variables. 229 os.unlink(main_dwo) 230 231 # We have to set a named breakpoint because we don't have any debug info 232 # because we deleted the main.o file since the mod times don't match 233 # and debug info won't be loaded 234 (target, process, thread, bkpt) = lldbutil.run_to_name_breakpoint(self, "main") 235 error_strings = [ 236 'unable to locate .dwo debug file "', 237 'main.dwo" for skeleton DIE 0x', 238 ] 239 self.check_frame_variable_errors(thread, error_strings) 240 241 @skipUnlessPlatform(["linux", "freebsd"]) 242 @add_test_categories(["dwo"]) 243 def test_fission_invalid_dwo_objectfile(self): 244 """ 245 Test that if we build a binary with "-gsplit-dwarf" that we can 246 set a file and line breakpoint successfully, and get an error 247 letting us know we were unable to load the .dwo file because it 248 existed, but it wasn't a valid object file. 249 """ 250 self.build(dictionary={"CFLAGS_EXTRAS": "-gsplit-dwarf"}) 251 exe = self.getBuildArtifact("a.out") 252 main_dwo = self.getBuildArtifact("main.dwo") 253 254 self.assertTrue( 255 os.path.exists(main_dwo), 'Make sure "%s" file exists' % (main_dwo) 256 ) 257 # Overwrite the main.dwo with the main.c source file so that the .dwo 258 # file exists, but it isn't a valid object file as there is an error 259 # for this case. 260 shutil.copyfile(self.getSourcePath("main.c"), main_dwo) 261 262 # We have to set a named breakpoint because we don't have any debug info 263 # because we deleted the main.o file since the mod times don't match 264 # and debug info won't be loaded 265 (target, process, thread, bkpt) = lldbutil.run_to_name_breakpoint(self, "main") 266 error_strings = [ 267 'unable to load object file for .dwo debug file "' 268 'main.dwo" for unit DIE 0x', 269 ] 270 self.check_frame_variable_errors(thread, error_strings) 271