| # TestSwiftStepping.py |
| # |
| # This source file is part of the Swift.org open source project |
| # |
| # Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors |
| # Licensed under Apache License v2.0 with Runtime Library Exception |
| # |
| # See https://swift.org/LICENSE.txt for license information |
| # See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| # |
| # ------------------------------------------------------------------------------ |
| """ |
| Tests simple swift stepping |
| """ |
| import lldb |
| import lldbsuite.test.decorators as decorators |
| import lldbsuite.test.lldbtest as lldbtest |
| import lldbsuite.test.lldbutil as lldbutil |
| import os |
| import platform |
| import unittest2 |
| |
| |
| class TestSwiftStepping(lldbtest.TestBase): |
| |
| mydir = lldbtest.TestBase.compute_mydir(__file__) |
| |
| @decorators.swiftTest |
| def test_swift_stepping(self): |
| """Tests that we can step reliably in swift code.""" |
| self.build() |
| self.do_test() |
| |
| def setUp(self): |
| lldbtest.TestBase.setUp(self) |
| self.main_source = "main.swift" |
| self.main_source_spec = lldb.SBFileSpec(self.main_source) |
| # If you are running against a debug swift you are going to |
| # end up stepping into the stdlib and that will make stepping |
| # tests impossible to write. So avoid that. |
| |
| if platform.system() == 'Darwin': |
| lib_name = "libswiftCore.dylib" |
| else: |
| lib_name = "libswiftCore.so" |
| |
| self.dbg.HandleCommand( |
| "settings set " |
| "target.process.thread.step-avoid-libraries {}".format(lib_name)) |
| |
| def hit_correct_line(self, thread, pattern, fail_if_wrong=True): |
| # print "Check if we got to: ", pattern |
| target_line = lldbtest.line_number(self.main_source, pattern) |
| self.assertTrue( |
| target_line != 0, |
| "Could not find source pattern " + pattern) |
| cur_line = thread.frames[0].GetLineEntry().GetLine() |
| hit_line = cur_line == target_line |
| if fail_if_wrong: |
| self.assertTrue( |
| hit_line, |
| "Stepped to line %d instead of expected %d " |
| "with pattern '%s'." % (cur_line, target_line, pattern)) |
| return hit_line |
| |
| def hit_correct_function(self, thread, pattern): |
| # print "Check if we got to: ", pattern |
| name = thread.frames[0].GetFunctionName() |
| self.assertTrue( |
| pattern in name, |
| "Got to '%s' not the expected function '%s'." % (name, pattern)) |
| |
| def do_test(self): |
| """Tests that we can step reliably in swift code.""" |
| exe_name = "a.out" |
| exe = os.path.join(os.getcwd(), exe_name) |
| |
| # Create the target |
| target = self.dbg.CreateTarget(exe) |
| self.assertTrue(target, lldbtest.VALID_TARGET) |
| |
| # Set the breakpoints |
| breakpoint = target.BreakpointCreateBySourceRegex( |
| 'Stop here first in main', self.main_source_spec) |
| self.assertTrue( |
| breakpoint.GetNumLocations() > 0, lldbtest.VALID_BREAKPOINT) |
| |
| # Launch the process, and do not stop at the entry point. |
| process = target.LaunchSimple(None, None, os.getcwd()) |
| |
| self.assertTrue(process, lldbtest.PROCESS_IS_VALID) |
| |
| # Frame #0 should be at our breakpoint. |
| threads = lldbutil.get_threads_stopped_at_breakpoint( |
| process, breakpoint) |
| |
| self.assertTrue(len(threads) == 1) |
| thread = threads[0] |
| frame = thread.frames[0] |
| self.assertTrue(frame, "Frame 0 is valid.") |
| |
| # We are before using an int to set an enum value (which has a |
| # toSomeValues function). So step into should go into the |
| # toSomeValues function. |
| |
| thread.StepInto() |
| self.hit_correct_function(thread, "toSomeValues") |
| |
| thread.StepOut() |
| self.hit_correct_function(thread, "main") |
| |
| thread.StepOver() |
| thread.StepOver() |
| |
| # That should take me to the equals operator. |
| self.hit_correct_line(thread, "Stop here to get into equality") |
| |
| thread.StepInto() |
| self.hit_correct_function(thread, "==") |
| |
| thread.StepOut() |
| thread.StepOver() |
| |
| self.hit_correct_line(thread, "Step over the if should get here") |
| |
| thread.StepOver() |
| self.hit_correct_line(thread, "Stop here to step into B constructor.") |
| |
| thread.StepInto() |
| prologue_at_first_line = self.hit_correct_line( |
| thread, "At the first line of B constructor.", False) |
| if prologue_at_first_line: |
| thread.StepOver() |
| |
| self.hit_correct_line( |
| thread, "In the B constructor about to call super.") |
| |
| # Make sure we can follow super into the parent constructor. |
| thread.StepInto() |
| |
| prologue_at_first_line = self.hit_correct_line( |
| thread, "Here is the A constructor.", False) |
| if prologue_at_first_line: |
| thread.StepOver() |
| |
| self.hit_correct_line(thread, "A init: x assignment.") |
| |
| thread.StepOver() |
| self.hit_correct_line(thread, "A init: y assignment.") |
| |
| thread.StepOver() |
| self.hit_correct_line(thread, "A init: end of function.") |
| |
| # Now two step outs should take us back where we started from: |
| thread.StepOut() |
| thread.StepOut() |
| name = thread.frames[0].GetFunctionName() |
| if "allocating_init" in name: |
| thread.StepOut() |
| self.hit_correct_line(thread, "Stop here to step into B constructor.") |
| |
| thread.StepOver() |
| self.hit_correct_line( |
| thread, "Stop here to step into call_overridden.") |
| |
| # Make sure we get into the overridden call as expected: |
| thread.StepInto() |
| self.hit_correct_function(thread, "call_overridden") |
| stopped_at_func_defn = self.hit_correct_line( |
| thread, "call_overridden func def", False) |
| if stopped_at_func_defn: |
| thread.StepOver() |
| thread.StepInto() |
| self.hit_correct_function(thread, "ClassB.do_something") |
| |
| # Now step out twice to get back to main: |
| thread.StepOut() |
| self.hit_correct_function(thread, "call_overridden") |
| thread.StepOut() |
| self.hit_correct_line( |
| thread, "Stop here to step into call_overridden.") |
| |
| # Two steps should get us to the switch: |
| thread.StepOver() |
| self.hit_correct_line(thread, "At point initializer.") |
| thread.StepOver() |
| # Due to: <rdar://problem/15888936> we will stop inside the |
| # switch statement instead of at the switch: |
| # self.hit_correct_line (thread, "At the beginning of the switch.") |
| self.hit_correct_line(thread, "case (0, 0):") |
| |
| thread.StepOver() |
| self.hit_correct_line(thread, "case (_, 0):") |
| thread.StepInto() |
| self.hit_correct_line(thread, "case (0, _):") |
| thread.StepOver() |
| |
| stopped_at_case = self.hit_correct_line( |
| thread, "case (let x, let y) where", False) |
| if stopped_at_case: |
| thread.StepOver() |
| |
| self.hit_correct_line(thread, "First case with a where statement.") |
| thread.StepOver() |
| self.hit_correct_line(thread, "Second case with a where statement") |
| |
| thread.StepInto() |
| self.hit_correct_line( |
| thread, "return_same gets called in both where statements") |
| |
| thread.StepOut() |
| self.hit_correct_line(thread, "Second case with a where statement") |
| |
| thread.StepOver() |
| self.hit_correct_line( |
| thread, "print in second case with where statement.") |
| |
| # |
| # For some reason, we don't step from the body of the case to |
| # the end of the switch, but back to the case: statement, and |
| # then directly out of the switch. |
| # |
| thread.StepOver() |
| steps_back_to_case = self.hit_correct_line( |
| thread, |
| "Sometimes the line table steps to here " |
| "after the body of the case.", False) |
| if steps_back_to_case: |
| self.fail( |
| "Stepping past a taken body of a case statement should not " |
| "step back to the case statement.") |
| |
| if self.hit_correct_line( |
| thread, |
| "This is the end of the switch statement", False): |
| thread.StepOver() |
| elif not self.hit_correct_line( |
| thread, "Make a version of P that conforms directly", False): |
| self.fail( |
| "Stepping past the body of the case didn't stop " |
| "where expected.") |
| |
| self.hit_correct_line( |
| thread, "Make a version of P that conforms directly") |
| |
| # FIXME: Stepping into constructors is kind of a mess, |
| # I'm going to just step over for these tests. |
| thread.StepOver() |
| self.hit_correct_line(thread, "direct.protocol_func(10)") |
| # This tests stepping in through the protocol thunk: |
| thread.StepInto() |
| |
| stop_at_prologue = self.hit_correct_line( |
| thread, |
| "We stopped at the protocol_func declaration instead.", |
| False) |
| if stop_at_prologue: |
| thread.StepOver() |
| self.hit_correct_line( |
| thread, "This is where we will stop in the protocol dispatch") |
| |
| # Step out of the protocol functions, one step should get us |
| # past any dispatch thunk. |
| thread.StepOut() |
| |
| # Finish may not get us past the line we were on, since it |
| # stops immediately on returning |
| # to the caller frame. If so we will need to step again: |
| stop_in_caller_line = self.hit_correct_line( |
| thread, "direct.protocol_func(10)", False) |
| if stop_in_caller_line: |
| thread.StepOver() |
| self.hit_correct_line( |
| thread, "Make a version of P that conforms through a subclass") |
| |
| # This steps into another protocol dispatch. |
| thread.StepOver() |
| thread.StepInto() |
| stop_at_prologue = self.hit_correct_line( |
| thread, |
| "We stopped at the protocol_func declaration instead.", |
| False) |
| if stop_at_prologue: |
| thread.StepOver() |
| self.hit_correct_line( |
| thread, "This is where we will stop in the protocol dispatch") |
| |
| # Step out of the protocol function, one step out should also |
| # get us past any dispatch thunk. |
| thread.StepOut() |
| stop_on_caller = self.hit_correct_line(thread, "indirect.protocol_func(20)", False) |
| |
| # And one step over is necessary because step out doesn't |
| # finish off the line. |
| if stop_on_caller: |
| thread.StepOver() |
| self.hit_correct_line(thread, "doSomethingWithFunction(cd_maker, 10)") |
| |
| thread.StepInto() |
| stop_in_prologue = self.hit_correct_line( |
| thread, "Stopped in doSomethingWithFunctionResult decl.", False) |
| if stop_in_prologue: |
| thread.StepOver() |
| self.hit_correct_line( |
| thread, "Calling doSomethingWithFunction with value") |
| thread.StepOver() |
| self.hit_correct_line(thread, "let result = f(other_value)") |
| |
| # Now try stepping into calling a closure, there's several |
| # layers of goo to get through: |
| thread.StepInto() |
| stop_in_prologue = self.hit_correct_line( |
| thread, "Step into cd_maker stops at closure decl instead.", False) |
| if stop_in_prologue: |
| thread.StepOver() |
| |
| self.hit_correct_line(thread, "Step into should stop here in closure.") |
| |
| # Then make sure we can get back out: |
| thread.StepOut() |
| # Again, step out may not have completed the source line we |
| # stepped in FROM... |
| stop_in_caller_line = self.hit_correct_line( |
| thread, "let result = f(other_value)", False) |
| if stop_in_caller_line: |
| thread.StepOver() |
| |
| self.hit_correct_line( |
| thread, "result.protocol_func(other_value)") |
| |
| def tearDown(self): |
| self.dbg.HandleCommand( |
| "settings clear target.process.thread.step-avoid-libraries") |
| super(TestSwiftStepping, self).tearDown() |
| |
| if __name__ == '__main__': |
| import atexit |
| lldb.SBDebugger.Initialize() |
| atexit.register(lldb.SBDebugger.Terminate) |
| unittest2.main() |