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