Home | History | Annotate | Download | only in inline-stepping
      1 """Test stepping over and into inlined functions."""
      2 
      3 import os, time
      4 import unittest2
      5 import lldb
      6 import lldbutil
      7 from lldbtest import *
      8 
      9 class TestInlineStepping(TestBase):
     10 
     11     mydir = os.path.join("functionalities", "inline-stepping")
     12 
     13     @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
     14     @python_api_test
     15     @dsym_test
     16     def test_with_dsym_and_python_api(self):
     17         """Test stepping over and into inlined functions."""
     18         self.buildDsym()
     19         self.inline_stepping()
     20 
     21     @python_api_test
     22     @dwarf_test
     23     @expectedFailureIcc # Not really a bug.  ICC combines two inlined functions.
     24     def test_with_dwarf_and_python_api(self):
     25         """Test stepping over and into inlined functions."""
     26         self.buildDwarf()
     27         self.inline_stepping()
     28 
     29     @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
     30     @python_api_test
     31     @dsym_test
     32     def test_step_over_with_dsym_and_python_api(self):
     33         """Test stepping over and into inlined functions."""
     34         self.buildDsym()
     35         self.inline_stepping_step_over()
     36 
     37     @python_api_test
     38     @dwarf_test
     39     def test_step_over_with_dwarf_and_python_api(self):
     40         """Test stepping over and into inlined functions."""
     41         self.buildDwarf()
     42         self.inline_stepping_step_over()
     43 
     44     def setUp(self):
     45         # Call super's setUp().
     46         TestBase.setUp(self)
     47         # Find the line numbers that we will step to in main:
     48         self.main_source = "calling.cpp"
     49         self.source_lines = {}
     50         functions = ['caller_ref_1', 'caller_ref_2', 'inline_ref_1', 'inline_ref_2', 'called_by_inline_ref', 'caller_trivial_1', 'caller_trivial_2', 'inline_trivial_1', 'inline_trivial_2', 'called_by_inline_trivial' ]
     51         for name in functions:
     52             self.source_lines[name] = line_number(self.main_source, "// In " + name + ".")
     53         self.main_source_spec = lldb.SBFileSpec (self.main_source)
     54 
     55     def do_step(self, step_type, destination_line_entry):
     56         expected_stack_depth = self.thread.GetNumFrames()
     57         if step_type == "into":
     58             expected_stack_depth += 1
     59             self.thread.StepInto()
     60         elif step_type == "out":
     61             expected_stack_depth -= 1
     62             self.thread.StepOut()
     63         elif step_type == "over":
     64             self.thread.StepOver()
     65         else:
     66             self.fail ("Unrecognized step type: " + step_type)
     67 
     68         threads = lldbutil.get_stopped_threads (self.process, lldb.eStopReasonPlanComplete)
     69         if len(threads) != 1:
     70             destination_description = lldb.SBStream()
     71             destination_line_entry.GetDescription(destination_description)
     72             self.fail ("Failed to stop due to step " + step_type + " operation stepping to: " + destination_description.GetData())
     73 
     74         self.thread = threads[0]
     75 
     76         stop_line_entry = self.thread.GetFrameAtIndex(0).GetLineEntry()
     77         self.assertTrue (stop_line_entry.IsValid(), "Stop line entry was not valid.")
     78 
     79         # Don't use the line entry equal operator because we don't care about the column number.
     80         stop_at_right_place = (stop_line_entry.GetFileSpec() == destination_line_entry.GetFileSpec() and stop_line_entry.GetLine() == destination_line_entry.GetLine())
     81         if stop_at_right_place == False:
     82             destination_description = lldb.SBStream()
     83             destination_line_entry.GetDescription(destination_description)
     84 
     85             actual_description = lldb.SBStream()
     86             stop_line_entry.GetDescription(actual_description)
     87 
     88             self.fail ("Step " + step_type + " stopped at wrong place: expected: " + destination_description.GetData() + " got: " + actual_description.GetData() + ".")
     89 
     90         real_stack_depth = self.thread.GetNumFrames()
     91 
     92         if real_stack_depth != expected_stack_depth:
     93             destination_description = lldb.SBStream()
     94             destination_line_entry.GetDescription(destination_description)
     95             self.fail ("Step %s to %s got wrong number of frames, should be: %d was: %d."%(step_type, destination_description.GetData(), expected_stack_depth, real_stack_depth))
     96             
     97     def run_step_sequence(self, step_sequence):
     98         """This function takes a list of duples instructing how to run the program.  The first element in each duple is
     99            a source pattern for the target location, and the second is the operation that will take you from the current
    100            source location to the target location.  It will then run all the steps in the sequence.
    101            It will check that you arrived at the expected source location at each step, and that the stack depth changed 
    102            correctly for the operation in the sequence."""
    103 
    104         target_line_entry = lldb.SBLineEntry()
    105         target_line_entry.SetFileSpec(self.main_source_spec)
    106 
    107         for step_pattern in step_sequence:
    108             step_stop_line = line_number (self.main_source, step_pattern[0])
    109             target_line_entry.SetLine(step_stop_line)
    110             self.do_step (step_pattern[1], target_line_entry)
    111         
    112 
    113     def inline_stepping(self):
    114         """Use Python APIs to test stepping over and hitting breakpoints."""
    115         exe = os.path.join(os.getcwd(), "a.out")
    116 
    117         target = self.dbg.CreateTarget(exe)
    118         self.assertTrue(target, VALID_TARGET)
    119 
    120         break_1_in_main = target.BreakpointCreateBySourceRegex ('// Stop here and step over to set up stepping over.', self.main_source_spec)
    121         self.assertTrue(break_1_in_main, VALID_BREAKPOINT)
    122 
    123         # Now launch the process, and do not stop at entry point.
    124         self.process = target.LaunchSimple (None, None, os.getcwd())
    125 
    126         self.assertTrue(self.process, PROCESS_IS_VALID)
    127 
    128         # The stop reason of the thread should be breakpoint.
    129         threads = lldbutil.get_threads_stopped_at_breakpoint (self.process, break_1_in_main)
    130 
    131         if len(threads) != 1:
    132             self.fail ("Failed to stop at first breakpoint in main.")
    133 
    134         self.thread = threads[0]
    135 
    136         # Step over the inline_value = 0 line to get us to inline_trivial_1 called from main.  Doing it this way works
    137         # around a bug in lldb where the breakpoint on the containing line of an inlined function with no return value
    138         # gets set past the insertion line in the function.
    139         # Then test stepping over a simple inlined function.  Note, to test all the parts of the inlined stepping
    140         # the calls inline_stepping_1 and inline_stepping_2 should line up at the same address, that way we will test
    141         # the "virtual" stepping.  
    142         # FIXME: Put in a check to see if that is true and warn if it is not.
    143 
    144         step_sequence = [["// At inline_trivial_1 called from main.", "over"], 
    145                          ["// At first call of caller_trivial_1 in main.", "over"]]
    146         self.run_step_sequence(step_sequence)
    147            
    148         # Now step from caller_ref_1 all the way into called_by_inline_trivial
    149 
    150         step_sequence = [["// In caller_trivial_1.", "into"], 
    151                          ["// In caller_trivial_2.", "into"], 
    152                          ["// In inline_trivial_1.", "into"],
    153                          ["// In inline_trivial_2.", "into"],
    154                          ["// At caller_by_inline_trivial in inline_trivial_2.", "over"],
    155                          ["// In called_by_inline_trivial.", "into"]]
    156         self.run_step_sequence(step_sequence)
    157 
    158         # Now run to the inline_trivial_1 just before the immediate step into inline_trivial_2:
    159 
    160         break_2_in_main = target.BreakpointCreateBySourceRegex ('// At second call of caller_trivial_1 in main.', self.main_source_spec)
    161         self.assertTrue(break_2_in_main, VALID_BREAKPOINT)
    162 
    163         threads = lldbutil.continue_to_breakpoint (self.process, break_2_in_main)
    164         self.assertTrue (len(threads) == 1, "Successfully ran to call site of second caller_trivial_1 call.")
    165         self.thread = threads[0]
    166         
    167         step_sequence = [["// In caller_trivial_1.", "into"], 
    168                          ["// In caller_trivial_2.", "into"], 
    169                          ["// In inline_trivial_1.", "into"]]
    170         self.run_step_sequence(step_sequence)
    171 
    172         # Then call some trivial function, and make sure we end up back where we were in the inlined call stack:
    173         
    174         frame = self.thread.GetFrameAtIndex(0)
    175         before_line_entry = frame.GetLineEntry()
    176         value = frame.EvaluateExpression ("function_to_call()")
    177         after_line_entry = frame.GetLineEntry()
    178 
    179         self.assertTrue (before_line_entry.GetLine() == after_line_entry.GetLine(), "Line entry before and after function calls are the same.")
    180 
    181         # Now make sure stepping OVER in the middle of the stack works, and then check finish from the inlined frame:
    182 
    183         step_sequence = [["// At increment in inline_trivial_1.", "over"],
    184                          ["// At increment in caller_trivial_2.", "out"]]
    185         self.run_step_sequence(step_sequence)
    186 
    187 
    188         # Now run to the place in main just before the first call to caller_ref_1:
    189 
    190         break_3_in_main = target.BreakpointCreateBySourceRegex ('// At first call of caller_ref_1 in main.', self.main_source_spec)
    191         self.assertTrue(break_3_in_main, VALID_BREAKPOINT)
    192 
    193         threads = lldbutil.continue_to_breakpoint (self.process, break_3_in_main)
    194         self.assertTrue (len(threads) == 1, "Successfully ran to call site of first caller_ref_1 call.")
    195         self.thread = threads[0]
    196 
    197         step_sequence = [["// In caller_ref_1.", "into"],
    198                          ["// In caller_ref_2.", "into"],
    199                          ["// In inline_ref_1.", "into"],
    200                          ["// In inline_ref_2.", "into"],
    201                          ["// In called_by_inline_ref.", "into"],
    202                          ["// In inline_ref_2.", "out"],
    203                          ["// In inline_ref_1.", "out"],
    204                          ["// At increment in inline_ref_1.", "over"],
    205                          ["// In caller_ref_2.", "out"],
    206                          ["// At increment in caller_ref_2.", "over"]]
    207         self.run_step_sequence (step_sequence)
    208 
    209     def inline_stepping_step_over(self):
    210         """Use Python APIs to test stepping over and hitting breakpoints."""
    211         exe = os.path.join(os.getcwd(), "a.out")
    212 
    213         target = self.dbg.CreateTarget(exe)
    214         self.assertTrue(target, VALID_TARGET)
    215 
    216         break_1_in_main = target.BreakpointCreateBySourceRegex ('// At second call of caller_ref_1 in main.', self.main_source_spec)
    217         self.assertTrue(break_1_in_main, VALID_BREAKPOINT)
    218 
    219         # Now launch the process, and do not stop at entry point.
    220         self.process = target.LaunchSimple (None, None, os.getcwd())
    221 
    222         self.assertTrue(self.process, PROCESS_IS_VALID)
    223 
    224         # The stop reason of the thread should be breakpoint.
    225         threads = lldbutil.get_threads_stopped_at_breakpoint (self.process, break_1_in_main)
    226 
    227         if len(threads) != 1:
    228             self.fail ("Failed to stop at first breakpoint in main.")
    229 
    230         self.thread = threads[0]
    231 
    232         step_sequence = [["// In caller_ref_1.", "into"],
    233                          ["// In caller_ref_2.", "into"],
    234                          ["// At increment in caller_ref_2.", "over"]]
    235         self.run_step_sequence (step_sequence)
    236 
    237         
    238         
    239 
    240 if __name__ == '__main__':
    241     import atexit
    242     lldb.SBDebugger.Initialize()
    243     atexit.register(lambda: lldb.SBDebugger.Terminate())
    244     unittest2.main()
    245