2012-09-07 23:58:16 +00:00
""" Test stepping over and into inlined functions. """
2015-10-23 17:04:29 +00:00
from __future__ import print_function
2015-11-03 19:20:39 +00:00
2015-10-22 20:06:20 +00:00
2014-03-19 17:50:46 +00:00
import os , time , sys
2012-09-07 23:58:16 +00:00
import lldb
2015-11-03 02:06:18 +00:00
import lldbsuite . test . lldbutil as lldbutil
from lldbsuite . test . lldbtest import *
2012-09-07 23:58:16 +00:00
class TestInlineStepping ( TestBase ) :
2013-12-10 23:19:29 +00:00
mydir = TestBase . compute_mydir ( __file__ )
2012-09-07 23:58:16 +00:00
2015-10-26 09:28:32 +00:00
@add_test_categories ( [ ' pyapi ' ] )
2013-09-13 15:34:59 +00:00
@expectedFailureFreeBSD ( ' llvm.org/pr17214 ' )
2013-08-05 17:12:35 +00:00
@expectedFailureIcc # Not really a bug. ICC combines two inlined functions.
2015-09-11 20:01:24 +00:00
@expectedFailureWindows ( " llvm.org/pr24778 " )
2015-05-21 18:07:16 +00:00
# failed 1/365 dosep runs, (i386-clang), TestInlineStepping.py:237 failed to stop at first breakpoint in main
@expectedFailureAll ( oslist = [ " linux " ] , archs = [ " i386 " ] )
2015-09-30 10:12:40 +00:00
def test_with_python_api ( self ) :
2012-09-07 23:58:16 +00:00
""" Test stepping over and into inlined functions. """
2015-09-30 10:12:40 +00:00
self . build ( )
2012-09-07 23:58:16 +00:00
self . inline_stepping ( )
2015-10-26 09:28:32 +00:00
@add_test_categories ( [ ' pyapi ' ] )
2015-09-30 10:12:40 +00:00
def test_step_over_with_python_api ( self ) :
2012-09-07 23:58:16 +00:00
""" Test stepping over and into inlined functions. """
2015-09-30 10:12:40 +00:00
self . build ( )
2012-09-07 23:58:16 +00:00
self . inline_stepping_step_over ( )
2015-07-24 08:54:22 +00:00
2015-10-26 09:28:32 +00:00
@add_test_categories ( [ ' pyapi ' ] )
2015-09-30 10:12:40 +00:00
def test_step_in_template_with_python_api ( self ) :
2015-07-24 08:54:22 +00:00
""" Test stepping in to templated functions. """
2015-09-30 10:12:40 +00:00
self . build ( )
2015-07-24 08:54:22 +00:00
self . step_in_template ( )
2012-09-07 23:58:16 +00:00
def setUp ( self ) :
# Call super's setUp().
TestBase . setUp ( self )
# Find the line numbers that we will step to in main:
self . main_source = " calling.cpp "
self . source_lines = { }
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 ' ]
for name in functions :
self . source_lines [ name ] = line_number ( self . main_source , " // In " + name + " . " )
self . main_source_spec = lldb . SBFileSpec ( self . main_source )
2014-03-19 17:50:46 +00:00
def do_step ( self , step_type , destination_line_entry , test_stack_depth ) :
2012-09-07 23:58:16 +00:00
expected_stack_depth = self . thread . GetNumFrames ( )
if step_type == " into " :
expected_stack_depth + = 1
self . thread . StepInto ( )
elif step_type == " out " :
expected_stack_depth - = 1
self . thread . StepOut ( )
elif step_type == " over " :
self . thread . StepOver ( )
else :
self . fail ( " Unrecognized step type: " + step_type )
threads = lldbutil . get_stopped_threads ( self . process , lldb . eStopReasonPlanComplete )
if len ( threads ) != 1 :
destination_description = lldb . SBStream ( )
destination_line_entry . GetDescription ( destination_description )
self . fail ( " Failed to stop due to step " + step_type + " operation stepping to: " + destination_description . GetData ( ) )
self . thread = threads [ 0 ]
stop_line_entry = self . thread . GetFrameAtIndex ( 0 ) . GetLineEntry ( )
self . assertTrue ( stop_line_entry . IsValid ( ) , " Stop line entry was not valid. " )
# Don't use the line entry equal operator because we don't care about the column number.
stop_at_right_place = ( stop_line_entry . GetFileSpec ( ) == destination_line_entry . GetFileSpec ( ) and stop_line_entry . GetLine ( ) == destination_line_entry . GetLine ( ) )
if stop_at_right_place == False :
destination_description = lldb . SBStream ( )
destination_line_entry . GetDescription ( destination_description )
actual_description = lldb . SBStream ( )
stop_line_entry . GetDescription ( actual_description )
self . fail ( " Step " + step_type + " stopped at wrong place: expected: " + destination_description . GetData ( ) + " got: " + actual_description . GetData ( ) + " . " )
real_stack_depth = self . thread . GetNumFrames ( )
2014-03-19 17:50:46 +00:00
if test_stack_depth and real_stack_depth != expected_stack_depth :
2012-09-07 23:58:16 +00:00
destination_description = lldb . SBStream ( )
destination_line_entry . GetDescription ( destination_description )
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 ) )
def run_step_sequence ( self , step_sequence ) :
""" This function takes a list of duples instructing how to run the program. The first element in each duple is
a source pattern for the target location , and the second is the operation that will take you from the current
source location to the target location . It will then run all the steps in the sequence .
It will check that you arrived at the expected source location at each step , and that the stack depth changed
correctly for the operation in the sequence . """
target_line_entry = lldb . SBLineEntry ( )
target_line_entry . SetFileSpec ( self . main_source_spec )
2014-03-19 17:50:46 +00:00
test_stack_depth = True
# Work around for <rdar://problem/16363195>, the darwin unwinder seems flakey about whether it duplicates the first frame
# or not, which makes counting stack depth unreliable.
2015-04-02 18:24:03 +00:00
if self . platformIsDarwin ( ) :
2014-03-19 17:50:46 +00:00
test_stack_depth = False
2012-09-07 23:58:16 +00:00
for step_pattern in step_sequence :
step_stop_line = line_number ( self . main_source , step_pattern [ 0 ] )
target_line_entry . SetLine ( step_stop_line )
2014-03-19 17:50:46 +00:00
self . do_step ( step_pattern [ 1 ] , target_line_entry , test_stack_depth )
2012-09-07 23:58:16 +00:00
def inline_stepping ( self ) :
""" Use Python APIs to test stepping over and hitting breakpoints. """
exe = os . path . join ( os . getcwd ( ) , " a.out " )
target = self . dbg . CreateTarget ( exe )
self . assertTrue ( target , VALID_TARGET )
break_1_in_main = target . BreakpointCreateBySourceRegex ( ' // Stop here and step over to set up stepping over. ' , self . main_source_spec )
self . assertTrue ( break_1_in_main , VALID_BREAKPOINT )
# Now launch the process, and do not stop at entry point.
2013-12-13 19:18:59 +00:00
self . process = target . LaunchSimple ( None , None , self . get_process_working_directory ( ) )
2012-09-07 23:58:16 +00:00
self . assertTrue ( self . process , PROCESS_IS_VALID )
# The stop reason of the thread should be breakpoint.
threads = lldbutil . get_threads_stopped_at_breakpoint ( self . process , break_1_in_main )
if len ( threads ) != 1 :
self . fail ( " Failed to stop at first breakpoint in main. " )
self . thread = threads [ 0 ]
# Step over the inline_value = 0 line to get us to inline_trivial_1 called from main. Doing it this way works
# around a bug in lldb where the breakpoint on the containing line of an inlined function with no return value
# gets set past the insertion line in the function.
# Then test stepping over a simple inlined function. Note, to test all the parts of the inlined stepping
# the calls inline_stepping_1 and inline_stepping_2 should line up at the same address, that way we will test
# the "virtual" stepping.
# FIXME: Put in a check to see if that is true and warn if it is not.
step_sequence = [ [ " // At inline_trivial_1 called from main. " , " over " ] ,
[ " // At first call of caller_trivial_1 in main. " , " over " ] ]
self . run_step_sequence ( step_sequence )
# Now step from caller_ref_1 all the way into called_by_inline_trivial
step_sequence = [ [ " // In caller_trivial_1. " , " into " ] ,
[ " // In caller_trivial_2. " , " into " ] ,
[ " // In inline_trivial_1. " , " into " ] ,
[ " // In inline_trivial_2. " , " into " ] ,
[ " // At caller_by_inline_trivial in inline_trivial_2. " , " over " ] ,
[ " // In called_by_inline_trivial. " , " into " ] ]
self . run_step_sequence ( step_sequence )
# Now run to the inline_trivial_1 just before the immediate step into inline_trivial_2:
break_2_in_main = target . BreakpointCreateBySourceRegex ( ' // At second call of caller_trivial_1 in main. ' , self . main_source_spec )
self . assertTrue ( break_2_in_main , VALID_BREAKPOINT )
threads = lldbutil . continue_to_breakpoint ( self . process , break_2_in_main )
self . assertTrue ( len ( threads ) == 1 , " Successfully ran to call site of second caller_trivial_1 call. " )
self . thread = threads [ 0 ]
step_sequence = [ [ " // In caller_trivial_1. " , " into " ] ,
[ " // In caller_trivial_2. " , " into " ] ,
[ " // In inline_trivial_1. " , " into " ] ]
self . run_step_sequence ( step_sequence )
# Then call some trivial function, and make sure we end up back where we were in the inlined call stack:
frame = self . thread . GetFrameAtIndex ( 0 )
before_line_entry = frame . GetLineEntry ( )
value = frame . EvaluateExpression ( " function_to_call() " )
after_line_entry = frame . GetLineEntry ( )
self . assertTrue ( before_line_entry . GetLine ( ) == after_line_entry . GetLine ( ) , " Line entry before and after function calls are the same. " )
# Now make sure stepping OVER in the middle of the stack works, and then check finish from the inlined frame:
step_sequence = [ [ " // At increment in inline_trivial_1. " , " over " ] ,
[ " // At increment in caller_trivial_2. " , " out " ] ]
self . run_step_sequence ( step_sequence )
# Now run to the place in main just before the first call to caller_ref_1:
break_3_in_main = target . BreakpointCreateBySourceRegex ( ' // At first call of caller_ref_1 in main. ' , self . main_source_spec )
self . assertTrue ( break_3_in_main , VALID_BREAKPOINT )
threads = lldbutil . continue_to_breakpoint ( self . process , break_3_in_main )
self . assertTrue ( len ( threads ) == 1 , " Successfully ran to call site of first caller_ref_1 call. " )
self . thread = threads [ 0 ]
step_sequence = [ [ " // In caller_ref_1. " , " into " ] ,
[ " // In caller_ref_2. " , " into " ] ,
[ " // In inline_ref_1. " , " into " ] ,
[ " // In inline_ref_2. " , " into " ] ,
[ " // In called_by_inline_ref. " , " into " ] ,
[ " // In inline_ref_2. " , " out " ] ,
[ " // In inline_ref_1. " , " out " ] ,
[ " // At increment in inline_ref_1. " , " over " ] ,
[ " // In caller_ref_2. " , " out " ] ,
[ " // At increment in caller_ref_2. " , " over " ] ]
self . run_step_sequence ( step_sequence )
def inline_stepping_step_over ( self ) :
""" Use Python APIs to test stepping over and hitting breakpoints. """
exe = os . path . join ( os . getcwd ( ) , " a.out " )
target = self . dbg . CreateTarget ( exe )
self . assertTrue ( target , VALID_TARGET )
break_1_in_main = target . BreakpointCreateBySourceRegex ( ' // At second call of caller_ref_1 in main. ' , self . main_source_spec )
self . assertTrue ( break_1_in_main , VALID_BREAKPOINT )
# Now launch the process, and do not stop at entry point.
2013-12-13 19:18:59 +00:00
self . process = target . LaunchSimple ( None , None , self . get_process_working_directory ( ) )
2012-09-07 23:58:16 +00:00
self . assertTrue ( self . process , PROCESS_IS_VALID )
# The stop reason of the thread should be breakpoint.
threads = lldbutil . get_threads_stopped_at_breakpoint ( self . process , break_1_in_main )
if len ( threads ) != 1 :
self . fail ( " Failed to stop at first breakpoint in main. " )
self . thread = threads [ 0 ]
step_sequence = [ [ " // In caller_ref_1. " , " into " ] ,
[ " // In caller_ref_2. " , " into " ] ,
[ " // At increment in caller_ref_2. " , " over " ] ]
self . run_step_sequence ( step_sequence )
2015-07-24 08:54:22 +00:00
def step_in_template ( self ) :
""" Use Python APIs to test stepping in to templated functions. """
exe = os . path . join ( os . getcwd ( ) , " a.out " )
target = self . dbg . CreateTarget ( exe )
self . assertTrue ( target , VALID_TARGET )
break_1_in_main = target . BreakpointCreateBySourceRegex ( ' // Call max_value template ' , self . main_source_spec )
self . assertTrue ( break_1_in_main , VALID_BREAKPOINT )
break_2_in_main = target . BreakpointCreateBySourceRegex ( ' // Call max_value specialized ' , self . main_source_spec )
self . assertTrue ( break_2_in_main , VALID_BREAKPOINT )
# Now launch the process, and do not stop at entry point.
self . process = target . LaunchSimple ( None , None , self . get_process_working_directory ( ) )
self . assertTrue ( self . process , PROCESS_IS_VALID )
# The stop reason of the thread should be breakpoint.
threads = lldbutil . get_threads_stopped_at_breakpoint ( self . process , break_1_in_main )
if len ( threads ) != 1 :
self . fail ( " Failed to stop at first breakpoint in main. " )
self . thread = threads [ 0 ]
step_sequence = [ [ " // In max_value template " , " into " ] ]
self . run_step_sequence ( step_sequence )
2012-09-07 23:58:16 +00:00
2015-07-24 08:54:22 +00:00
threads = lldbutil . continue_to_breakpoint ( self . process , break_2_in_main )
self . assertEqual ( len ( threads ) , 1 , " Successfully ran to call site of second caller_trivial_1 call. " )
self . thread = threads [ 0 ]
2012-09-07 23:58:16 +00:00
2015-07-24 08:54:22 +00:00
step_sequence = [ [ " // In max_value specialized " , " into " ] ]
self . run_step_sequence ( step_sequence )