347 lines · python
1"""2Set breakpoints on objective-c class and instance methods in foundation.3Also lookup objective-c data types and evaluate expressions.4"""5 6import os7import os.path8import lldb9from lldbsuite.test.decorators import *10from lldbsuite.test.lldbtest import *11from lldbsuite.test import lldbutil12 13file_index = 014 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/8542091120 # rdar://problem/8492646121 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=True130 )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=[r"ARG: \(SEL|objc_selector \*\) _cmd"],170 )171 172 # rdar://problem/8651752173 # don't crash trying to ask clang how many children an empty record has174 self.runCmd("frame variable *_cmd")175 176 # rdar://problem/8492646177 # test/foundation fails after updating to tot r115023178 # self->str displays nothing as output179 self.expect(180 "frame variable --show-types self->str",181 VARIABLES_DISPLAYED_CORRECTLY,182 startstr="(NSString *) self->str",183 )184 185 # rdar://problem/8447030186 # 'frame variable self->date' displays the wrong data member187 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->str221 # error: instance variable 'str' is protected222 # error: 1 errors parsing expression223 #224 # (lldb) expression self->date225 # error: instance variable 'date' is protected226 # error: 1 errors parsing expression227 #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=True232 )233 234 self.runCmd("process continue")235 236 # rdar://problem/8542091237 # test/foundation: expr -o -- my not working?238 #239 # Test new feature with r115115:240 # Add "-o" option to "expression" which prints the object description241 # 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 offsets252 # 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_str270 271 self.fail(272 STOPPED_DUE_TO_BREAKPOINT_WITH_STOP_REASON_AS273 % 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_index317 # Log any DWARF lookups318 ++file_index319 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 = 0337 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 += 1344 print(line, end="")345 self.assertEqual(num_errors, 0, "Spurious lookups detected")346 f.close()347