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