2015-06-12 19:49:23 +00:00
package proc
2014-12-08 23:40:59 +00:00
import (
2016-03-18 08:32:17 +00:00
"errors"
2017-09-01 13:30:45 +00:00
"fmt"
2016-04-11 11:50:01 +00:00
"go/ast"
2017-05-16 18:23:33 +00:00
"go/token"
2015-04-19 22:11:33 +00:00
"path/filepath"
2016-01-24 09:25:54 +00:00
"reflect"
2016-07-26 13:34:31 +00:00
"strings"
2015-07-08 20:02:43 +00:00
2019-01-04 18:39:25 +00:00
"github.com/go-delve/delve/pkg/dwarf/godwarf"
"github.com/go-delve/delve/pkg/dwarf/reader"
2014-12-08 23:40:59 +00:00
)
2017-04-21 07:50:38 +00:00
// Thread represents a thread.
type Thread interface {
2017-04-21 06:55:53 +00:00
MemoryReadWriter
2017-02-15 13:41:03 +00:00
Location ( ) ( * Location , error )
// Breakpoint will return the breakpoint that this thread is stopped at or
// nil if the thread is not stopped at any breakpoint.
2020-01-21 20:41:24 +00:00
Breakpoint ( ) * BreakpointState
2017-02-15 13:41:03 +00:00
ThreadID ( ) int
2018-08-03 15:17:01 +00:00
// Registers returns the CPU registers of this thread. The contents of the
// variable returned may or may not change to reflect the new CPU status
// when the thread is resumed or the registers are changed by calling
// SetPC/SetSP/etc.
// To insure that the the returned variable won't change call the Copy
// method of Registers.
2017-02-15 13:41:03 +00:00
Registers ( floatingPoint bool ) ( Registers , error )
2018-08-03 15:17:01 +00:00
2018-05-04 17:31:45 +00:00
// RestoreRegisters restores saved registers
2018-08-03 15:17:01 +00:00
RestoreRegisters ( Registers ) error
2017-02-15 13:41:03 +00:00
Arch ( ) Arch
BinInfo ( ) * BinaryInfo
2017-02-22 08:35:21 +00:00
StepInstruction ( ) error
2017-04-21 06:55:53 +00:00
// Blocked returns true if the thread is blocked
Blocked ( ) bool
2019-08-12 22:11:19 +00:00
// SetCurrentBreakpoint updates the current breakpoint of this thread, if adjustPC is true also checks for breakpoints that were just hit (this should only be passed true after a thread resume)
SetCurrentBreakpoint ( adjustPC bool ) error
2018-05-11 12:51:15 +00:00
// Common returns the CommonThread structure for this thread
Common ( ) * CommonThread
2018-05-04 17:31:45 +00:00
SetPC ( uint64 ) error
SetSP ( uint64 ) error
2018-07-31 16:32:30 +00:00
SetDX ( uint64 ) error
2017-02-15 13:41:03 +00:00
}
2016-01-10 08:57:52 +00:00
// Location represents the location of a thread.
2015-06-18 03:01:31 +00:00
// Holds information on the current instruction
// address, the source file:line, and the function.
2015-05-27 17:16:45 +00:00
type Location struct {
PC uint64
File string
Line int
2017-09-01 13:30:45 +00:00
Fn * Function
2015-05-27 17:16:45 +00:00
}
2018-08-31 18:08:18 +00:00
// ErrThreadBlocked is returned when the thread
2016-01-10 08:57:52 +00:00
// is blocked in the scheduler.
2018-08-31 18:08:18 +00:00
type ErrThreadBlocked struct { }
2015-08-20 14:28:11 +00:00
2018-08-31 18:08:18 +00:00
func ( tbe ErrThreadBlocked ) Error ( ) string {
2018-07-02 09:37:52 +00:00
return "thread blocked"
2015-08-20 14:28:11 +00:00
}
2018-05-11 12:51:15 +00:00
// CommonThread contains fields used by this package, common to all
// implementations of the Thread interface.
type CommonThread struct {
returnValues [ ] * Variable
2020-03-10 19:48:46 +00:00
g * G // cached g for this thread
2018-05-11 12:51:15 +00:00
}
2018-08-31 18:08:18 +00:00
// ReturnValues reads the return values from the function executing on
// this thread using the provided LoadConfig.
2018-05-11 12:51:15 +00:00
func ( t * CommonThread ) ReturnValues ( cfg LoadConfig ) [ ] * Variable {
loadValues ( t . returnValues , cfg )
return t . returnValues
}
2017-05-16 18:23:33 +00:00
// topframe returns the two topmost frames of g, or thread if g is nil.
func topframe ( g * G , thread Thread ) ( Stackframe , Stackframe , error ) {
2016-04-11 11:50:01 +00:00
var frames [ ] Stackframe
2016-04-13 13:25:23 +00:00
var err error
2016-04-11 11:50:01 +00:00
2016-07-03 07:02:21 +00:00
if g == nil {
2017-04-21 06:55:53 +00:00
if thread . Blocked ( ) {
2018-08-31 18:08:18 +00:00
return Stackframe { } , Stackframe { } , ErrThreadBlocked { }
2016-04-11 11:50:01 +00:00
}
2017-05-16 18:23:33 +00:00
frames , err = ThreadStacktrace ( thread , 1 )
2016-04-11 11:50:01 +00:00
} else {
2019-09-25 17:21:20 +00:00
frames , err = g . Stacktrace ( 1 , StacktraceReadDefers )
2015-08-20 14:28:11 +00:00
}
2014-12-08 23:40:59 +00:00
if err != nil {
2017-05-16 18:23:33 +00:00
return Stackframe { } , Stackframe { } , err
2014-12-08 23:40:59 +00:00
}
2017-05-16 18:23:33 +00:00
switch len ( frames ) {
case 0 :
return Stackframe { } , Stackframe { } , errors . New ( "empty stack trace" )
case 1 :
return frames [ 0 ] , Stackframe { } , nil
default :
return frames [ 0 ] , frames [ 1 ] , nil
2016-04-13 13:25:23 +00:00
}
}
2018-08-31 18:08:18 +00:00
// ErrNoSourceForPC is returned when the given address
// does not correspond with a source file location.
type ErrNoSourceForPC struct {
2017-10-05 07:26:19 +00:00
pc uint64
}
2018-08-31 18:08:18 +00:00
func ( err * ErrNoSourceForPC ) Error ( ) string {
return fmt . Sprintf ( "no source for PC %#x" , err . pc )
2017-10-05 07:26:19 +00:00
}
2016-07-26 13:34:31 +00:00
// Set breakpoints at every line, and the return address. Also look for
// a deferred function and set a breakpoint there too.
// If stepInto is true it will also set breakpoints inside all
// functions called on the current source line, for non-absolute CALLs
// a breakpoint of kind StepBreakpoint is set on the CALL instruction,
// Continue will take care of setting a breakpoint to the destination
// once the CALL is reached.
2017-05-16 18:23:33 +00:00
//
// Regardless of stepInto the following breakpoints will be set:
// - a breakpoint on the first deferred function with NextDeferBreakpoint
// kind, the list of all the addresses to deferreturn calls in this function
// and condition checking that we remain on the same goroutine
// - a breakpoint on each line of the function, with a condition checking
// that we stay on the same stack frame and goroutine.
// - a breakpoint on the return address of the function, with a condition
// checking that we move to the previous stack frame and stay on the same
// goroutine.
proc: support inlining
Go 1.10 added inlined calls to debug_info, this commit adds support
for DW_TAG_inlined_call to delve, both for stack traces (where
inlined calls will appear as normal stack frames) and to correct
the behavior of next, step and stepout.
The calls to Next and Frame of stackIterator continue to work
unchanged and only return real stack frames, after reading each line
appendInlinedCalls is called to unpacked all the inlined calls that
involve the current PC.
The fake stack frames produced by appendInlinedCalls are
distinguished from real stack frames by having the Inlined attribute
set to true. Also their Current and Call locations are treated
differently. The Call location will be changed to represent the
position inside the inlined call, while the Current location will
always reference the real stack frame. This is done because:
* next, step and stepout need to access the debug_info entry of
the real function they are stepping through
* we are already manipulating Call in different ways while Current
is just what we read from the call stack
The strategy remains mostly the same, we disassemble the function
and we set a breakpoint on each instruction corresponding to a
different file:line. The function in question will be the one
corresponding to the first real (i.e. non-inlined) stack frame.
* If the current function contains inlined calls, 'next' will not
set any breakpoints on instructions that belong to inlined calls. We
do not do this for 'step'.
* If we are inside an inlined call that makes other inlined
functions, 'next' will not set any breakpoints that belong to
inlined calls that are children of the current inlined call.
* If the current function is inlined the breakpoint on the return
address won't be set, because inlined frames don't have a return
address.
* The code we use for stepout doesn't work at all if we are inside
an inlined call, instead we call 'next' but instruct it to remove
all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
//
// The breakpoint on the return address is *not* set if the current frame is
// an inlined call. For inlined calls topframe.Current.Fn is the function
// where the inlining happened and the second set of breakpoints will also
// cover the "return address".
//
// If inlinedStepOut is true this function implements the StepOut operation
// for an inlined function call. Everything works the same as normal except
// when removing instructions belonging to inlined calls we also remove all
// instructions belonging to the current inlined call.
2020-03-10 19:27:38 +00:00
func next ( dbp * Target , stepInto , inlinedStepOut bool ) error {
proc,terminal: Implement reverse step, next and stepout (#1785)
* proc: move defer breakpoint code into a function
Moves the code that sets a breakpoint on the first deferred function,
used by both next and StepOut, to its function.
* proc: implement reverse step/next/stepout
When the direction of execution is reversed (on a recording) Step, Next and
StepOut will behave similarly to their forward version. However there are
some subtle interactions between their behavior, prologue skipping, deferred
calls and normal calls. Specifically:
- when stepping backwards we need to set a breakpoint on the first
instruction after each CALL instruction, once this breakpoint is reached we
need to execute a single StepInstruction operation to reverse step into the
CALL.
- to insure that the prologue is skipped reverse next needs to check if it
is on the first instruction after the prologue, and if it is behave like
reverse stepout.
- there is no reason to set breakpoints on deferred calls when reverse
nexting or reverse stepping out, they will never be hit.
- reverse step out should generally place its breakpoint on the CALL
instruction that created the current stack frame (which will be the CALL
instruction immediately preceding the instruction at the return address).
- reverse step out needs to treat panic calls and deferreturn calls
specially.
* service,terminal: implement reverse step, next, stepout
2020-03-11 22:40:41 +00:00
backward := dbp . GetDirection ( ) == Backward
2017-02-22 08:35:21 +00:00
selg := dbp . SelectedGoroutine ( )
curthread := dbp . CurrentThread ( )
2017-05-16 18:23:33 +00:00
topframe , retframe , err := topframe ( selg , curthread )
2016-04-13 13:25:23 +00:00
if err != nil {
return err
2016-04-11 11:50:01 +00:00
}
2014-12-08 23:40:59 +00:00
2017-09-01 13:30:45 +00:00
if topframe . Current . Fn == nil {
2018-08-31 18:08:18 +00:00
return & ErrNoSourceForPC { topframe . Current . PC }
2017-09-01 13:30:45 +00:00
}
proc,terminal: Implement reverse step, next and stepout (#1785)
* proc: move defer breakpoint code into a function
Moves the code that sets a breakpoint on the first deferred function,
used by both next and StepOut, to its function.
* proc: implement reverse step/next/stepout
When the direction of execution is reversed (on a recording) Step, Next and
StepOut will behave similarly to their forward version. However there are
some subtle interactions between their behavior, prologue skipping, deferred
calls and normal calls. Specifically:
- when stepping backwards we need to set a breakpoint on the first
instruction after each CALL instruction, once this breakpoint is reached we
need to execute a single StepInstruction operation to reverse step into the
CALL.
- to insure that the prologue is skipped reverse next needs to check if it
is on the first instruction after the prologue, and if it is behave like
reverse stepout.
- there is no reason to set breakpoints on deferred calls when reverse
nexting or reverse stepping out, they will never be hit.
- reverse step out should generally place its breakpoint on the CALL
instruction that created the current stack frame (which will be the CALL
instruction immediately preceding the instruction at the return address).
- reverse step out needs to treat panic calls and deferreturn calls
specially.
* service,terminal: implement reverse step, next, stepout
2020-03-11 22:40:41 +00:00
if backward && retframe . Current . Fn == nil {
return & ErrNoSourceForPC { retframe . Current . PC }
}
proc: support inlining
Go 1.10 added inlined calls to debug_info, this commit adds support
for DW_TAG_inlined_call to delve, both for stack traces (where
inlined calls will appear as normal stack frames) and to correct
the behavior of next, step and stepout.
The calls to Next and Frame of stackIterator continue to work
unchanged and only return real stack frames, after reading each line
appendInlinedCalls is called to unpacked all the inlined calls that
involve the current PC.
The fake stack frames produced by appendInlinedCalls are
distinguished from real stack frames by having the Inlined attribute
set to true. Also their Current and Call locations are treated
differently. The Call location will be changed to represent the
position inside the inlined call, while the Current location will
always reference the real stack frame. This is done because:
* next, step and stepout need to access the debug_info entry of
the real function they are stepping through
* we are already manipulating Call in different ways while Current
is just what we read from the call stack
The strategy remains mostly the same, we disassemble the function
and we set a breakpoint on each instruction corresponding to a
different file:line. The function in question will be the one
corresponding to the first real (i.e. non-inlined) stack frame.
* If the current function contains inlined calls, 'next' will not
set any breakpoints on instructions that belong to inlined calls. We
do not do this for 'step'.
* If we are inside an inlined call that makes other inlined
functions, 'next' will not set any breakpoints that belong to
inlined calls that are children of the current inlined call.
* If the current function is inlined the breakpoint on the return
address won't be set, because inlined frames don't have a return
address.
* The code we use for stepout doesn't work at all if we are inside
an inlined call, instead we call 'next' but instruct it to remove
all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
// sanity check
if inlinedStepOut && ! topframe . Inlined {
panic ( "next called with inlinedStepOut but topframe was not inlined" )
}
2017-01-09 23:21:54 +00:00
success := false
defer func ( ) {
if ! success {
dbp . ClearInternalBreakpoints ( )
}
} ( )
2017-06-23 15:20:40 +00:00
ext := filepath . Ext ( topframe . Current . File )
csource := ext != ".go" && ext != ".s"
2017-04-21 06:55:53 +00:00
var thread MemoryReadWriter = curthread
2017-04-13 23:19:57 +00:00
var regs Registers
2017-04-21 06:55:53 +00:00
if selg != nil && selg . Thread != nil {
thread = selg . Thread
regs , err = selg . Thread . Registers ( false )
2017-04-13 23:19:57 +00:00
if err != nil {
return err
}
2014-12-08 23:40:59 +00:00
}
2015-04-19 22:11:33 +00:00
proc,terminal: Implement reverse step, next and stepout (#1785)
* proc: move defer breakpoint code into a function
Moves the code that sets a breakpoint on the first deferred function,
used by both next and StepOut, to its function.
* proc: implement reverse step/next/stepout
When the direction of execution is reversed (on a recording) Step, Next and
StepOut will behave similarly to their forward version. However there are
some subtle interactions between their behavior, prologue skipping, deferred
calls and normal calls. Specifically:
- when stepping backwards we need to set a breakpoint on the first
instruction after each CALL instruction, once this breakpoint is reached we
need to execute a single StepInstruction operation to reverse step into the
CALL.
- to insure that the prologue is skipped reverse next needs to check if it
is on the first instruction after the prologue, and if it is behave like
reverse stepout.
- there is no reason to set breakpoints on deferred calls when reverse
nexting or reverse stepping out, they will never be hit.
- reverse step out should generally place its breakpoint on the CALL
instruction that created the current stack frame (which will be the CALL
instruction immediately preceding the instruction at the return address).
- reverse step out needs to treat panic calls and deferreturn calls
specially.
* service,terminal: implement reverse step, next, stepout
2020-03-11 22:40:41 +00:00
sameGCond := SameGoroutineCondition ( selg )
var firstPCAfterPrologue uint64
if backward {
firstPCAfterPrologue , err = FirstPCAfterPrologue ( dbp , topframe . Current . Fn , false )
if err != nil {
return err
}
if firstPCAfterPrologue == topframe . Current . PC {
// We don't want to step into the prologue so we just execute a reverse step out instead
if err := stepOutReverse ( dbp , topframe , retframe , sameGCond ) ; err != nil {
return err
}
success = true
return nil
}
topframe . Ret , err = findCallInstrForRet ( dbp , thread , topframe . Ret , retframe . Current . Fn )
if err != nil {
return err
}
}
2017-11-06 17:34:59 +00:00
text , err := disassemble ( thread , regs , dbp . Breakpoints ( ) , dbp . BinInfo ( ) , topframe . Current . Fn . Entry , topframe . Current . Fn . End , false )
2016-07-26 13:34:31 +00:00
if err != nil && stepInto {
return err
}
2015-04-19 22:11:33 +00:00
2017-09-01 13:34:13 +00:00
retFrameCond := andFrameoffCondition ( sameGCond , retframe . FrameOffset ( ) )
sameFrameCond := andFrameoffCondition ( sameGCond , topframe . FrameOffset ( ) )
2017-05-16 18:23:33 +00:00
var sameOrRetFrameCond ast . Expr
if sameGCond != nil {
proc: support inlining
Go 1.10 added inlined calls to debug_info, this commit adds support
for DW_TAG_inlined_call to delve, both for stack traces (where
inlined calls will appear as normal stack frames) and to correct
the behavior of next, step and stepout.
The calls to Next and Frame of stackIterator continue to work
unchanged and only return real stack frames, after reading each line
appendInlinedCalls is called to unpacked all the inlined calls that
involve the current PC.
The fake stack frames produced by appendInlinedCalls are
distinguished from real stack frames by having the Inlined attribute
set to true. Also their Current and Call locations are treated
differently. The Call location will be changed to represent the
position inside the inlined call, while the Current location will
always reference the real stack frame. This is done because:
* next, step and stepout need to access the debug_info entry of
the real function they are stepping through
* we are already manipulating Call in different ways while Current
is just what we read from the call stack
The strategy remains mostly the same, we disassemble the function
and we set a breakpoint on each instruction corresponding to a
different file:line. The function in question will be the one
corresponding to the first real (i.e. non-inlined) stack frame.
* If the current function contains inlined calls, 'next' will not
set any breakpoints on instructions that belong to inlined calls. We
do not do this for 'step'.
* If we are inside an inlined call that makes other inlined
functions, 'next' will not set any breakpoints that belong to
inlined calls that are children of the current inlined call.
* If the current function is inlined the breakpoint on the return
address won't be set, because inlined frames don't have a return
address.
* The code we use for stepout doesn't work at all if we are inside
an inlined call, instead we call 'next' but instruct it to remove
all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
if topframe . Inlined {
sameOrRetFrameCond = sameFrameCond
} else {
sameOrRetFrameCond = & ast . BinaryExpr {
Op : token . LAND ,
X : sameGCond ,
Y : & ast . BinaryExpr {
Op : token . LOR ,
X : frameoffCondition ( topframe . FrameOffset ( ) ) ,
Y : frameoffCondition ( retframe . FrameOffset ( ) ) ,
} ,
}
2017-05-16 18:23:33 +00:00
}
}
2016-07-14 07:43:39 +00:00
proc,terminal: Implement reverse step, next and stepout (#1785)
* proc: move defer breakpoint code into a function
Moves the code that sets a breakpoint on the first deferred function,
used by both next and StepOut, to its function.
* proc: implement reverse step/next/stepout
When the direction of execution is reversed (on a recording) Step, Next and
StepOut will behave similarly to their forward version. However there are
some subtle interactions between their behavior, prologue skipping, deferred
calls and normal calls. Specifically:
- when stepping backwards we need to set a breakpoint on the first
instruction after each CALL instruction, once this breakpoint is reached we
need to execute a single StepInstruction operation to reverse step into the
CALL.
- to insure that the prologue is skipped reverse next needs to check if it
is on the first instruction after the prologue, and if it is behave like
reverse stepout.
- there is no reason to set breakpoints on deferred calls when reverse
nexting or reverse stepping out, they will never be hit.
- reverse step out should generally place its breakpoint on the CALL
instruction that created the current stack frame (which will be the CALL
instruction immediately preceding the instruction at the return address).
- reverse step out needs to treat panic calls and deferreturn calls
specially.
* service,terminal: implement reverse step, next, stepout
2020-03-11 22:40:41 +00:00
if stepInto && ! backward {
err := setStepIntoBreakpoints ( dbp , text , topframe , sameGCond )
if err != nil {
return err
2016-07-14 07:43:39 +00:00
}
}
2016-04-11 11:50:01 +00:00
proc,terminal: Implement reverse step, next and stepout (#1785)
* proc: move defer breakpoint code into a function
Moves the code that sets a breakpoint on the first deferred function,
used by both next and StepOut, to its function.
* proc: implement reverse step/next/stepout
When the direction of execution is reversed (on a recording) Step, Next and
StepOut will behave similarly to their forward version. However there are
some subtle interactions between their behavior, prologue skipping, deferred
calls and normal calls. Specifically:
- when stepping backwards we need to set a breakpoint on the first
instruction after each CALL instruction, once this breakpoint is reached we
need to execute a single StepInstruction operation to reverse step into the
CALL.
- to insure that the prologue is skipped reverse next needs to check if it
is on the first instruction after the prologue, and if it is behave like
reverse stepout.
- there is no reason to set breakpoints on deferred calls when reverse
nexting or reverse stepping out, they will never be hit.
- reverse step out should generally place its breakpoint on the CALL
instruction that created the current stack frame (which will be the CALL
instruction immediately preceding the instruction at the return address).
- reverse step out needs to treat panic calls and deferreturn calls
specially.
* service,terminal: implement reverse step, next, stepout
2020-03-11 22:40:41 +00:00
if ! backward {
_ , err = setDeferBreakpoint ( dbp , text , topframe , sameGCond , stepInto )
if err != nil {
return err
2016-07-26 13:34:31 +00:00
}
2016-07-14 07:43:39 +00:00
}
// Add breakpoints on all the lines in the current function
2018-03-25 08:04:32 +00:00
pcs , err := topframe . Current . Fn . cu . lineInfo . AllPCsBetween ( topframe . Current . Fn . Entry , topframe . Current . Fn . End - 1 , topframe . Current . File , topframe . Current . Line )
2017-01-09 23:21:54 +00:00
if err != nil {
return err
}
2014-12-08 23:40:59 +00:00
proc,terminal: Implement reverse step, next and stepout (#1785)
* proc: move defer breakpoint code into a function
Moves the code that sets a breakpoint on the first deferred function,
used by both next and StepOut, to its function.
* proc: implement reverse step/next/stepout
When the direction of execution is reversed (on a recording) Step, Next and
StepOut will behave similarly to their forward version. However there are
some subtle interactions between their behavior, prologue skipping, deferred
calls and normal calls. Specifically:
- when stepping backwards we need to set a breakpoint on the first
instruction after each CALL instruction, once this breakpoint is reached we
need to execute a single StepInstruction operation to reverse step into the
CALL.
- to insure that the prologue is skipped reverse next needs to check if it
is on the first instruction after the prologue, and if it is behave like
reverse stepout.
- there is no reason to set breakpoints on deferred calls when reverse
nexting or reverse stepping out, they will never be hit.
- reverse step out should generally place its breakpoint on the CALL
instruction that created the current stack frame (which will be the CALL
instruction immediately preceding the instruction at the return address).
- reverse step out needs to treat panic calls and deferreturn calls
specially.
* service,terminal: implement reverse step, next, stepout
2020-03-11 22:40:41 +00:00
if backward {
// Ensure that pcs contains firstPCAfterPrologue when reverse stepping.
found := false
for _ , pc := range pcs {
if pc == firstPCAfterPrologue {
found = true
break
}
}
if ! found {
pcs = append ( pcs , firstPCAfterPrologue )
}
}
proc: support inlining
Go 1.10 added inlined calls to debug_info, this commit adds support
for DW_TAG_inlined_call to delve, both for stack traces (where
inlined calls will appear as normal stack frames) and to correct
the behavior of next, step and stepout.
The calls to Next and Frame of stackIterator continue to work
unchanged and only return real stack frames, after reading each line
appendInlinedCalls is called to unpacked all the inlined calls that
involve the current PC.
The fake stack frames produced by appendInlinedCalls are
distinguished from real stack frames by having the Inlined attribute
set to true. Also their Current and Call locations are treated
differently. The Call location will be changed to represent the
position inside the inlined call, while the Current location will
always reference the real stack frame. This is done because:
* next, step and stepout need to access the debug_info entry of
the real function they are stepping through
* we are already manipulating Call in different ways while Current
is just what we read from the call stack
The strategy remains mostly the same, we disassemble the function
and we set a breakpoint on each instruction corresponding to a
different file:line. The function in question will be the one
corresponding to the first real (i.e. non-inlined) stack frame.
* If the current function contains inlined calls, 'next' will not
set any breakpoints on instructions that belong to inlined calls. We
do not do this for 'step'.
* If we are inside an inlined call that makes other inlined
functions, 'next' will not set any breakpoints that belong to
inlined calls that are children of the current inlined call.
* If the current function is inlined the breakpoint on the return
address won't be set, because inlined frames don't have a return
address.
* The code we use for stepout doesn't work at all if we are inside
an inlined call, instead we call 'next' but instruct it to remove
all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
if ! stepInto {
// Removing any PC range belonging to an inlined call
frame := topframe
if inlinedStepOut {
frame = retframe
}
pcs , err = removeInlinedCalls ( dbp , pcs , frame )
if err != nil {
return err
}
}
2016-07-26 13:34:31 +00:00
if ! csource {
var covered bool
for i := range pcs {
2017-09-01 13:30:45 +00:00
if topframe . Current . Fn . Entry <= pcs [ i ] && pcs [ i ] < topframe . Current . Fn . End {
2016-07-26 13:34:31 +00:00
covered = true
break
}
2015-03-28 01:12:07 +00:00
}
2015-04-19 22:11:33 +00:00
2016-07-26 13:34:31 +00:00
if ! covered {
2017-09-01 13:30:45 +00:00
fn := dbp . BinInfo ( ) . PCToFunc ( topframe . Ret )
2017-02-22 08:35:21 +00:00
if selg != nil && fn != nil && fn . Name == "runtime.goexit" {
2016-07-26 13:34:31 +00:00
return nil
}
2015-03-28 01:12:07 +00:00
}
2015-01-10 23:33:06 +00:00
}
2016-07-14 07:43:39 +00:00
2017-05-16 18:23:33 +00:00
for _ , pc := range pcs {
proc,terminal: Implement reverse step, next and stepout (#1785)
* proc: move defer breakpoint code into a function
Moves the code that sets a breakpoint on the first deferred function,
used by both next and StepOut, to its function.
* proc: implement reverse step/next/stepout
When the direction of execution is reversed (on a recording) Step, Next and
StepOut will behave similarly to their forward version. However there are
some subtle interactions between their behavior, prologue skipping, deferred
calls and normal calls. Specifically:
- when stepping backwards we need to set a breakpoint on the first
instruction after each CALL instruction, once this breakpoint is reached we
need to execute a single StepInstruction operation to reverse step into the
CALL.
- to insure that the prologue is skipped reverse next needs to check if it
is on the first instruction after the prologue, and if it is behave like
reverse stepout.
- there is no reason to set breakpoints on deferred calls when reverse
nexting or reverse stepping out, they will never be hit.
- reverse step out should generally place its breakpoint on the CALL
instruction that created the current stack frame (which will be the CALL
instruction immediately preceding the instruction at the return address).
- reverse step out needs to treat panic calls and deferreturn calls
specially.
* service,terminal: implement reverse step, next, stepout
2020-03-11 22:40:41 +00:00
if _ , err := allowDuplicateBreakpoint ( dbp . SetBreakpoint ( pc , NextBreakpoint , sameFrameCond ) ) ; err != nil {
dbp . ClearInternalBreakpoints ( )
return err
2017-05-16 18:23:33 +00:00
}
proc,terminal: Implement reverse step, next and stepout (#1785)
* proc: move defer breakpoint code into a function
Moves the code that sets a breakpoint on the first deferred function,
used by both next and StepOut, to its function.
* proc: implement reverse step/next/stepout
When the direction of execution is reversed (on a recording) Step, Next and
StepOut will behave similarly to their forward version. However there are
some subtle interactions between their behavior, prologue skipping, deferred
calls and normal calls. Specifically:
- when stepping backwards we need to set a breakpoint on the first
instruction after each CALL instruction, once this breakpoint is reached we
need to execute a single StepInstruction operation to reverse step into the
CALL.
- to insure that the prologue is skipped reverse next needs to check if it
is on the first instruction after the prologue, and if it is behave like
reverse stepout.
- there is no reason to set breakpoints on deferred calls when reverse
nexting or reverse stepping out, they will never be hit.
- reverse step out should generally place its breakpoint on the CALL
instruction that created the current stack frame (which will be the CALL
instruction immediately preceding the instruction at the return address).
- reverse step out needs to treat panic calls and deferreturn calls
specially.
* service,terminal: implement reverse step, next, stepout
2020-03-11 22:40:41 +00:00
}
2017-05-16 18:23:33 +00:00
proc,terminal: Implement reverse step, next and stepout (#1785)
* proc: move defer breakpoint code into a function
Moves the code that sets a breakpoint on the first deferred function,
used by both next and StepOut, to its function.
* proc: implement reverse step/next/stepout
When the direction of execution is reversed (on a recording) Step, Next and
StepOut will behave similarly to their forward version. However there are
some subtle interactions between their behavior, prologue skipping, deferred
calls and normal calls. Specifically:
- when stepping backwards we need to set a breakpoint on the first
instruction after each CALL instruction, once this breakpoint is reached we
need to execute a single StepInstruction operation to reverse step into the
CALL.
- to insure that the prologue is skipped reverse next needs to check if it
is on the first instruction after the prologue, and if it is behave like
reverse stepout.
- there is no reason to set breakpoints on deferred calls when reverse
nexting or reverse stepping out, they will never be hit.
- reverse step out should generally place its breakpoint on the CALL
instruction that created the current stack frame (which will be the CALL
instruction immediately preceding the instruction at the return address).
- reverse step out needs to treat panic calls and deferreturn calls
specially.
* service,terminal: implement reverse step, next, stepout
2020-03-11 22:40:41 +00:00
if stepInto && backward {
err := setStepIntoBreakpointsReverse ( dbp , text , topframe , sameGCond )
if err != nil {
return err
}
2017-05-16 18:23:33 +00:00
}
proc,terminal: Implement reverse step, next and stepout (#1785)
* proc: move defer breakpoint code into a function
Moves the code that sets a breakpoint on the first deferred function,
used by both next and StepOut, to its function.
* proc: implement reverse step/next/stepout
When the direction of execution is reversed (on a recording) Step, Next and
StepOut will behave similarly to their forward version. However there are
some subtle interactions between their behavior, prologue skipping, deferred
calls and normal calls. Specifically:
- when stepping backwards we need to set a breakpoint on the first
instruction after each CALL instruction, once this breakpoint is reached we
need to execute a single StepInstruction operation to reverse step into the
CALL.
- to insure that the prologue is skipped reverse next needs to check if it
is on the first instruction after the prologue, and if it is behave like
reverse stepout.
- there is no reason to set breakpoints on deferred calls when reverse
nexting or reverse stepping out, they will never be hit.
- reverse step out should generally place its breakpoint on the CALL
instruction that created the current stack frame (which will be the CALL
instruction immediately preceding the instruction at the return address).
- reverse step out needs to treat panic calls and deferreturn calls
specially.
* service,terminal: implement reverse step, next, stepout
2020-03-11 22:40:41 +00:00
proc: support inlining
Go 1.10 added inlined calls to debug_info, this commit adds support
for DW_TAG_inlined_call to delve, both for stack traces (where
inlined calls will appear as normal stack frames) and to correct
the behavior of next, step and stepout.
The calls to Next and Frame of stackIterator continue to work
unchanged and only return real stack frames, after reading each line
appendInlinedCalls is called to unpacked all the inlined calls that
involve the current PC.
The fake stack frames produced by appendInlinedCalls are
distinguished from real stack frames by having the Inlined attribute
set to true. Also their Current and Call locations are treated
differently. The Call location will be changed to represent the
position inside the inlined call, while the Current location will
always reference the real stack frame. This is done because:
* next, step and stepout need to access the debug_info entry of
the real function they are stepping through
* we are already manipulating Call in different ways while Current
is just what we read from the call stack
The strategy remains mostly the same, we disassemble the function
and we set a breakpoint on each instruction corresponding to a
different file:line. The function in question will be the one
corresponding to the first real (i.e. non-inlined) stack frame.
* If the current function contains inlined calls, 'next' will not
set any breakpoints on instructions that belong to inlined calls. We
do not do this for 'step'.
* If we are inside an inlined call that makes other inlined
functions, 'next' will not set any breakpoints that belong to
inlined calls that are children of the current inlined call.
* If the current function is inlined the breakpoint on the return
address won't be set, because inlined frames don't have a return
address.
* The code we use for stepout doesn't work at all if we are inside
an inlined call, instead we call 'next' but instruct it to remove
all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
if ! topframe . Inlined {
// Add a breakpoint on the return address for the current frame.
// For inlined functions there is no need to do this, the set of PCs
// returned by the AllPCsBetween call above already cover all instructions
// of the containing function.
2018-05-11 12:51:15 +00:00
bp , err := dbp . SetBreakpoint ( topframe . Ret , NextBreakpoint , retFrameCond )
if err != nil {
proc: support inlining
Go 1.10 added inlined calls to debug_info, this commit adds support
for DW_TAG_inlined_call to delve, both for stack traces (where
inlined calls will appear as normal stack frames) and to correct
the behavior of next, step and stepout.
The calls to Next and Frame of stackIterator continue to work
unchanged and only return real stack frames, after reading each line
appendInlinedCalls is called to unpacked all the inlined calls that
involve the current PC.
The fake stack frames produced by appendInlinedCalls are
distinguished from real stack frames by having the Inlined attribute
set to true. Also their Current and Call locations are treated
differently. The Call location will be changed to represent the
position inside the inlined call, while the Current location will
always reference the real stack frame. This is done because:
* next, step and stepout need to access the debug_info entry of
the real function they are stepping through
* we are already manipulating Call in different ways while Current
is just what we read from the call stack
The strategy remains mostly the same, we disassemble the function
and we set a breakpoint on each instruction corresponding to a
different file:line. The function in question will be the one
corresponding to the first real (i.e. non-inlined) stack frame.
* If the current function contains inlined calls, 'next' will not
set any breakpoints on instructions that belong to inlined calls. We
do not do this for 'step'.
* If we are inside an inlined call that makes other inlined
functions, 'next' will not set any breakpoints that belong to
inlined calls that are children of the current inlined call.
* If the current function is inlined the breakpoint on the return
address won't be set, because inlined frames don't have a return
address.
* The code we use for stepout doesn't work at all if we are inside
an inlined call, instead we call 'next' but instruct it to remove
all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
if _ , isexists := err . ( BreakpointExistsError ) ; isexists {
if bp . Kind == NextBreakpoint {
// If the return address shares the same address with one of the lines
// of the function (because we are stepping through a recursive
// function) then the corresponding breakpoint should be active both on
// this frame and on the return frame.
bp . Cond = sameOrRetFrameCond
}
2017-05-16 18:23:33 +00:00
}
proc: support inlining
Go 1.10 added inlined calls to debug_info, this commit adds support
for DW_TAG_inlined_call to delve, both for stack traces (where
inlined calls will appear as normal stack frames) and to correct
the behavior of next, step and stepout.
The calls to Next and Frame of stackIterator continue to work
unchanged and only return real stack frames, after reading each line
appendInlinedCalls is called to unpacked all the inlined calls that
involve the current PC.
The fake stack frames produced by appendInlinedCalls are
distinguished from real stack frames by having the Inlined attribute
set to true. Also their Current and Call locations are treated
differently. The Call location will be changed to represent the
position inside the inlined call, while the Current location will
always reference the real stack frame. This is done because:
* next, step and stepout need to access the debug_info entry of
the real function they are stepping through
* we are already manipulating Call in different ways while Current
is just what we read from the call stack
The strategy remains mostly the same, we disassemble the function
and we set a breakpoint on each instruction corresponding to a
different file:line. The function in question will be the one
corresponding to the first real (i.e. non-inlined) stack frame.
* If the current function contains inlined calls, 'next' will not
set any breakpoints on instructions that belong to inlined calls. We
do not do this for 'step'.
* If we are inside an inlined call that makes other inlined
functions, 'next' will not set any breakpoints that belong to
inlined calls that are children of the current inlined call.
* If the current function is inlined the breakpoint on the return
address won't be set, because inlined frames don't have a return
address.
* The code we use for stepout doesn't work at all if we are inside
an inlined call, instead we call 'next' but instruct it to remove
all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
// Return address could be wrong, if we are unable to set a breakpoint
// there it's ok.
2017-05-16 18:23:33 +00:00
}
2018-05-11 12:51:15 +00:00
if bp != nil {
configureReturnBreakpoint ( dbp . BinInfo ( ) , bp , & topframe , retFrameCond )
}
2017-05-16 18:23:33 +00:00
}
2017-06-23 11:48:09 +00:00
2017-09-25 06:29:13 +00:00
if bp := curthread . Breakpoint ( ) ; bp . Breakpoint == nil {
2019-08-12 22:11:19 +00:00
curthread . SetCurrentBreakpoint ( false )
2017-05-16 18:23:33 +00:00
}
2017-01-09 23:21:54 +00:00
success = true
2017-05-16 18:23:33 +00:00
return nil
2015-04-20 18:03:22 +00:00
}
proc,terminal: Implement reverse step, next and stepout (#1785)
* proc: move defer breakpoint code into a function
Moves the code that sets a breakpoint on the first deferred function,
used by both next and StepOut, to its function.
* proc: implement reverse step/next/stepout
When the direction of execution is reversed (on a recording) Step, Next and
StepOut will behave similarly to their forward version. However there are
some subtle interactions between their behavior, prologue skipping, deferred
calls and normal calls. Specifically:
- when stepping backwards we need to set a breakpoint on the first
instruction after each CALL instruction, once this breakpoint is reached we
need to execute a single StepInstruction operation to reverse step into the
CALL.
- to insure that the prologue is skipped reverse next needs to check if it
is on the first instruction after the prologue, and if it is behave like
reverse stepout.
- there is no reason to set breakpoints on deferred calls when reverse
nexting or reverse stepping out, they will never be hit.
- reverse step out should generally place its breakpoint on the CALL
instruction that created the current stack frame (which will be the CALL
instruction immediately preceding the instruction at the return address).
- reverse step out needs to treat panic calls and deferreturn calls
specially.
* service,terminal: implement reverse step, next, stepout
2020-03-11 22:40:41 +00:00
func setStepIntoBreakpoints ( dbp Process , text [ ] AsmInstruction , topframe Stackframe , sameGCond ast . Expr ) error {
for _ , instr := range text {
if instr . Loc . File != topframe . Current . File || instr . Loc . Line != topframe . Current . Line || ! instr . IsCall ( ) {
continue
}
if instr . DestLoc != nil {
if err := setStepIntoBreakpoint ( dbp , [ ] AsmInstruction { instr } , sameGCond ) ; err != nil {
return err
}
} else {
// Non-absolute call instruction, set a StepBreakpoint here
if _ , err := allowDuplicateBreakpoint ( dbp . SetBreakpoint ( instr . Loc . PC , StepBreakpoint , sameGCond ) ) ; err != nil {
return err
}
}
}
return nil
}
func setStepIntoBreakpointsReverse ( dbp Process , text [ ] AsmInstruction , topframe Stackframe , sameGCond ast . Expr ) error {
// Set a breakpoint after every CALL instruction
for i , instr := range text {
if instr . Loc . File != topframe . Current . File || ! instr . IsCall ( ) || instr . DestLoc == nil || instr . DestLoc . Fn == nil {
continue
}
if fn := instr . DestLoc . Fn ; strings . HasPrefix ( fn . Name , "runtime." ) && ! isExportedRuntime ( fn . Name ) {
continue
}
if nextIdx := i + 1 ; nextIdx < len ( text ) {
if _ , err := allowDuplicateBreakpoint ( dbp . SetBreakpoint ( text [ nextIdx ] . Loc . PC , StepBreakpoint , sameGCond ) ) ; err != nil {
return err
}
}
}
return nil
}
2019-08-08 18:54:56 +00:00
func FindDeferReturnCalls ( text [ ] AsmInstruction ) [ ] uint64 {
2018-10-16 15:49:20 +00:00
const deferreturn = "runtime.deferreturn"
deferreturns := [ ] uint64 { }
// Find all runtime.deferreturn locations in the function
// See documentation of Breakpoint.DeferCond for why this is necessary
for _ , instr := range text {
if instr . IsCall ( ) && instr . DestLoc != nil && instr . DestLoc . Fn != nil && instr . DestLoc . Fn . Name == deferreturn {
deferreturns = append ( deferreturns , instr . Loc . PC )
}
}
return deferreturns
}
proc: support inlining
Go 1.10 added inlined calls to debug_info, this commit adds support
for DW_TAG_inlined_call to delve, both for stack traces (where
inlined calls will appear as normal stack frames) and to correct
the behavior of next, step and stepout.
The calls to Next and Frame of stackIterator continue to work
unchanged and only return real stack frames, after reading each line
appendInlinedCalls is called to unpacked all the inlined calls that
involve the current PC.
The fake stack frames produced by appendInlinedCalls are
distinguished from real stack frames by having the Inlined attribute
set to true. Also their Current and Call locations are treated
differently. The Call location will be changed to represent the
position inside the inlined call, while the Current location will
always reference the real stack frame. This is done because:
* next, step and stepout need to access the debug_info entry of
the real function they are stepping through
* we are already manipulating Call in different ways while Current
is just what we read from the call stack
The strategy remains mostly the same, we disassemble the function
and we set a breakpoint on each instruction corresponding to a
different file:line. The function in question will be the one
corresponding to the first real (i.e. non-inlined) stack frame.
* If the current function contains inlined calls, 'next' will not
set any breakpoints on instructions that belong to inlined calls. We
do not do this for 'step'.
* If we are inside an inlined call that makes other inlined
functions, 'next' will not set any breakpoints that belong to
inlined calls that are children of the current inlined call.
* If the current function is inlined the breakpoint on the return
address won't be set, because inlined frames don't have a return
address.
* The code we use for stepout doesn't work at all if we are inside
an inlined call, instead we call 'next' but instruct it to remove
all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
// Removes instructions belonging to inlined calls of topframe from pcs.
// If includeCurrentFn is true it will also remove all instructions
// belonging to the current function.
func removeInlinedCalls ( dbp Process , pcs [ ] uint64 , topframe Stackframe ) ( [ ] uint64 , error ) {
2020-03-20 17:23:10 +00:00
dwarfTree , err := topframe . Call . Fn . cu . image . getDwarfTree ( topframe . Call . Fn . offset )
if err != nil {
return pcs , err
}
for _ , e := range reader . InlineStack ( dwarfTree , 0 ) {
proc: support inlining
Go 1.10 added inlined calls to debug_info, this commit adds support
for DW_TAG_inlined_call to delve, both for stack traces (where
inlined calls will appear as normal stack frames) and to correct
the behavior of next, step and stepout.
The calls to Next and Frame of stackIterator continue to work
unchanged and only return real stack frames, after reading each line
appendInlinedCalls is called to unpacked all the inlined calls that
involve the current PC.
The fake stack frames produced by appendInlinedCalls are
distinguished from real stack frames by having the Inlined attribute
set to true. Also their Current and Call locations are treated
differently. The Call location will be changed to represent the
position inside the inlined call, while the Current location will
always reference the real stack frame. This is done because:
* next, step and stepout need to access the debug_info entry of
the real function they are stepping through
* we are already manipulating Call in different ways while Current
is just what we read from the call stack
The strategy remains mostly the same, we disassemble the function
and we set a breakpoint on each instruction corresponding to a
different file:line. The function in question will be the one
corresponding to the first real (i.e. non-inlined) stack frame.
* If the current function contains inlined calls, 'next' will not
set any breakpoints on instructions that belong to inlined calls. We
do not do this for 'step'.
* If we are inside an inlined call that makes other inlined
functions, 'next' will not set any breakpoints that belong to
inlined calls that are children of the current inlined call.
* If the current function is inlined the breakpoint on the return
address won't be set, because inlined frames don't have a return
address.
* The code we use for stepout doesn't work at all if we are inside
an inlined call, instead we call 'next' but instruct it to remove
all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
if e . Offset == topframe . Call . Fn . offset {
continue
}
2020-03-20 17:23:10 +00:00
for _ , rng := range e . Ranges {
pcs = removePCsBetween ( pcs , rng [ 0 ] , rng [ 1 ] )
proc: support inlining
Go 1.10 added inlined calls to debug_info, this commit adds support
for DW_TAG_inlined_call to delve, both for stack traces (where
inlined calls will appear as normal stack frames) and to correct
the behavior of next, step and stepout.
The calls to Next and Frame of stackIterator continue to work
unchanged and only return real stack frames, after reading each line
appendInlinedCalls is called to unpacked all the inlined calls that
involve the current PC.
The fake stack frames produced by appendInlinedCalls are
distinguished from real stack frames by having the Inlined attribute
set to true. Also their Current and Call locations are treated
differently. The Call location will be changed to represent the
position inside the inlined call, while the Current location will
always reference the real stack frame. This is done because:
* next, step and stepout need to access the debug_info entry of
the real function they are stepping through
* we are already manipulating Call in different ways while Current
is just what we read from the call stack
The strategy remains mostly the same, we disassemble the function
and we set a breakpoint on each instruction corresponding to a
different file:line. The function in question will be the one
corresponding to the first real (i.e. non-inlined) stack frame.
* If the current function contains inlined calls, 'next' will not
set any breakpoints on instructions that belong to inlined calls. We
do not do this for 'step'.
* If we are inside an inlined call that makes other inlined
functions, 'next' will not set any breakpoints that belong to
inlined calls that are children of the current inlined call.
* If the current function is inlined the breakpoint on the return
address won't be set, because inlined frames don't have a return
address.
* The code we use for stepout doesn't work at all if we are inside
an inlined call, instead we call 'next' but instruct it to remove
all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
}
}
2020-03-20 17:23:10 +00:00
return pcs , nil
proc: support inlining
Go 1.10 added inlined calls to debug_info, this commit adds support
for DW_TAG_inlined_call to delve, both for stack traces (where
inlined calls will appear as normal stack frames) and to correct
the behavior of next, step and stepout.
The calls to Next and Frame of stackIterator continue to work
unchanged and only return real stack frames, after reading each line
appendInlinedCalls is called to unpacked all the inlined calls that
involve the current PC.
The fake stack frames produced by appendInlinedCalls are
distinguished from real stack frames by having the Inlined attribute
set to true. Also their Current and Call locations are treated
differently. The Call location will be changed to represent the
position inside the inlined call, while the Current location will
always reference the real stack frame. This is done because:
* next, step and stepout need to access the debug_info entry of
the real function they are stepping through
* we are already manipulating Call in different ways while Current
is just what we read from the call stack
The strategy remains mostly the same, we disassemble the function
and we set a breakpoint on each instruction corresponding to a
different file:line. The function in question will be the one
corresponding to the first real (i.e. non-inlined) stack frame.
* If the current function contains inlined calls, 'next' will not
set any breakpoints on instructions that belong to inlined calls. We
do not do this for 'step'.
* If we are inside an inlined call that makes other inlined
functions, 'next' will not set any breakpoints that belong to
inlined calls that are children of the current inlined call.
* If the current function is inlined the breakpoint on the return
address won't be set, because inlined frames don't have a return
address.
* The code we use for stepout doesn't work at all if we are inside
an inlined call, instead we call 'next' but instruct it to remove
all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
}
2020-03-20 17:23:10 +00:00
func removePCsBetween ( pcs [ ] uint64 , start , end uint64 ) [ ] uint64 {
proc: support inlining
Go 1.10 added inlined calls to debug_info, this commit adds support
for DW_TAG_inlined_call to delve, both for stack traces (where
inlined calls will appear as normal stack frames) and to correct
the behavior of next, step and stepout.
The calls to Next and Frame of stackIterator continue to work
unchanged and only return real stack frames, after reading each line
appendInlinedCalls is called to unpacked all the inlined calls that
involve the current PC.
The fake stack frames produced by appendInlinedCalls are
distinguished from real stack frames by having the Inlined attribute
set to true. Also their Current and Call locations are treated
differently. The Call location will be changed to represent the
position inside the inlined call, while the Current location will
always reference the real stack frame. This is done because:
* next, step and stepout need to access the debug_info entry of
the real function they are stepping through
* we are already manipulating Call in different ways while Current
is just what we read from the call stack
The strategy remains mostly the same, we disassemble the function
and we set a breakpoint on each instruction corresponding to a
different file:line. The function in question will be the one
corresponding to the first real (i.e. non-inlined) stack frame.
* If the current function contains inlined calls, 'next' will not
set any breakpoints on instructions that belong to inlined calls. We
do not do this for 'step'.
* If we are inside an inlined call that makes other inlined
functions, 'next' will not set any breakpoints that belong to
inlined calls that are children of the current inlined call.
* If the current function is inlined the breakpoint on the return
address won't be set, because inlined frames don't have a return
address.
* The code we use for stepout doesn't work at all if we are inside
an inlined call, instead we call 'next' but instruct it to remove
all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
out := pcs [ : 0 ]
for _ , pc := range pcs {
2020-03-20 17:23:10 +00:00
if pc < start || pc >= end {
proc: support inlining
Go 1.10 added inlined calls to debug_info, this commit adds support
for DW_TAG_inlined_call to delve, both for stack traces (where
inlined calls will appear as normal stack frames) and to correct
the behavior of next, step and stepout.
The calls to Next and Frame of stackIterator continue to work
unchanged and only return real stack frames, after reading each line
appendInlinedCalls is called to unpacked all the inlined calls that
involve the current PC.
The fake stack frames produced by appendInlinedCalls are
distinguished from real stack frames by having the Inlined attribute
set to true. Also their Current and Call locations are treated
differently. The Call location will be changed to represent the
position inside the inlined call, while the Current location will
always reference the real stack frame. This is done because:
* next, step and stepout need to access the debug_info entry of
the real function they are stepping through
* we are already manipulating Call in different ways while Current
is just what we read from the call stack
The strategy remains mostly the same, we disassemble the function
and we set a breakpoint on each instruction corresponding to a
different file:line. The function in question will be the one
corresponding to the first real (i.e. non-inlined) stack frame.
* If the current function contains inlined calls, 'next' will not
set any breakpoints on instructions that belong to inlined calls. We
do not do this for 'step'.
* If we are inside an inlined call that makes other inlined
functions, 'next' will not set any breakpoints that belong to
inlined calls that are children of the current inlined call.
* If the current function is inlined the breakpoint on the return
address won't be set, because inlined frames don't have a return
address.
* The code we use for stepout doesn't work at all if we are inside
an inlined call, instead we call 'next' but instruct it to remove
all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
out = append ( out , pc )
}
}
return out
}
2017-04-21 07:50:38 +00:00
func setStepIntoBreakpoint ( dbp Process , text [ ] AsmInstruction , cond ast . Expr ) error {
2016-07-26 13:34:31 +00:00
if len ( text ) <= 0 {
return nil
}
instr := text [ 0 ]
2018-11-12 12:06:24 +00:00
if instr . DestLoc == nil {
// Call destination couldn't be resolved because this was not the
// current instruction, therefore the step-into breakpoint can not be set.
2016-07-26 13:34:31 +00:00
return nil
}
fn := instr . DestLoc . Fn
// Skip unexported runtime functions
2018-11-12 12:06:24 +00:00
if fn != nil && strings . HasPrefix ( fn . Name , "runtime." ) && ! isExportedRuntime ( fn . Name ) {
2016-07-26 13:34:31 +00:00
return nil
}
//TODO(aarzilli): if we want to let users hide functions
// or entire packages from being stepped into with 'step'
// those extra checks should be done here.
2018-11-12 12:06:24 +00:00
pc := instr . DestLoc . PC
2020-03-10 16:34:40 +00:00
// Skip InhibitStepInto functions for different arch.
if dbp . BinInfo ( ) . Arch . InhibitStepInto ( dbp . BinInfo ( ) , pc ) {
return nil
}
2018-11-12 12:06:24 +00:00
// We want to skip the function prologue but we should only do it if the
// destination address of the CALL instruction is the entry point of the
// function.
// Calls to runtime.duffzero and duffcopy inserted by the compiler can
// sometimes point inside the body of those functions, well after the
// prologue.
if fn != nil && fn . Entry == instr . DestLoc . PC {
pc , _ = FirstPCAfterPrologue ( dbp , fn , false )
}
2016-07-26 13:34:31 +00:00
// Set a breakpoint after the function's prologue
proc,terminal: Implement reverse step, next and stepout (#1785)
* proc: move defer breakpoint code into a function
Moves the code that sets a breakpoint on the first deferred function,
used by both next and StepOut, to its function.
* proc: implement reverse step/next/stepout
When the direction of execution is reversed (on a recording) Step, Next and
StepOut will behave similarly to their forward version. However there are
some subtle interactions between their behavior, prologue skipping, deferred
calls and normal calls. Specifically:
- when stepping backwards we need to set a breakpoint on the first
instruction after each CALL instruction, once this breakpoint is reached we
need to execute a single StepInstruction operation to reverse step into the
CALL.
- to insure that the prologue is skipped reverse next needs to check if it
is on the first instruction after the prologue, and if it is behave like
reverse stepout.
- there is no reason to set breakpoints on deferred calls when reverse
nexting or reverse stepping out, they will never be hit.
- reverse step out should generally place its breakpoint on the CALL
instruction that created the current stack frame (which will be the CALL
instruction immediately preceding the instruction at the return address).
- reverse step out needs to treat panic calls and deferreturn calls
specially.
* service,terminal: implement reverse step, next, stepout
2020-03-11 22:40:41 +00:00
if _ , err := allowDuplicateBreakpoint ( dbp . SetBreakpoint ( pc , NextBreakpoint , cond ) ) ; err != nil {
return err
2016-07-26 13:34:31 +00:00
}
return nil
2015-04-20 18:03:22 +00:00
}
2015-04-19 22:11:33 +00:00
2017-04-21 07:50:38 +00:00
func getGVariable ( thread Thread ) ( * Variable , error ) {
2016-11-15 16:16:33 +00:00
regs , err := thread . Registers ( false )
2016-01-10 17:29:14 +00:00
if err != nil {
return nil , err
}
2017-02-10 14:11:40 +00:00
gaddr , hasgaddr := regs . GAddr ( )
if ! hasgaddr {
2020-03-10 16:34:40 +00:00
var err error
gaddr , err = readUintRaw ( thread , uintptr ( regs . TLS ( ) + thread . BinInfo ( ) . GStructOffset ( ) ) , int64 ( thread . BinInfo ( ) . Arch . PtrSize ( ) ) )
2017-02-10 14:11:40 +00:00
if err != nil {
return nil , err
}
2016-01-10 17:29:14 +00:00
}
2016-01-24 16:30:23 +00:00
2017-06-21 22:40:42 +00:00
return newGVariable ( thread , uintptr ( gaddr ) , thread . Arch ( ) . DerefTLS ( ) )
2016-01-10 17:29:14 +00:00
}
2017-04-21 07:50:38 +00:00
func newGVariable ( thread Thread , gaddr uintptr , deref bool ) ( * Variable , error ) {
2017-02-15 13:41:03 +00:00
typ , err := thread . BinInfo ( ) . findType ( "runtime.g" )
2016-01-10 17:29:14 +00:00
if err != nil {
return nil , err
}
name := ""
if deref {
2018-08-31 18:08:18 +00:00
typ = & godwarf . PtrType {
CommonType : godwarf . CommonType {
ByteSize : int64 ( thread . Arch ( ) . PtrSize ( ) ) ,
Name : "" ,
ReflectKind : reflect . Ptr ,
Offset : 0 ,
} ,
Type : typ ,
}
2016-01-10 17:29:14 +00:00
} else {
name = "runtime.curg"
}
2017-02-15 13:41:03 +00:00
return newVariableFromThread ( thread , name , gaddr , typ ) , nil
2016-01-10 17:29:14 +00:00
}
2016-01-10 08:57:52 +00:00
// GetG returns information on the G (goroutine) that is executing on this thread.
2015-06-11 20:17:56 +00:00
//
2015-12-17 01:16:59 +00:00
// The G structure for a thread is stored in thread local storage. Here we simply
// calculate the address and read and parse the G struct.
2015-06-11 20:17:56 +00:00
//
// We cannot simply use the allg linked list in order to find the M that represents
// the given OS thread and follow its G pointer because on Darwin mach ports are not
// universal, so our port for this thread would not map to the `id` attribute of the M
// structure. Also, when linked against libc, Go prefers the libc version of clone as
// opposed to the runtime version. This has the consequence of not setting M.id for
// any thread, regardless of OS.
//
2015-07-28 05:33:07 +00:00
// In order to get around all this craziness, we read the address of the G structure for
// the current thread from the thread local storage area.
2018-01-03 10:13:43 +00:00
func GetG ( thread Thread ) ( * G , error ) {
2020-03-10 19:48:46 +00:00
if thread . Common ( ) . g != nil {
return thread . Common ( ) . g , nil
}
2019-10-21 17:44:25 +00:00
if loc , _ := thread . Location ( ) ; loc != nil && loc . Fn != nil && loc . Fn . Name == "runtime.clone" {
2019-02-26 17:22:33 +00:00
// When threads are executing runtime.clone the value of TLS is unreliable.
return nil , nil
}
2017-02-15 13:41:03 +00:00
gaddr , err := getGVariable ( thread )
2015-06-11 20:17:56 +00:00
if err != nil {
return nil , err
}
2016-01-10 17:29:14 +00:00
2018-01-03 10:13:43 +00:00
g , err := gaddr . parseG ( )
2017-09-01 13:34:13 +00:00
if err != nil {
2018-01-03 10:13:43 +00:00
return nil , err
2017-09-01 13:34:13 +00:00
}
if g . ID == 0 {
// The runtime uses a special goroutine with ID == 0 to mark that the
// current goroutine is executing on the system stack (sometimes also
// referred to as the g0 stack or scheduler stack, I'm not sure if there's
// actually any difference between those).
// For our purposes it's better if we always return the real goroutine
// since the rest of the code assumes the goroutine ID is univocal.
// The real 'current goroutine' is stored in g0.m.curg
2020-02-17 17:27:56 +00:00
mvar , err := g . variable . structMember ( "m" )
if err != nil {
return nil , err
}
curgvar , err := mvar . structMember ( "curg" )
2018-01-03 10:13:43 +00:00
if err != nil {
return nil , err
}
2017-09-01 13:34:13 +00:00
g , err = curgvar . parseG ( )
if err != nil {
2020-02-23 16:14:00 +00:00
if _ , ok := err . ( ErrNoGoroutine ) ; ok {
err = ErrNoGoroutine { thread . ThreadID ( ) }
}
2018-01-03 10:13:43 +00:00
return nil , err
2017-02-07 21:44:36 +00:00
}
2017-09-01 13:34:13 +00:00
g . SystemStack = true
}
g . Thread = thread
if loc , err := thread . Location ( ) ; err == nil {
g . CurrentLoc = * loc
2015-06-21 23:52:57 +00:00
}
2020-03-10 19:48:46 +00:00
thread . Common ( ) . g = g
2018-01-03 10:13:43 +00:00
return g , nil
2014-12-08 23:40:59 +00:00
}
2015-08-20 14:28:11 +00:00
2017-04-13 23:19:57 +00:00
// ThreadScope returns an EvalScope for this thread.
2017-04-21 07:50:38 +00:00
func ThreadScope ( thread Thread ) ( * EvalScope , error ) {
2018-04-14 13:44:39 +00:00
locations , err := ThreadStacktrace ( thread , 1 )
2015-08-28 20:06:29 +00:00
if err != nil {
return nil , err
}
2016-03-18 08:32:17 +00:00
if len ( locations ) < 1 {
return nil , errors . New ( "could not decode first frame" )
}
2018-04-14 13:44:39 +00:00
return FrameToScope ( thread . BinInfo ( ) , thread , nil , locations ... ) , nil
2017-04-13 23:19:57 +00:00
}
// GoroutineScope returns an EvalScope for the goroutine running on this thread.
2017-04-21 07:50:38 +00:00
func GoroutineScope ( thread Thread ) ( * EvalScope , error ) {
2018-04-14 13:44:39 +00:00
locations , err := ThreadStacktrace ( thread , 1 )
2017-04-13 23:19:57 +00:00
if err != nil {
return nil , err
}
if len ( locations ) < 1 {
return nil , errors . New ( "could not decode first frame" )
}
2017-05-16 18:23:33 +00:00
g , err := GetG ( thread )
2017-04-13 23:19:57 +00:00
if err != nil {
return nil , err
}
2018-04-14 13:44:39 +00:00
return FrameToScope ( thread . BinInfo ( ) , thread , g , locations ... ) , nil
2015-08-28 20:06:29 +00:00
}
2015-10-02 16:17:07 +00:00
2018-03-20 10:05:35 +00:00
// onNextGoroutine returns true if this thread is on the goroutine requested by the current 'next' command
2017-09-24 13:00:55 +00:00
func onNextGoroutine ( thread Thread , breakpoints * BreakpointMap ) ( bool , error ) {
2016-01-16 08:13:15 +00:00
var bp * Breakpoint
2017-09-24 13:00:55 +00:00
for i := range breakpoints . M {
2017-09-25 06:29:13 +00:00
if breakpoints . M [ i ] . Kind != UserBreakpoint && breakpoints . M [ i ] . internalCond != nil {
2017-09-24 13:00:55 +00:00
bp = breakpoints . M [ i ]
2016-07-26 13:34:31 +00:00
break
2016-01-16 08:13:15 +00:00
}
}
if bp == nil {
2016-01-12 08:01:42 +00:00
return false , nil
2016-01-16 08:13:15 +00:00
}
2017-05-16 18:23:33 +00:00
// Internal breakpoint conditions can take multiple different forms:
// Step into breakpoints:
// runtime.curg.goid == X
// Next or StepOut breakpoints:
// runtime.curg.goid == X && runtime.frameoff == Y
// Breakpoints that can be hit either by stepping on a line in the same
// function or by returning from the function:
// runtime.curg.goid == X && (runtime.frameoff == Y || runtime.frameoff == Z)
// Here we are only interested in testing the runtime.curg.goid clause.
w := onNextGoroutineWalker { thread : thread }
2017-09-25 06:29:13 +00:00
ast . Walk ( & w , bp . internalCond )
2017-05-16 18:23:33 +00:00
return w . ret , w . err
}
type onNextGoroutineWalker struct {
thread Thread
ret bool
err error
}
func ( w * onNextGoroutineWalker ) Visit ( n ast . Node ) ast . Visitor {
if binx , isbin := n . ( * ast . BinaryExpr ) ; isbin && binx . Op == token . EQL && exprToString ( binx . X ) == "runtime.curg.goid" {
w . ret , w . err = evalBreakpointCondition ( w . thread , n . ( ast . Expr ) )
return nil
2016-07-26 13:34:31 +00:00
}
2017-05-16 18:23:33 +00:00
return w
2017-02-15 13:41:03 +00:00
}