1""" 2Set breakpoints on objective-c class and instance methods in foundation. 3Also lookup objective-c data types and evaluate expressions. 4""" 5 6import os 7import os.path 8import lldb 9from lldbsuite.test.decorators import * 10from lldbsuite.test.lldbtest import * 11from lldbsuite.test import lldbutil 12 13file_index = 0 14 15 16class FoundationTestCase(TestBase): 17 def setUp(self): 18 # Call super's setUp(). 19 TestBase.setUp(self) 20 # Find the line number to break inside main(). 21 self.main_source = "main.m" 22 self.line = line_number(self.main_source, "// Set break point at this line.") 23 24 def test_break(self): 25 """Test setting objc breakpoints using '_regexp-break' and 'breakpoint set'.""" 26 self.build() 27 exe = self.getBuildArtifact("a.out") 28 self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) 29 30 # Stop at +[NSString stringWithFormat:]. 31 break_results = lldbutil.run_break_set_command( 32 self, "_regexp-break +[NSString stringWithFormat:]" 33 ) 34 lldbutil.check_breakpoint_result( 35 self, 36 break_results, 37 symbol_name="+[NSString stringWithFormat:]", 38 num_locations=1, 39 ) 40 41 # Stop at -[MyString initWithNSString:]. 42 lldbutil.run_break_set_by_symbol( 43 self, 44 "-[MyString initWithNSString:]", 45 num_expected_locations=1, 46 sym_exact=True, 47 ) 48 49 # Stop at the "description" selector. 50 lldbutil.run_break_set_by_selector( 51 self, "description", num_expected_locations=1, module_name="a.out" 52 ) 53 54 # Stop at -[NSAutoreleasePool release]. 55 break_results = lldbutil.run_break_set_command( 56 self, "_regexp-break -[NSAutoreleasePool release]" 57 ) 58 lldbutil.check_breakpoint_result( 59 self, 60 break_results, 61 symbol_name="-[NSAutoreleasePool release]", 62 num_locations=1, 63 ) 64 65 self.runCmd("run", RUN_SUCCEEDED) 66 67 # First stop is +[NSString stringWithFormat:]. 68 self.expect( 69 "thread backtrace", 70 "Stop at +[NSString stringWithFormat:]", 71 substrs=["Foundation`+[NSString stringWithFormat:]"], 72 ) 73 74 self.runCmd("process continue") 75 76 # Second stop is still +[NSString stringWithFormat:]. 77 self.expect( 78 "thread backtrace", 79 "Stop at +[NSString stringWithFormat:]", 80 substrs=["Foundation`+[NSString stringWithFormat:]"], 81 ) 82 83 self.runCmd("process continue") 84 85 # Followed by a.out`-[MyString initWithNSString:]. 86 self.expect( 87 "thread backtrace", 88 "Stop at a.out`-[MyString initWithNSString:]", 89 substrs=["a.out`-[MyString initWithNSString:]"], 90 ) 91 92 self.runCmd("process continue") 93 94 # Followed by -[MyString description]. 95 self.expect( 96 "thread backtrace", 97 "Stop at -[MyString description]", 98 substrs=["a.out`-[MyString description]"], 99 ) 100 101 self.runCmd("process continue") 102 103 # Followed by the same -[MyString description]. 104 self.expect( 105 "thread backtrace", 106 "Stop at -[MyString description]", 107 substrs=["a.out`-[MyString description]"], 108 ) 109 110 self.runCmd("process continue") 111 112 # Followed by -[NSAutoreleasePool release]. 113 self.expect( 114 "thread backtrace", 115 "Stop at -[NSAutoreleasePool release]", 116 substrs=["Foundation`-[NSAutoreleasePool release]"], 117 ) 118 119 # rdar://problem/8542091 120 # rdar://problem/8492646 121 def test_data_type_and_expr(self): 122 """Lookup objective-c data types and evaluate expressions.""" 123 self.build() 124 exe = self.getBuildArtifact("a.out") 125 self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) 126 127 # Stop at -[MyString description]. 128 lldbutil.run_break_set_by_symbol( 129 self, "-[MyString description]", num_expected_locations=1, sym_exact=True 130 ) 131 # self.expect("breakpoint set -n '-[MyString description]", BREAKPOINT_CREATED, 132 # startstr = "Breakpoint created: 1: name = '-[MyString description]', 133 # locations = 1") 134 135 self.runCmd("run", RUN_SUCCEEDED) 136 137 self.runCmd("settings set target.prefer-dynamic-value no-dynamic-values") 138 139 # The backtrace should show we stop at -[MyString description]. 140 self.expect( 141 "thread backtrace", 142 "Stop at -[MyString description]", 143 substrs=["a.out`-[MyString description]"], 144 ) 145 146 # Lookup objc data type MyString and evaluate some expressions. 147 148 self.expect( 149 "image lookup -t NSString", 150 DATA_TYPES_DISPLAYED_CORRECTLY, 151 substrs=['name = "NSString"', 'compiler_type = "@interface NSString'], 152 ) 153 154 self.expect( 155 "image lookup -t MyString", 156 DATA_TYPES_DISPLAYED_CORRECTLY, 157 substrs=[ 158 'name = "MyString"', 159 'compiler_type = "@interface MyString', 160 "NSString * str;", 161 "NSDate * date;", 162 ], 163 ) 164 165 self.expect( 166 "frame variable --show-types --scope", 167 VARIABLES_DISPLAYED_CORRECTLY, 168 substrs=["ARG: (MyString *) self"], 169 patterns=["ARG: \(.*\) _cmd", "(objc_selector *)|(SEL)"], 170 ) 171 172 # rdar://problem/8651752 173 # don't crash trying to ask clang how many children an empty record has 174 self.runCmd("frame variable *_cmd") 175 176 # rdar://problem/8492646 177 # test/foundation fails after updating to tot r115023 178 # self->str displays nothing as output 179 self.expect( 180 "frame variable --show-types self->str", 181 VARIABLES_DISPLAYED_CORRECTLY, 182 startstr="(NSString *) self->str", 183 ) 184 185 # rdar://problem/8447030 186 # 'frame variable self->date' displays the wrong data member 187 self.expect( 188 "frame variable --show-types self->date", 189 VARIABLES_DISPLAYED_CORRECTLY, 190 startstr="(NSDate *) self->date", 191 ) 192 193 # This should display the str and date member fields as well. 194 self.expect( 195 "frame variable --show-types *self", 196 VARIABLES_DISPLAYED_CORRECTLY, 197 substrs=["(MyString) *self", "(NSString *) str", "(NSDate *) date"], 198 ) 199 200 # isa should be accessible. 201 self.expect( 202 "expression self->isa", VARIABLES_DISPLAYED_CORRECTLY, substrs=["Class)"] 203 ) 204 205 # This should fail expectedly. 206 self.expect( 207 "expression self->non_existent_member", 208 COMMAND_FAILED_AS_EXPECTED, 209 error=True, 210 substrs=[ 211 "error:", 212 "'MyString' does not have a member named 'non_existent_member'", 213 ], 214 ) 215 216 # Use expression parser. 217 self.runCmd("expression self->str") 218 self.runCmd("expression self->date") 219 220 # (lldb) expression self->str 221 # error: instance variable 'str' is protected 222 # error: 1 errors parsing expression 223 # 224 # (lldb) expression self->date 225 # error: instance variable 'date' is protected 226 # error: 1 errors parsing expression 227 # 228 229 self.runCmd("breakpoint delete 1") 230 lldbutil.run_break_set_by_file_and_line( 231 self, "main.m", self.line, num_expected_locations=1, loc_exact=True 232 ) 233 234 self.runCmd("process continue") 235 236 # rdar://problem/8542091 237 # test/foundation: expr -o -- my not working? 238 # 239 # Test new feature with r115115: 240 # Add "-o" option to "expression" which prints the object description 241 # if available. 242 self.expect( 243 "expression --object-description -- my", 244 "Object description displayed correctly", 245 patterns=["Hello from.*a.out.*with timestamp: "], 246 ) 247 248 @add_test_categories(["pyapi"]) 249 def test_print_ivars_correctly(self): 250 self.build() 251 # See: <rdar://problem/8717050> lldb needs to use the ObjC runtime symbols for ivar offsets 252 # Only fails for the ObjC 2.0 runtime. 253 exe = self.getBuildArtifact("a.out") 254 255 target = self.dbg.CreateTarget(exe) 256 self.assertTrue(target, VALID_TARGET) 257 258 break1 = target.BreakpointCreateByLocation(self.main_source, self.line) 259 self.assertTrue(break1, VALID_BREAKPOINT) 260 261 # Now launch the process, and do not stop at entry point. 262 process = target.LaunchSimple(None, None, self.get_process_working_directory()) 263 264 self.assertTrue(process, PROCESS_IS_VALID) 265 266 # The stop reason of the thread should be breakpoint. 267 thread = process.GetThreadAtIndex(0) 268 if thread.GetStopReason() != lldb.eStopReasonBreakpoint: 269 from lldbsuite.test.lldbutil import stop_reason_to_str 270 271 self.fail( 272 STOPPED_DUE_TO_BREAKPOINT_WITH_STOP_REASON_AS 273 % stop_reason_to_str(thread.GetStopReason()) 274 ) 275 276 # Make sure we stopped at the first breakpoint. 277 278 cur_frame = thread.GetFrameAtIndex(0) 279 280 line_number = cur_frame.GetLineEntry().GetLine() 281 self.assertEqual(line_number, self.line, "Hit the first breakpoint.") 282 283 my_var = cur_frame.FindVariable("my") 284 self.assertTrue(my_var, "Made a variable object for my") 285 286 str_var = cur_frame.FindVariable("str") 287 self.assertTrue(str_var, "Made a variable object for str") 288 289 # Now make sure that the my->str == str: 290 291 my_str_var = my_var.GetChildMemberWithName("str") 292 self.assertTrue(my_str_var, "Found a str ivar in my") 293 294 str_value = int(str_var.GetValue(), 0) 295 296 my_str_value = int(my_str_var.GetValue(), 0) 297 298 self.assertEqual(str_value, my_str_value, "Got the correct value for my->str") 299 300 def test_expression_lookups_objc(self): 301 """Test running an expression detect spurious debug info lookups (DWARF).""" 302 self.build() 303 exe = self.getBuildArtifact("a.out") 304 self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) 305 306 # Stop at -[MyString initWithNSString:]. 307 lldbutil.run_break_set_by_symbol( 308 self, 309 "-[MyString initWithNSString:]", 310 num_expected_locations=1, 311 sym_exact=True, 312 ) 313 314 self.runCmd("run", RUN_SUCCEEDED) 315 316 global file_index 317 # Log any DWARF lookups 318 ++file_index 319 logfile = os.path.join( 320 self.getBuildDir(), 321 "dwarf-lookups-" + self.getArchitecture() + "-" + str(file_index) + ".txt", 322 ) 323 self.runCmd("log enable -f %s dwarf lookups" % (logfile)) 324 self.runCmd("expr self") 325 self.runCmd("log disable dwarf lookups") 326 327 def cleanup(): 328 if os.path.exists(logfile): 329 os.unlink(logfile) 330 331 self.addTearDownHook(cleanup) 332 333 if os.path.exists(logfile): 334 f = open(logfile) 335 lines = f.readlines() 336 num_errors = 0 337 for line in lines: 338 if "$__lldb" in line: 339 if num_errors == 0: 340 print( 341 "error: found spurious name lookups when evaluating an expression:" 342 ) 343 num_errors += 1 344 print(line, end="") 345 self.assertEqual(num_errors, 0, "Spurious lookups detected") 346 f.close() 347