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