xref: /llvm-project/lldb/test/API/lang/objc/foundation/TestObjCMethods.py (revision 2238dcc39358353cac21df75c3c3286ab20b8f53)
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