2015-03-20 21:11:11 +00:00
package debugger
import (
2020-03-24 09:47:36 +00:00
"bytes"
2018-07-05 10:15:22 +00:00
"debug/dwarf"
2015-07-03 19:18:03 +00:00
"errors"
2015-03-20 21:11:11 +00:00
"fmt"
2016-01-24 08:16:45 +00:00
"go/parser"
2021-05-28 18:21:53 +00:00
"go/token"
2020-03-24 16:09:28 +00:00
"os"
2016-01-30 21:27:08 +00:00
"path/filepath"
2015-03-20 21:11:11 +00:00
"regexp"
2016-01-30 21:27:08 +00:00
"runtime"
2019-11-01 19:41:06 +00:00
"sort"
2021-05-28 18:21:53 +00:00
"strconv"
2016-01-30 21:27:08 +00:00
"strings"
2016-02-15 14:39:29 +00:00
"sync"
2016-12-22 16:53:34 +00:00
"time"
2015-03-20 21:11:11 +00:00
2020-02-24 18:47:02 +00:00
"github.com/go-delve/delve/pkg/dwarf/op"
2020-06-05 18:03:09 +00:00
"github.com/go-delve/delve/pkg/gobuild"
2019-01-04 18:39:25 +00:00
"github.com/go-delve/delve/pkg/goversion"
2020-05-13 06:38:10 +00:00
"github.com/go-delve/delve/pkg/locspec"
2019-01-04 18:39:25 +00:00
"github.com/go-delve/delve/pkg/logflags"
"github.com/go-delve/delve/pkg/proc"
"github.com/go-delve/delve/pkg/proc/core"
"github.com/go-delve/delve/pkg/proc/gdbserial"
"github.com/go-delve/delve/pkg/proc/native"
"github.com/go-delve/delve/service/api"
2018-06-14 18:12:11 +00:00
"github.com/sirupsen/logrus"
2015-03-20 21:11:11 +00:00
)
2020-04-01 00:47:50 +00:00
var (
// ErrCanNotRestart is returned when the target cannot be restarted.
// This is returned for targets that have been attached to, or when
// debugging core files.
ErrCanNotRestart = errors . New ( "can not restart this target" )
// ErrNotRecording is returned when StopRecording is called while the
// debugger is not recording the target.
ErrNotRecording = errors . New ( "debugger is not recording" )
2021-01-29 21:39:33 +00:00
// ErrCoreDumpInProgress is returned when a core dump is already in progress.
ErrCoreDumpInProgress = errors . New ( "core dump in progress" )
// ErrCoreDumpNotSupported is returned when core dumping is not supported
ErrCoreDumpNotSupported = errors . New ( "core dumping not supported" )
2020-04-01 00:47:50 +00:00
)
2015-06-20 22:50:24 +00:00
// Debugger service.
//
// Debugger provides a higher level of
2015-06-20 22:54:52 +00:00
// abstraction over proc.Process.
2015-06-20 22:50:24 +00:00
// It handles converting from internal types to
// the types expected by clients. It also handles
// functionality needed by clients, but not needed in
// lower lever packages such as proc.
2015-03-20 21:11:11 +00:00
type Debugger struct {
2017-02-08 00:23:47 +00:00
config * Config
2018-01-18 22:16:11 +00:00
// arguments to launch a new process.
processArgs [ ] string
2020-04-16 17:40:50 +00:00
targetMutex sync . Mutex
2022-07-14 21:14:45 +00:00
target * proc . TargetGroup
2020-04-16 17:40:50 +00:00
log * logrus . Entry
2018-06-21 11:07:37 +00:00
running bool
runningMutex sync . Mutex
2020-03-24 16:09:28 +00:00
stopRecording func ( ) error
recordMutex sync . Mutex
2021-01-29 21:39:33 +00:00
dumpState proc . DumpState
2022-02-25 09:09:53 +00:00
breakpointIDCounter int
2015-03-20 21:11:11 +00:00
}
2020-06-05 18:03:09 +00:00
type ExecuteKind int
const (
ExecutingExistingFile = ExecuteKind ( iota )
ExecutingGeneratedFile
ExecutingGeneratedTest
ExecutingOther
)
2015-03-20 21:11:11 +00:00
// Config provides the configuration to start a Debugger.
//
// Only one of ProcessArgs or AttachPid should be specified. If ProcessArgs is
// provided, a new process will be launched. Otherwise, the debugger will try
// to attach to an existing process with AttachPid.
type Config struct {
2016-11-01 19:58:43 +00:00
// WorkingDir is working directory of the new process. This field is used
// only when launching a new process.
WorkingDir string
2015-03-20 21:11:11 +00:00
// AttachPid is the PID of an existing process to which the debugger should
// attach.
AttachPid int
2017-04-03 09:17:54 +00:00
2017-02-10 14:11:40 +00:00
// CoreFile specifies the path to the core dump to open.
2017-04-03 09:17:54 +00:00
CoreFile string
2020-04-13 18:07:15 +00:00
2017-02-10 14:11:40 +00:00
// Backend specifies the debugger backend.
Backend string
2018-04-16 10:01:08 +00:00
// Foreground lets target process access stdin.
Foreground bool
2018-11-07 22:21:35 +00:00
// DebugInfoDirectories is the list of directories to look for
// when resolving external debug info files.
DebugInfoDirectories [ ] string
2019-04-26 17:24:21 +00:00
// CheckGoVersion is true if the debugger should check the version of Go
// used to compile the executable and refuse to work on incompatible
// versions.
CheckGoVersion bool
2020-04-04 22:19:35 +00:00
// TTY is passed along to the target process on creation. Used to specify a
// TTY for that process.
TTY string
2020-06-05 18:03:09 +00:00
// Packages contains the packages that we are debugging.
Packages [ ] string
// BuildFlags contains the flags passed to the compiler.
BuildFlags string
// ExecuteKind contains the kind of the executed program.
ExecuteKind ExecuteKind
2020-08-21 14:14:02 +00:00
// Redirects specifies redirect rules for stdin, stdout and stderr
Redirects [ 3 ] string
2020-10-21 19:50:52 +00:00
// DisableASLR disables ASLR
DisableASLR bool
2015-03-20 21:11:11 +00:00
}
2018-01-18 22:16:11 +00:00
// New creates a new Debugger. ProcessArgs specify the commandline arguments for the
// new process.
func New ( config * Config , processArgs [ ] string ) ( * Debugger , error ) {
2019-03-27 21:58:36 +00:00
logger := logflags . DebuggerLogger ( )
2015-06-13 04:47:30 +00:00
d := & Debugger {
2018-01-18 22:16:11 +00:00
config : config ,
processArgs : processArgs ,
2018-06-14 18:12:11 +00:00
log : logger ,
2015-03-20 21:11:11 +00:00
}
// Create the process by either attaching or launching.
2017-04-03 09:17:54 +00:00
switch {
case d . config . AttachPid > 0 :
2018-06-14 18:12:11 +00:00
d . log . Infof ( "attaching to pid %d" , d . config . AttachPid )
2017-02-10 14:11:40 +00:00
path := ""
2018-01-18 22:16:11 +00:00
if len ( d . processArgs ) > 0 {
path = d . processArgs [ 0 ]
2017-02-10 14:11:40 +00:00
}
p , err := d . Attach ( d . config . AttachPid , path )
2015-03-20 21:11:11 +00:00
if err != nil {
2018-07-05 10:15:22 +00:00
err = go11DecodeErrorCheck ( err )
2022-04-01 17:31:03 +00:00
err = noDebugErrorWarning ( err )
2015-06-27 09:42:58 +00:00
return nil , attachErrorMessage ( d . config . AttachPid , err )
2015-03-20 21:11:11 +00:00
}
2022-07-14 21:14:45 +00:00
d . target = proc . NewGroup ( p )
2017-04-03 09:17:54 +00:00
case d . config . CoreFile != "" :
2020-01-21 20:41:24 +00:00
var p * proc . Target
2017-05-05 22:17:52 +00:00
var err error
switch d . config . Backend {
case "rr" :
2018-06-14 18:12:11 +00:00
d . log . Infof ( "opening trace %s" , d . config . CoreFile )
2019-10-21 18:48:04 +00:00
p , err = gdbserial . Replay ( d . config . CoreFile , false , false , d . config . DebugInfoDirectories )
2017-05-05 22:17:52 +00:00
default :
2018-06-14 18:12:11 +00:00
d . log . Infof ( "opening core file %s (executable %s)" , d . config . CoreFile , d . processArgs [ 0 ] )
2018-11-07 22:21:35 +00:00
p , err = core . OpenCore ( d . config . CoreFile , d . processArgs [ 0 ] , d . config . DebugInfoDirectories )
2017-05-05 22:17:52 +00:00
}
2017-04-03 09:17:54 +00:00
if err != nil {
2018-07-05 10:15:22 +00:00
err = go11DecodeErrorCheck ( err )
2017-04-03 09:17:54 +00:00
return nil , err
}
2022-07-14 21:14:45 +00:00
d . target = proc . NewGroup ( p )
2019-04-26 17:24:21 +00:00
if err := d . checkGoVersion ( ) ; err != nil {
d . target . Detach ( true )
return nil , err
}
2017-04-03 09:17:54 +00:00
default :
2018-06-14 18:12:11 +00:00
d . log . Infof ( "launching process with args: %v" , d . processArgs )
2018-01-18 22:16:11 +00:00
p , err := d . Launch ( d . processArgs , d . config . WorkingDir )
2015-03-20 21:11:11 +00:00
if err != nil {
2020-03-10 16:34:40 +00:00
if _ , ok := err . ( * proc . ErrUnsupportedArch ) ; ! ok {
2018-07-05 10:15:22 +00:00
err = go11DecodeErrorCheck ( err )
2022-04-01 17:31:03 +00:00
err = noDebugErrorWarning ( err )
2016-04-21 10:19:21 +00:00
err = fmt . Errorf ( "could not launch process: %s" , err )
}
return nil , err
2015-03-20 21:11:11 +00:00
}
2020-03-24 16:09:28 +00:00
if p != nil {
// if p == nil and err == nil then we are doing a recording, don't touch d.target
2022-07-14 21:14:45 +00:00
d . target = proc . NewGroup ( p )
2020-03-24 16:09:28 +00:00
}
2019-04-26 17:24:21 +00:00
if err := d . checkGoVersion ( ) ; err != nil {
d . target . Detach ( true )
return nil , err
}
2015-03-20 21:11:11 +00:00
}
2021-03-19 18:02:23 +00:00
2015-06-13 04:47:30 +00:00
return d , nil
2015-03-20 21:11:11 +00:00
}
2019-10-21 18:48:04 +00:00
// canRestart returns true if the target was started with Launch and can be restarted
func ( d * Debugger ) canRestart ( ) bool {
switch {
case d . config . AttachPid > 0 :
return false
case d . config . CoreFile != "" :
return false
default :
return true
}
}
2019-04-26 17:24:21 +00:00
func ( d * Debugger ) checkGoVersion ( ) error {
2020-03-24 16:09:28 +00:00
if d . isRecording ( ) {
// do not do anything if we are still recording
return nil
}
2022-07-14 21:14:45 +00:00
producer := d . target . Selected . BinInfo ( ) . Producer ( )
2019-04-26 17:24:21 +00:00
if producer == "" {
return nil
}
2021-09-25 15:41:00 +00:00
return goversion . Compatible ( producer , ! d . config . CheckGoVersion )
2019-04-26 17:24:21 +00:00
}
2021-06-28 15:39:34 +00:00
func ( d * Debugger ) TargetGoVersion ( ) string {
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2022-07-14 21:14:45 +00:00
return d . target . Selected . BinInfo ( ) . Producer ( )
2021-06-28 15:39:34 +00:00
}
2018-08-31 18:08:18 +00:00
// Launch will start a process with the given args and working directory.
2020-01-21 20:41:24 +00:00
func ( d * Debugger ) Launch ( processArgs [ ] string , wd string ) ( * proc . Target , error ) {
2020-04-01 00:47:50 +00:00
if err := verifyBinaryFormat ( processArgs [ 0 ] ) ; err != nil {
return nil , err
}
2020-10-21 19:50:52 +00:00
launchFlags := proc . LaunchFlags ( 0 )
if d . config . Foreground {
launchFlags |= proc . LaunchForeground
}
if d . config . DisableASLR {
launchFlags |= proc . LaunchDisableASLR
}
2017-02-10 14:11:40 +00:00
switch d . config . Backend {
case "native" :
2020-10-21 19:50:52 +00:00
return native . Launch ( processArgs , wd , launchFlags , d . config . DebugInfoDirectories , d . config . TTY , d . config . Redirects )
2017-02-10 14:11:40 +00:00
case "lldb" :
2020-10-21 19:50:52 +00:00
return betterGdbserialLaunchError ( gdbserial . LLDBLaunch ( processArgs , wd , launchFlags , d . config . DebugInfoDirectories , d . config . TTY , d . config . Redirects ) )
2017-05-05 22:17:52 +00:00
case "rr" :
2020-03-24 16:09:28 +00:00
if d . target != nil {
// restart should not call us if the backend is 'rr'
panic ( "internal error: call to Launch with rr backend and target already exists" )
}
2020-08-21 14:14:02 +00:00
run , stop , err := gdbserial . RecordAsync ( processArgs , wd , false , d . config . Redirects )
2020-03-24 16:09:28 +00:00
if err != nil {
return nil , err
}
2020-04-16 17:40:50 +00:00
// let the initialization proceed but hold the targetMutex lock so that
2020-03-24 16:09:28 +00:00
// any other request to debugger will block except State(nowait=true) and
// Command(halt).
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
2020-03-24 16:09:28 +00:00
d . recordingStart ( stop )
go func ( ) {
2020-04-16 17:40:50 +00:00
defer d . targetMutex . Unlock ( )
2020-03-24 16:09:28 +00:00
p , err := d . recordingRun ( run )
if err != nil {
d . log . Errorf ( "could not record target: %v" , err )
// this is ugly but we can't respond to any client requests at this
// point so it's better if we die.
os . Exit ( 1 )
}
d . recordingDone ( )
2022-07-14 21:14:45 +00:00
d . target = proc . NewGroup ( p )
2020-03-24 16:09:28 +00:00
if err := d . checkGoVersion ( ) ; err != nil {
d . log . Error ( err )
err := d . target . Detach ( true )
if err != nil {
d . log . Errorf ( "Error detaching from target: %v" , err )
}
}
} ( )
return nil , nil
2017-02-10 14:11:40 +00:00
case "default" :
if runtime . GOOS == "darwin" {
2020-10-21 19:50:52 +00:00
return betterGdbserialLaunchError ( gdbserial . LLDBLaunch ( processArgs , wd , launchFlags , d . config . DebugInfoDirectories , d . config . TTY , d . config . Redirects ) )
2017-02-10 14:11:40 +00:00
}
2020-10-21 19:50:52 +00:00
return native . Launch ( processArgs , wd , launchFlags , d . config . DebugInfoDirectories , d . config . TTY , d . config . Redirects )
2017-02-10 14:11:40 +00:00
default :
return nil , fmt . Errorf ( "unknown backend %q" , d . config . Backend )
}
}
2020-03-24 16:09:28 +00:00
func ( d * Debugger ) recordingStart ( stop func ( ) error ) {
d . recordMutex . Lock ( )
d . stopRecording = stop
d . recordMutex . Unlock ( )
}
func ( d * Debugger ) recordingDone ( ) {
d . recordMutex . Lock ( )
d . stopRecording = nil
d . recordMutex . Unlock ( )
}
func ( d * Debugger ) isRecording ( ) bool {
d . recordMutex . Lock ( )
defer d . recordMutex . Unlock ( )
return d . stopRecording != nil
}
func ( d * Debugger ) recordingRun ( run func ( ) ( string , error ) ) ( * proc . Target , error ) {
tracedir , err := run ( )
if err != nil && tracedir == "" {
return nil , err
}
return gdbserial . Replay ( tracedir , false , true , d . config . DebugInfoDirectories )
}
2018-08-31 18:08:18 +00:00
// Attach will attach to the process specified by 'pid'.
2020-01-21 20:41:24 +00:00
func ( d * Debugger ) Attach ( pid int , path string ) ( * proc . Target , error ) {
2017-02-10 14:11:40 +00:00
switch d . config . Backend {
case "native" :
2018-11-07 22:21:35 +00:00
return native . Attach ( pid , d . config . DebugInfoDirectories )
2017-02-10 14:11:40 +00:00
case "lldb" :
2018-11-07 22:21:35 +00:00
return betterGdbserialLaunchError ( gdbserial . LLDBAttach ( pid , path , d . config . DebugInfoDirectories ) )
2017-02-10 14:11:40 +00:00
case "default" :
if runtime . GOOS == "darwin" {
2018-11-07 22:21:35 +00:00
return betterGdbserialLaunchError ( gdbserial . LLDBAttach ( pid , path , d . config . DebugInfoDirectories ) )
2017-02-10 14:11:40 +00:00
}
2018-11-07 22:21:35 +00:00
return native . Attach ( pid , d . config . DebugInfoDirectories )
2017-02-10 14:11:40 +00:00
default :
return nil , fmt . Errorf ( "unknown backend %q" , d . config . Backend )
}
}
2020-04-04 11:58:06 +00:00
var errMacOSBackendUnavailable = errors . New ( "debugserver or lldb-server not found: install Xcode's command line tools or lldb-server" )
2018-04-08 14:49:52 +00:00
2020-01-21 20:41:24 +00:00
func betterGdbserialLaunchError ( p * proc . Target , err error ) ( * proc . Target , error ) {
2018-04-08 14:49:52 +00:00
if runtime . GOOS != "darwin" {
return p , err
}
if _ , isUnavailable := err . ( * gdbserial . ErrBackendUnavailable ) ; ! isUnavailable {
return p , err
}
2018-08-31 18:08:18 +00:00
return p , errMacOSBackendUnavailable
2018-04-08 14:49:52 +00:00
}
2016-01-10 08:57:52 +00:00
// ProcessPid returns the PID of the process
// the debugger is debugging.
2015-07-03 20:35:22 +00:00
func ( d * Debugger ) ProcessPid ( ) int {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2022-07-14 21:14:45 +00:00
return d . target . Selected . Pid ( )
2015-07-03 20:35:22 +00:00
}
2016-12-22 16:53:34 +00:00
// LastModified returns the time that the process' executable was last
// modified.
func ( d * Debugger ) LastModified ( ) time . Time {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2022-07-14 21:14:45 +00:00
return d . target . Selected . BinInfo ( ) . LastModified ( )
2016-12-22 16:53:34 +00:00
}
2018-10-16 15:49:20 +00:00
// FunctionReturnLocations returns all return locations
2019-08-08 18:54:56 +00:00
// for the given function, a list of addresses corresponding
// to 'ret' or 'call runtime.deferreturn'.
2018-10-16 15:49:20 +00:00
func ( d * Debugger ) FunctionReturnLocations ( fnName string ) ( [ ] uint64 , error ) {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2019-12-06 09:55:22 +00:00
2019-08-08 18:54:56 +00:00
var (
2022-07-14 21:14:45 +00:00
p = d . target . Selected
2019-08-08 18:54:56 +00:00
g = p . SelectedGoroutine ( )
)
2021-10-30 18:52:26 +00:00
fns , err := p . BinInfo ( ) . FindFunction ( fnName )
2019-08-08 18:54:56 +00:00
if err != nil {
return nil , err
}
var addrs [ ] uint64
2021-10-30 18:52:26 +00:00
for _ , fn := range fns {
var regs proc . Registers
mem := p . Memory ( )
if g != nil && g . Thread != nil {
regs , _ = g . Thread . Registers ( )
}
instructions , err := proc . Disassemble ( mem , regs , p . Breakpoints ( ) , p . BinInfo ( ) , fn . Entry , fn . End )
if err != nil {
return nil , err
}
for _ , instruction := range instructions {
if instruction . IsRet ( ) {
addrs = append ( addrs , instruction . Loc . PC )
}
2019-08-08 18:54:56 +00:00
}
2021-10-30 18:52:26 +00:00
addrs = append ( addrs , proc . FindDeferReturnCalls ( instructions ) ... )
2019-08-08 18:54:56 +00:00
}
return addrs , nil
2018-10-16 15:49:20 +00:00
}
2016-01-10 08:57:52 +00:00
// Detach detaches from the target process.
// If `kill` is true we will kill the process after
// detaching.
2015-03-20 21:11:11 +00:00
func ( d * Debugger ) Detach ( kill bool ) error {
2021-02-24 16:19:07 +00:00
d . log . Debug ( "detaching" )
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2020-12-15 16:13:13 +00:00
if ok , _ := d . target . Valid ( ) ; ! ok {
return nil
}
2016-02-15 14:39:29 +00:00
return d . detach ( kill )
}
func ( d * Debugger ) detach ( kill bool ) error {
2017-03-27 08:37:07 +00:00
if d . config . AttachPid == 0 {
kill = true
2015-07-11 06:43:47 +00:00
}
2017-03-27 08:37:07 +00:00
return d . target . Detach ( kill )
2015-03-20 21:11:11 +00:00
}
2016-01-10 08:57:52 +00:00
// Restart will restart the target process, first killing
// and then exec'ing it again.
2017-05-05 22:17:52 +00:00
// If the target process is a recording it will restart it from the given
// position. If pos starts with 'c' it's a checkpoint ID, otherwise it's an
2018-01-18 22:16:11 +00:00
// event number. If resetArgs is true, newArgs will replace the process args.
2020-08-21 14:14:02 +00:00
func ( d * Debugger ) Restart ( rerecord bool , pos string , resetArgs bool , newArgs [ ] string , newRedirects [ 3 ] string , rebuild bool ) ( [ ] api . DiscardedBreakpoint , error ) {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2016-02-15 14:39:29 +00:00
2019-10-21 18:48:04 +00:00
recorded , _ := d . target . Recorded ( )
if recorded && ! rerecord {
2021-12-13 19:39:20 +00:00
d . target . ResumeNotify ( nil )
2017-05-05 22:17:52 +00:00
return nil , d . target . Restart ( pos )
}
if pos != "" {
2018-08-31 18:08:18 +00:00
return nil , proc . ErrNotRecorded
2017-04-03 09:17:54 +00:00
}
2019-10-21 18:48:04 +00:00
if ! d . canRestart ( ) {
return nil , ErrCanNotRestart
}
2017-03-27 08:37:07 +00:00
if err := d . detach ( true ) ; err != nil {
return nil , err
2015-07-03 20:35:22 +00:00
}
2018-01-18 22:16:11 +00:00
if resetArgs {
d . processArgs = append ( [ ] string { d . processArgs [ 0 ] } , newArgs ... )
2020-08-21 14:14:02 +00:00
d . config . Redirects = newRedirects
2018-01-18 22:16:11 +00:00
}
2020-03-24 16:09:28 +00:00
var p * proc . Target
var err error
2020-06-05 18:03:09 +00:00
if rebuild {
switch d . config . ExecuteKind {
case ExecutingGeneratedFile :
err = gobuild . GoBuild ( d . processArgs [ 0 ] , d . config . Packages , d . config . BuildFlags )
if err != nil {
return nil , fmt . Errorf ( "could not rebuild process: %s" , err )
}
case ExecutingGeneratedTest :
err = gobuild . GoTestBuild ( d . processArgs [ 0 ] , d . config . Packages , d . config . BuildFlags )
if err != nil {
return nil , fmt . Errorf ( "could not rebuild process: %s" , err )
}
default :
// We cannot build a process that we didn't start, because we don't know how it was built.
return nil , fmt . Errorf ( "cannot rebuild a binary" )
}
}
2020-03-24 16:09:28 +00:00
if recorded {
2020-08-21 14:14:02 +00:00
run , stop , err2 := gdbserial . RecordAsync ( d . processArgs , d . config . WorkingDir , false , d . config . Redirects )
2020-03-24 16:09:28 +00:00
if err2 != nil {
return nil , err2
}
d . recordingStart ( stop )
p , err = d . recordingRun ( run )
d . recordingDone ( )
} else {
p , err = d . Launch ( d . processArgs , d . config . WorkingDir )
}
2015-07-03 20:35:22 +00:00
if err != nil {
2016-12-22 16:53:34 +00:00
return nil , fmt . Errorf ( "could not launch process: %s" , err )
2015-07-03 20:35:22 +00:00
}
2020-03-24 16:09:28 +00:00
2016-12-22 16:53:34 +00:00
discarded := [ ] api . DiscardedBreakpoint { }
2022-07-14 21:14:45 +00:00
p . Breakpoints ( ) . Logical = d . target . LogicalBreakpoints
d . target = proc . NewGroup ( p )
for _ , oldBp := range d . target . LogicalBreakpoints {
2022-05-25 20:58:26 +00:00
if oldBp . LogicalID < 0 || ! oldBp . Enabled {
2016-03-06 17:54:43 +00:00
continue
}
2022-05-25 20:58:26 +00:00
if len ( oldBp . File ) > 0 {
oldBp . TotalHitCount = 0
oldBp . HitCount = make ( map [ int ] uint64 )
err := d . createPhysicalBreakpoints ( oldBp )
2016-12-22 16:53:34 +00:00
if err != nil {
2022-05-25 20:58:26 +00:00
discarded = append ( discarded , api . DiscardedBreakpoint { Breakpoint : api . ConvertLogicalBreakpoint ( oldBp ) , Reason : err . Error ( ) } )
2016-12-22 16:53:34 +00:00
continue
}
2019-11-01 19:41:06 +00:00
} else {
2022-05-25 20:58:26 +00:00
discarded = append ( discarded , api . DiscardedBreakpoint { Breakpoint : api . ConvertLogicalBreakpoint ( oldBp ) , Reason : "can not recreate watchpoint on restart" } )
2021-04-12 21:59:43 +00:00
}
}
2017-03-27 08:37:07 +00:00
return discarded , nil
2015-07-03 20:35:22 +00:00
}
2016-01-10 08:57:52 +00:00
// State returns the current state of the debugger.
2018-06-21 11:07:37 +00:00
func ( d * Debugger ) State ( nowait bool ) ( * api . DebuggerState , error ) {
2021-05-04 19:49:52 +00:00
if d . IsRunning ( ) && nowait {
2018-06-21 11:07:37 +00:00
return & api . DebuggerState { Running : true } , nil
}
2020-03-24 16:09:28 +00:00
if d . isRecording ( ) && nowait {
return & api . DebuggerState { Recording : true } , nil
}
2021-01-29 21:39:33 +00:00
d . dumpState . Mutex . Lock ( )
if d . dumpState . Dumping && nowait {
return & api . DebuggerState { CoreDumping : true } , nil
}
d . dumpState . Mutex . Unlock ( )
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2018-05-11 12:51:15 +00:00
return d . state ( nil )
2016-02-15 14:39:29 +00:00
}
2018-05-11 12:51:15 +00:00
func ( d * Debugger ) state ( retLoadCfg * proc . LoadConfig ) ( * api . DebuggerState , error ) {
2018-06-21 11:07:37 +00:00
if _ , err := d . target . Valid ( ) ; err != nil {
return nil , err
2015-10-09 23:30:28 +00:00
}
2015-06-13 04:47:30 +00:00
var (
2015-08-28 20:06:29 +00:00
state * api . DebuggerState
goroutine * api . Goroutine
2015-06-13 04:47:30 +00:00
)
2015-08-28 20:06:29 +00:00
2022-07-14 21:14:45 +00:00
tgt := d . target . Selected
if tgt . SelectedGoroutine ( ) != nil {
goroutine = api . ConvertGoroutine ( tgt , tgt . SelectedGoroutine ( ) )
2015-06-13 04:47:30 +00:00
}
2015-03-20 21:11:11 +00:00
2018-06-21 11:07:37 +00:00
exited := false
2022-07-14 21:14:45 +00:00
if _ , err := tgt . Valid ( ) ; err != nil {
2021-04-13 06:52:29 +00:00
_ , exited = err . ( proc . ErrProcessExited )
2018-06-21 11:07:37 +00:00
}
2015-06-13 04:47:30 +00:00
state = & api . DebuggerState {
2022-07-14 21:14:45 +00:00
Pid : tgt . Pid ( ) ,
2015-08-28 20:06:29 +00:00
SelectedGoroutine : goroutine ,
2018-06-21 11:07:37 +00:00
Exited : exited ,
2015-06-13 04:47:30 +00:00
}
2015-03-20 21:11:11 +00:00
2017-02-15 13:41:03 +00:00
for _ , thread := range d . target . ThreadList ( ) {
2022-05-25 20:58:26 +00:00
th := api . ConvertThread ( thread , d . ConvertThreadBreakpoint ( thread ) )
2018-05-11 12:51:15 +00:00
2020-12-10 16:57:50 +00:00
th . CallReturn = thread . Common ( ) . CallReturn
2018-05-11 12:51:15 +00:00
if retLoadCfg != nil {
2020-07-07 13:21:18 +00:00
th . ReturnValues = api . ConvertVars ( thread . Common ( ) . ReturnValues ( * retLoadCfg ) )
2018-05-11 12:51:15 +00:00
}
2015-10-29 09:59:22 +00:00
state . Threads = append ( state . Threads , th )
2022-07-14 21:14:45 +00:00
if thread . ThreadID ( ) == tgt . CurrentThread ( ) . ThreadID ( ) {
2015-10-29 09:59:22 +00:00
state . CurrentThread = th
}
}
2022-07-14 21:14:45 +00:00
state . NextInProgress = d . target . HasSteppingBreakpoints ( )
2016-04-24 23:20:02 +00:00
2017-05-05 22:17:52 +00:00
if recorded , _ := d . target . Recorded ( ) ; recorded {
state . When , _ = d . target . When ( )
}
2022-07-14 21:14:45 +00:00
for _ , t := range d . target . Targets ( ) {
for _ , bp := range t . Breakpoints ( ) . WatchOutOfScope {
abp := api . ConvertLogicalBreakpoint ( bp . Logical )
api . ConvertPhysicalBreakpoints ( abp , [ ] * proc . Breakpoint { bp } )
state . WatchOutOfScope = append ( state . WatchOutOfScope , abp )
}
2021-08-09 17:41:25 +00:00
}
2015-06-13 04:47:30 +00:00
return state , nil
2015-03-20 21:11:11 +00:00
}
2021-06-29 13:14:24 +00:00
// CreateBreakpoint creates a breakpoint using information from the provided `requestedBp`.
// This function accepts several different ways of specifying where and how to create the
// breakpoint that has been requested. Any error encountered during the attempt to set the
// breakpoint will be returned to the caller.
//
// The ways of specifying a breakpoint are listed below in the order they are considered by
// this function:
//
// - If requestedBp.TraceReturn is true then it is expected that
// requestedBp.Addrs will contain the list of return addresses
// supplied by the caller.
//
// - If requestedBp.File is not an empty string the breakpoint
// will be created on the specified file:line location
//
// - If requestedBp.FunctionName is not an empty string
// the breakpoint will be created on the specified function:line
// location.
//
// - If requestedBp.Addrs is filled it will create a logical breakpoint
// corresponding to all specified addresses.
//
// - Otherwise the value specified by arg.Breakpoint.Addr will be used.
//
// Note that this method will use the first successful method in order to
// create a breakpoint, so mixing different fields will not result is multiple
// breakpoints being set.
2015-06-12 19:32:32 +00:00
func ( d * Debugger ) CreateBreakpoint ( requestedBp * api . Breakpoint ) ( * api . Breakpoint , error ) {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2016-02-15 14:39:29 +00:00
2022-07-14 21:14:45 +00:00
if len ( d . target . Targets ( ) ) != 1 {
//TODO(aarzilli):
// - the calls to FindFileLocation and FindFunctionLocation need to be done on all targets
// - the Addrs slice and the Addr field need to be converted to a format
// that can specify to which target an address belongs when there are
// multiple targets (but this must happen in a backwards compatible way)
panic ( "multiple targets not implemented" )
}
2015-08-07 16:50:14 +00:00
var (
2019-11-01 19:41:06 +00:00
addrs [ ] uint64
err error
2015-08-07 16:50:14 +00:00
)
2016-01-24 08:16:45 +00:00
if requestedBp . Name != "" {
2022-05-25 20:58:26 +00:00
if d . findBreakpointByName ( requestedBp . Name ) != nil {
2016-01-24 08:16:45 +00:00
return nil , errors . New ( "breakpoint name already exists" )
}
}
2015-06-13 04:47:30 +00:00
switch {
2018-10-16 15:49:20 +00:00
case requestedBp . TraceReturn :
2019-11-01 19:41:06 +00:00
addrs = [ ] uint64 { requestedBp . Addr }
2015-06-13 04:47:30 +00:00
case len ( requestedBp . File ) > 0 :
2016-01-30 21:27:08 +00:00
fileName := requestedBp . File
if runtime . GOOS == "windows" {
// Accept fileName which is case-insensitive and slash-insensitive match
fileNameNormalized := strings . ToLower ( filepath . ToSlash ( fileName ) )
2022-07-14 21:14:45 +00:00
caseInsensitiveSearch :
for _ , t := range d . target . Targets ( ) {
for _ , symFile := range t . BinInfo ( ) . Sources {
if fileNameNormalized == strings . ToLower ( filepath . ToSlash ( symFile ) ) {
fileName = symFile
break caseInsensitiveSearch
}
2016-01-30 21:27:08 +00:00
}
}
}
2022-07-14 21:14:45 +00:00
addrs , err = proc . FindFileLocation ( d . target . Selected , fileName , requestedBp . Line )
2015-06-13 04:47:30 +00:00
case len ( requestedBp . FunctionName ) > 0 :
2022-07-14 21:14:45 +00:00
addrs , err = proc . FindFunctionLocation ( d . target . Selected , requestedBp . FunctionName , requestedBp . Line )
2019-11-04 16:43:12 +00:00
case len ( requestedBp . Addrs ) > 0 :
addrs = requestedBp . Addrs
2015-06-13 04:47:30 +00:00
default :
2019-11-01 19:41:06 +00:00
addrs = [ ] uint64 { requestedBp . Addr }
2015-08-07 16:50:14 +00:00
}
if err != nil {
return nil , err
2015-06-13 04:47:30 +00:00
}
2015-03-20 21:11:11 +00:00
2021-04-12 21:59:43 +00:00
createdBp , err := createLogicalBreakpoint ( d , addrs , requestedBp , 0 )
2015-06-13 04:47:30 +00:00
if err != nil {
return nil , err
}
2019-11-01 19:41:06 +00:00
d . log . Infof ( "created breakpoint: %#v" , createdBp )
return createdBp , nil
}
2022-05-25 20:58:26 +00:00
func ( d * Debugger ) convertBreakpoint ( lbp * proc . LogicalBreakpoint ) * api . Breakpoint {
abp := api . ConvertLogicalBreakpoint ( lbp )
api . ConvertPhysicalBreakpoints ( abp , d . findBreakpoint ( lbp . LogicalID ) )
return abp
}
func ( d * Debugger ) ConvertThreadBreakpoint ( thread proc . Thread ) * api . Breakpoint {
if b := thread . Breakpoint ( ) ; b . Active && b . Breakpoint . Logical != nil {
return d . convertBreakpoint ( b . Breakpoint . Logical )
}
return nil
}
2019-11-01 19:41:06 +00:00
// createLogicalBreakpoint creates one physical breakpoint for each address
// in addrs and associates all of them with the same logical breakpoint.
2021-04-12 21:59:43 +00:00
func createLogicalBreakpoint ( d * Debugger , addrs [ ] uint64 , requestedBp * api . Breakpoint , id int ) ( * api . Breakpoint , error ) {
2022-07-14 21:14:45 +00:00
if len ( d . target . Targets ( ) ) != 1 {
panic ( "multiple targets not implemented" )
}
p := d . target . Selected
2021-03-19 18:02:23 +00:00
2022-05-25 20:58:26 +00:00
if lbp := p . Breakpoints ( ) . Logical [ requestedBp . ID ] ; lbp != nil {
abp := d . convertBreakpoint ( lbp )
return abp , proc . BreakpointExistsError { File : lbp . File , Line : lbp . Line }
}
var lbp * proc . LogicalBreakpoint
if id <= 0 {
d . breakpointIDCounter ++
id = d . breakpointIDCounter
lbp = & proc . LogicalBreakpoint { LogicalID : id , HitCount : make ( map [ int ] uint64 ) , Enabled : true }
p . Breakpoints ( ) . Logical [ id ] = lbp
}
err := copyLogicalBreakpointInfo ( lbp , requestedBp )
if err != nil {
return nil , err
2021-03-19 18:02:23 +00:00
}
2019-11-01 19:41:06 +00:00
bps := make ( [ ] * proc . Breakpoint , len ( addrs ) )
for i := range addrs {
2022-02-25 09:09:53 +00:00
bps [ i ] , err = p . SetBreakpoint ( id , addrs [ i ] , proc . UserBreakpoint , nil )
2019-11-01 19:41:06 +00:00
if err != nil {
break
}
}
if err != nil {
2022-05-25 20:58:26 +00:00
delete ( p . Breakpoints ( ) . Logical , id )
2020-02-25 20:29:20 +00:00
if isBreakpointExistsErr ( err ) {
return nil , err
}
2019-11-01 19:41:06 +00:00
for _ , bp := range bps {
if bp == nil {
continue
}
2021-09-29 10:01:37 +00:00
if err1 := p . ClearBreakpoint ( bp . Addr ) ; err1 != nil {
2019-11-01 19:41:06 +00:00
err = fmt . Errorf ( "error while creating breakpoint: %v, additionally the breakpoint could not be properly rolled back: %v" , err , err1 )
return nil , err
}
2016-01-24 08:16:45 +00:00
}
return nil , err
}
2021-03-19 18:02:23 +00:00
2022-05-25 20:58:26 +00:00
return d . convertBreakpoint ( bps [ 0 ] . Logical ) , nil
}
func ( d * Debugger ) createPhysicalBreakpoints ( lbp * proc . LogicalBreakpoint ) error {
2022-07-14 21:14:45 +00:00
if len ( d . target . Targets ( ) ) != 1 {
panic ( "multiple targets not implemented" )
}
p := d . target . Selected
addrs , err := proc . FindFileLocation ( p , lbp . File , lbp . Line )
2022-05-25 20:58:26 +00:00
if err != nil {
return err
}
bps := make ( [ ] * proc . Breakpoint , len ( addrs ) )
for i := range addrs {
2022-07-14 21:14:45 +00:00
bps [ i ] , err = p . SetBreakpoint ( lbp . LogicalID , addrs [ i ] , proc . UserBreakpoint , nil )
2022-05-25 20:58:26 +00:00
if err != nil {
break
}
}
if err != nil {
if isBreakpointExistsErr ( err ) {
return err
}
for _ , bp := range bps {
if bp == nil {
continue
}
2022-07-14 21:14:45 +00:00
if err1 := p . ClearBreakpoint ( bp . Addr ) ; err1 != nil {
2022-05-25 20:58:26 +00:00
return fmt . Errorf ( "error while creating breakpoint: %v, additionally the breakpoint could not be properly rolled back: %v" , err , err1 )
}
}
return err
}
return nil
}
func ( d * Debugger ) clearPhysicalBreakpoints ( id int ) error {
2022-07-14 21:14:45 +00:00
if len ( d . target . Targets ( ) ) != 1 {
panic ( "multiple targets not implemented" )
}
p := d . target . Selected
2022-05-25 20:58:26 +00:00
var errs [ ] error
n := 0
2022-07-14 21:14:45 +00:00
for _ , bp := range p . Breakpoints ( ) . M {
2022-05-25 20:58:26 +00:00
if bp . LogicalID ( ) == id {
n ++
2022-07-14 21:14:45 +00:00
err := p . ClearBreakpoint ( bp . Addr )
2022-05-25 20:58:26 +00:00
if err != nil {
errs = append ( errs , err )
}
}
}
if len ( errs ) > 0 {
buf := new ( bytes . Buffer )
for i , err := range errs {
fmt . Fprintf ( buf , "%s" , err )
if i != len ( errs ) - 1 {
fmt . Fprintf ( buf , ", " )
}
}
if len ( errs ) == n {
return fmt . Errorf ( "unable to clear breakpoint %d: %v" , id , buf . String ( ) )
}
return fmt . Errorf ( "unable to clear breakpoint %d (partial): %s" , id , buf . String ( ) )
}
return nil
2015-03-20 21:11:11 +00:00
}
2020-02-25 20:29:20 +00:00
func isBreakpointExistsErr ( err error ) bool {
_ , r := err . ( proc . BreakpointExistsError )
return r
}
2021-07-31 15:16:26 +00:00
func ( d * Debugger ) CreateEBPFTracepoint ( fnName string ) error {
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2022-07-14 21:14:45 +00:00
if len ( d . target . Targets ( ) ) != 1 {
panic ( "multiple targets not implemented" )
}
p := d . target . Selected
return p . SetEBPFTracepoint ( fnName )
2021-07-31 15:16:26 +00:00
}
2022-01-06 17:00:46 +00:00
// amendBreakpoint will update the breakpoint with the matching ID.
2021-03-19 18:02:23 +00:00
// It also enables or disables the breakpoint.
2022-01-06 17:00:46 +00:00
// We can consume this function to avoid locking a goroutine.
func ( d * Debugger ) amendBreakpoint ( amend * api . Breakpoint ) error {
2022-07-14 21:14:45 +00:00
if len ( d . target . Targets ( ) ) != 1 {
panic ( "multiple targets not implemented" )
}
original := d . target . LogicalBreakpoints [ amend . ID ]
2022-05-25 20:58:26 +00:00
if original == nil {
2016-01-24 08:16:45 +00:00
return fmt . Errorf ( "no breakpoint with ID %d" , amend . ID )
}
2022-05-25 20:58:26 +00:00
if amend . Disabled && original . Enabled {
original . Enabled = false
err := copyLogicalBreakpointInfo ( original , amend )
2021-03-19 18:02:23 +00:00
if err != nil {
return err
}
2022-05-25 20:58:26 +00:00
return d . clearPhysicalBreakpoints ( amend . ID )
2021-03-19 18:02:23 +00:00
}
2022-05-25 20:58:26 +00:00
if ! amend . Disabled && ! original . Enabled {
original . Enabled = true
copyLogicalBreakpointInfo ( original , amend )
return d . createPhysicalBreakpoints ( original )
2019-11-01 19:41:06 +00:00
}
2021-03-19 18:02:23 +00:00
2022-05-25 20:58:26 +00:00
err := copyLogicalBreakpointInfo ( original , amend )
if err != nil {
return err
}
for _ , bp := range d . findBreakpoint ( amend . ID ) {
bp . UserBreaklet ( ) . Cond = original . Cond
}
2019-11-01 19:41:06 +00:00
return nil
2016-01-24 08:16:45 +00:00
}
2022-01-06 17:00:46 +00:00
// AmendBreakpoint will update the breakpoint with the matching ID.
// It also enables or disables the breakpoint.
func ( d * Debugger ) AmendBreakpoint ( amend * api . Breakpoint ) error {
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
return d . amendBreakpoint ( amend )
}
2018-08-31 18:08:18 +00:00
// CancelNext will clear internal breakpoints, thus cancelling the 'next',
// 'step' or 'stepout' operation.
2016-04-24 23:20:02 +00:00
func ( d * Debugger ) CancelNext ( ) error {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2021-07-21 15:24:19 +00:00
return d . target . ClearSteppingBreakpoints ( )
2016-04-24 23:20:02 +00:00
}
2022-05-25 20:58:26 +00:00
func copyLogicalBreakpointInfo ( lbp * proc . LogicalBreakpoint , requested * api . Breakpoint ) error {
lbp . Name = requested . Name
lbp . Tracepoint = requested . Tracepoint
lbp . TraceReturn = requested . TraceReturn
lbp . Goroutine = requested . Goroutine
lbp . Stacktrace = requested . Stacktrace
lbp . Variables = requested . Variables
lbp . LoadArgs = api . LoadConfigToProc ( requested . LoadArgs )
lbp . LoadLocals = api . LoadConfigToProc ( requested . LoadLocals )
lbp . UserData = requested . UserData
lbp . Cond = nil
if requested . Cond != "" {
var err error
lbp . Cond , err = parser . ParseExpr ( requested . Cond )
if err != nil {
return err
2021-05-28 18:21:53 +00:00
}
}
2022-05-25 20:58:26 +00:00
lbp . HitCond = nil
if requested . HitCond != "" {
opTok , val , err := parseHitCondition ( requested . HitCond )
if err != nil {
return err
}
lbp . HitCond = & struct {
Op token . Token
Val int
} { opTok , val }
2022-07-12 08:31:34 +00:00
lbp . HitCondPerG = requested . HitCondPerG
2022-05-25 20:58:26 +00:00
}
return nil
2016-01-24 08:16:45 +00:00
}
2021-05-28 18:21:53 +00:00
func parseHitCondition ( hitCond string ) ( token . Token , int , error ) {
// A hit condition can be in the following formats:
// - "number"
// - "OP number"
hitConditionRegex := regexp . MustCompile ( ` ((=|>|<|%|!)+|)( |)((\d|_)+) ` )
match := hitConditionRegex . FindStringSubmatch ( strings . TrimSpace ( hitCond ) )
if match == nil || len ( match ) != 6 {
return 0 , 0 , fmt . Errorf ( "unable to parse breakpoint hit condition: %q\nhit conditions should be of the form \"number\" or \"OP number\"" , hitCond )
}
opStr := match [ 1 ]
var opTok token . Token
switch opStr {
case "==" , "" :
opTok = token . EQL
case ">=" :
opTok = token . GEQ
case "<=" :
opTok = token . LEQ
case ">" :
opTok = token . GTR
case "<" :
opTok = token . LSS
case "%" :
opTok = token . REM
case "!=" :
opTok = token . NEQ
default :
return 0 , 0 , fmt . Errorf ( "unable to parse breakpoint hit condition: %q\ninvalid operator: %q" , hitCond , opStr )
}
numStr := match [ 4 ]
val , parseErr := strconv . Atoi ( numStr )
if parseErr != nil {
return 0 , 0 , fmt . Errorf ( "unable to parse breakpoint hit condition: %q\ninvalid number: %q" , hitCond , numStr )
}
return opTok , val , nil
}
2016-01-10 08:57:52 +00:00
// ClearBreakpoint clears a breakpoint.
2015-06-12 19:32:32 +00:00
func ( d * Debugger ) ClearBreakpoint ( requestedBp * api . Breakpoint ) ( * api . Breakpoint , error ) {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2021-03-19 18:02:23 +00:00
return d . clearBreakpoint ( requestedBp )
}
// clearBreakpoint clears a breakpoint, we can consume this function to avoid locking a goroutine
func ( d * Debugger ) clearBreakpoint ( requestedBp * api . Breakpoint ) ( * api . Breakpoint , error ) {
2022-07-14 21:14:45 +00:00
if len ( d . target . Targets ( ) ) != 1 {
panic ( "multiple targets not implemented" )
}
p := d . target . Selected
2022-05-25 20:58:26 +00:00
if requestedBp . ID <= 0 {
2022-07-14 21:14:45 +00:00
bp := p . Breakpoints ( ) . M [ requestedBp . Addr ]
2022-05-25 20:58:26 +00:00
requestedBp . ID = bp . LogicalID ( )
2021-03-19 18:02:23 +00:00
}
2016-02-15 14:39:29 +00:00
2022-07-14 21:14:45 +00:00
lbp := d . target . LogicalBreakpoints [ requestedBp . ID ]
2022-05-25 20:58:26 +00:00
clearedBp := d . convertBreakpoint ( lbp )
2020-03-24 09:47:36 +00:00
2022-07-14 21:14:45 +00:00
delete ( d . target . LogicalBreakpoints , requestedBp . ID )
2020-03-24 09:47:36 +00:00
2022-05-25 20:58:26 +00:00
err := d . clearPhysicalBreakpoints ( requestedBp . ID )
if err != nil {
return nil , err
2020-03-24 09:47:36 +00:00
}
2018-06-14 18:12:11 +00:00
d . log . Infof ( "cleared breakpoint: %#v" , clearedBp )
2021-09-29 10:01:37 +00:00
return clearedBp , nil
2015-03-20 21:11:11 +00:00
}
2022-01-06 17:00:46 +00:00
// isBpHitCondNotSatisfiable returns true if the breakpoint bp has a hit
// condition that is no more satisfiable.
// The hit condition is considered no more satisfiable if it can no longer be
// hit again, for example with {Op: "==", Val: 1} and TotalHitCount == 1.
func isBpHitCondNotSatisfiable ( bp * api . Breakpoint ) bool {
if bp . HitCond == "" {
return false
}
tok , val , err := parseHitCondition ( bp . HitCond )
if err != nil {
return false
}
switch tok {
case token . EQL , token . LEQ :
if int ( bp . TotalHitCount ) >= val {
return true
}
case token . LSS :
if int ( bp . TotalHitCount ) >= val - 1 {
return true
}
}
return false
}
2016-01-10 08:57:52 +00:00
// Breakpoints returns the list of current breakpoints.
2021-08-09 17:41:25 +00:00
func ( d * Debugger ) Breakpoints ( all bool ) [ ] * api . Breakpoint {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2022-07-14 21:14:45 +00:00
if len ( d . target . Targets ( ) ) != 1 {
panic ( "multiple targets not implemented" )
}
p := d . target . Selected
2021-03-19 18:02:23 +00:00
2022-05-25 20:58:26 +00:00
abps := [ ] * api . Breakpoint { }
if all {
2022-07-14 21:14:45 +00:00
for _ , bp := range p . Breakpoints ( ) . M {
2022-05-25 20:58:26 +00:00
var abp * api . Breakpoint
if bp . Logical != nil {
abp = api . ConvertLogicalBreakpoint ( bp . Logical )
} else {
abp = & api . Breakpoint { }
}
api . ConvertPhysicalBreakpoints ( abp , [ ] * proc . Breakpoint { bp } )
2021-08-09 17:41:25 +00:00
abp . VerboseDescr = bp . VerboseDescr ( )
2022-05-25 20:58:26 +00:00
abps = append ( abps , abp )
2021-08-09 17:41:25 +00:00
}
2022-05-25 20:58:26 +00:00
} else {
2022-07-14 21:14:45 +00:00
for _ , lbp := range d . target . LogicalBreakpoints {
2022-05-25 20:58:26 +00:00
abps = append ( abps , d . convertBreakpoint ( lbp ) )
2015-03-20 21:11:11 +00:00
}
2015-06-13 04:47:30 +00:00
}
2022-05-25 20:58:26 +00:00
return abps
2015-03-20 21:11:11 +00:00
}
2016-01-10 08:57:52 +00:00
// FindBreakpoint returns the breakpoint specified by 'id'.
2015-06-12 19:32:32 +00:00
func ( d * Debugger ) FindBreakpoint ( id int ) * api . Breakpoint {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2022-07-14 21:14:45 +00:00
lbp := d . target . LogicalBreakpoints [ id ]
2022-05-25 20:58:26 +00:00
if lbp == nil {
2016-01-24 08:16:45 +00:00
return nil
}
2022-05-25 20:58:26 +00:00
return d . convertBreakpoint ( lbp )
2016-01-24 08:16:45 +00:00
}
2019-11-01 19:41:06 +00:00
func ( d * Debugger ) findBreakpoint ( id int ) [ ] * proc . Breakpoint {
2022-07-14 21:14:45 +00:00
if len ( d . target . Targets ( ) ) != 1 {
panic ( "multiple targets not implemented" )
}
p := d . target . Selected
2019-11-01 19:41:06 +00:00
var bps [ ] * proc . Breakpoint
2022-07-14 21:14:45 +00:00
for _ , bp := range p . Breakpoints ( ) . M {
2021-09-29 10:01:37 +00:00
if bp . LogicalID ( ) == id {
2019-11-01 19:41:06 +00:00
bps = append ( bps , bp )
2015-03-20 21:11:11 +00:00
}
}
2019-11-01 19:41:06 +00:00
return bps
2015-03-20 21:11:11 +00:00
}
2016-01-24 08:16:45 +00:00
// FindBreakpointByName returns the breakpoint specified by 'name'
func ( d * Debugger ) FindBreakpointByName ( name string ) * api . Breakpoint {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2022-05-25 20:58:26 +00:00
return d . findBreakpointByName ( name )
2016-02-15 14:39:29 +00:00
}
func ( d * Debugger ) findBreakpointByName ( name string ) * api . Breakpoint {
2022-07-14 21:14:45 +00:00
for _ , lbp := range d . target . LogicalBreakpoints {
2022-05-25 20:58:26 +00:00
if lbp . Name == name {
return d . convertBreakpoint ( lbp )
2021-03-19 18:02:23 +00:00
}
}
return nil
}
2021-05-20 17:04:02 +00:00
// CreateWatchpoint creates a watchpoint on the specified expression.
func ( d * Debugger ) CreateWatchpoint ( goid , frame , deferredCall int , expr string , wtype api . WatchType ) ( * api . Breakpoint , error ) {
2022-07-14 21:14:45 +00:00
if len ( d . target . Targets ( ) ) != 1 {
panic ( "multiple targets not implemented" )
}
p := d . target . Selected
s , err := proc . ConvertEvalScope ( p , goid , frame , deferredCall )
2021-05-20 17:04:02 +00:00
if err != nil {
return nil , err
}
2022-02-25 09:09:53 +00:00
d . breakpointIDCounter ++
2022-07-14 21:14:45 +00:00
bp , err := p . SetWatchpoint ( d . breakpointIDCounter , s , expr , proc . WatchType ( wtype ) , nil )
2021-05-20 17:04:02 +00:00
if err != nil {
return nil , err
}
2021-05-28 18:21:53 +00:00
if d . findBreakpointByName ( expr ) == nil {
2022-05-25 20:58:26 +00:00
bp . Logical . Name = expr
2021-05-20 17:04:02 +00:00
}
2022-05-25 20:58:26 +00:00
return d . convertBreakpoint ( bp . Logical ) , nil
2021-05-20 17:04:02 +00:00
}
2016-01-10 08:57:52 +00:00
// Threads returns the threads of the target process.
2020-07-07 13:21:18 +00:00
func ( d * Debugger ) Threads ( ) ( [ ] proc . Thread , error ) {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2016-02-15 14:39:29 +00:00
2018-06-21 11:07:37 +00:00
if _ , err := d . target . Valid ( ) ; err != nil {
return nil , err
2016-01-30 06:23:14 +00:00
}
2017-07-26 22:57:47 +00:00
2020-07-07 13:21:18 +00:00
return d . target . ThreadList ( ) , nil
2015-03-20 21:11:11 +00:00
}
2016-01-10 08:57:52 +00:00
// FindThread returns the thread for the given 'id'.
2020-07-07 13:21:18 +00:00
func ( d * Debugger ) FindThread ( id int ) ( proc . Thread , error ) {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2016-02-15 14:39:29 +00:00
2018-06-21 11:07:37 +00:00
if _ , err := d . target . Valid ( ) ; err != nil {
return nil , err
2016-01-30 06:23:14 +00:00
}
2016-02-15 14:39:29 +00:00
2017-02-15 13:41:03 +00:00
for _ , th := range d . target . ThreadList ( ) {
if th . ThreadID ( ) == id {
2020-07-07 13:21:18 +00:00
return th , nil
2015-03-20 21:11:11 +00:00
}
}
2016-01-30 06:23:14 +00:00
return nil , nil
2015-03-20 21:11:11 +00:00
}
2021-05-17 16:25:41 +00:00
// FindGoroutine returns the goroutine for the given 'id'.
func ( d * Debugger ) FindGoroutine ( id int ) ( * proc . G , error ) {
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2022-07-14 21:14:45 +00:00
return proc . FindGoroutine ( d . target . Selected , id )
2021-05-17 16:25:41 +00:00
}
2018-06-21 11:07:37 +00:00
func ( d * Debugger ) setRunning ( running bool ) {
d . runningMutex . Lock ( )
d . running = running
d . runningMutex . Unlock ( )
}
2021-05-04 19:49:52 +00:00
func ( d * Debugger ) IsRunning ( ) bool {
2018-06-21 11:07:37 +00:00
d . runningMutex . Lock ( )
defer d . runningMutex . Unlock ( )
return d . running
}
2015-06-20 22:50:24 +00:00
// Command handles commands which control the debugger lifecycle
2021-03-08 18:05:10 +00:00
func ( d * Debugger ) Command ( command * api . DebuggerCommand , resumeNotify chan struct { } ) ( * api . DebuggerState , error ) {
2015-03-20 21:11:11 +00:00
var err error
2016-02-15 14:39:29 +00:00
if command . Name == api . Halt {
// RequestManualStop does not invoke any ptrace syscalls, so it's safe to
// access the process directly.
2018-06-14 18:12:11 +00:00
d . log . Debug ( "halting" )
2020-03-24 16:09:28 +00:00
d . recordMutex . Lock ( )
if d . stopRecording == nil {
err = d . target . RequestManualStop ( )
2021-09-24 22:18:28 +00:00
// The error returned from d.target.Valid will have more context
// about the exited process.
if _ , valErr := d . target . Valid ( ) ; valErr != nil {
err = valErr
}
2020-03-24 16:09:28 +00:00
}
d . recordMutex . Unlock ( )
2016-02-15 14:39:29 +00:00
}
2017-06-06 12:59:52 +00:00
withBreakpointInfo := true
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2016-02-15 14:39:29 +00:00
2018-06-21 11:07:37 +00:00
d . setRunning ( true )
defer d . setRunning ( false )
2021-03-08 18:05:10 +00:00
if command . Name != api . SwitchGoroutine && command . Name != api . SwitchThread && command . Name != api . Halt {
d . target . ResumeNotify ( resumeNotify )
} else if resumeNotify != nil {
close ( resumeNotify )
}
2015-03-20 21:11:11 +00:00
switch command . Name {
case api . Continue :
2018-06-14 18:12:11 +00:00
d . log . Debug ( "continuing" )
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 := d . target . ChangeDirection ( proc . Forward ) ; err != nil {
return nil , err
}
2020-03-24 16:16:58 +00:00
err = d . target . Continue ( )
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
case api . DirectionCongruentContinue :
d . log . Debug ( "continuing (direction congruent)" )
2020-03-24 16:16:58 +00:00
err = d . target . Continue ( )
2018-05-04 17:31:45 +00:00
case api . Call :
d . log . Debugf ( "function call %s" , command . Expr )
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 := d . target . ChangeDirection ( proc . Forward ) ; err != nil {
return nil , err
}
2019-05-09 15:29:58 +00:00
if command . ReturnInfoLoadConfig == nil {
return nil , errors . New ( "can not call function with nil ReturnInfoLoadConfig" )
}
2022-07-14 21:14:45 +00:00
g := d . target . Selected . SelectedGoroutine ( )
2019-06-30 17:44:30 +00:00
if command . GoroutineID > 0 {
2022-07-14 21:14:45 +00:00
g , err = proc . FindGoroutine ( d . target . Selected , command . GoroutineID )
2019-06-30 17:44:30 +00:00
if err != nil {
return nil , err
}
}
err = proc . EvalExpressionWithCalls ( d . target , g , command . Expr , * api . LoadConfigToProc ( command . ReturnInfoLoadConfig ) , ! command . UnsafeCall )
2017-05-05 22:17:52 +00:00
case api . Rewind :
2018-06-14 18:12:11 +00:00
d . log . Debug ( "rewinding" )
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 := d . target . ChangeDirection ( proc . Backward ) ; err != nil {
2017-05-05 22:17:52 +00:00
return nil , err
}
2020-03-24 16:16:58 +00:00
err = d . target . Continue ( )
2015-03-20 21:11:11 +00:00
case api . Next :
2018-06-14 18:12:11 +00:00
d . log . Debug ( "nexting" )
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 := d . target . ChangeDirection ( proc . Forward ) ; err != nil {
return nil , err
}
2020-03-24 16:16:58 +00:00
err = d . target . Next ( )
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
case api . ReverseNext :
d . log . Debug ( "reverse nexting" )
if err := d . target . ChangeDirection ( proc . Backward ) ; err != nil {
return nil , err
}
2020-03-24 16:16:58 +00:00
err = d . target . Next ( )
2015-03-20 21:11:11 +00:00
case api . Step :
2018-06-14 18:12:11 +00:00
d . log . Debug ( "stepping" )
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 := d . target . ChangeDirection ( proc . Forward ) ; err != nil {
return nil , err
}
2020-03-24 16:16:58 +00:00
err = d . target . Step ( )
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
case api . ReverseStep :
d . log . Debug ( "reverse stepping" )
if err := d . target . ChangeDirection ( proc . Backward ) ; err != nil {
return nil , err
}
2020-03-24 16:16:58 +00:00
err = d . target . Step ( )
2016-01-24 05:25:46 +00:00
case api . StepInstruction :
2018-06-14 18:12:11 +00:00
d . log . Debug ( "single stepping" )
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 := d . target . ChangeDirection ( proc . Forward ) ; err != nil {
return nil , err
}
2020-03-24 16:16:58 +00:00
err = d . target . StepInstruction ( )
2019-07-09 01:01:00 +00:00
case api . ReverseStepInstruction :
d . log . Debug ( "reverse single stepping" )
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 := d . target . ChangeDirection ( proc . Backward ) ; err != nil {
2019-07-09 01:01:00 +00:00
return nil , err
}
2020-03-24 16:16:58 +00:00
err = d . target . StepInstruction ( )
2016-04-14 09:58:59 +00:00
case api . StepOut :
2018-06-14 18:12:11 +00:00
d . log . Debug ( "step out" )
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 := d . target . ChangeDirection ( proc . Forward ) ; err != nil {
return nil , err
}
2020-03-24 16:16:58 +00:00
err = d . target . StepOut ( )
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
case api . ReverseStepOut :
d . log . Debug ( "reverse step out" )
if err := d . target . ChangeDirection ( proc . Backward ) ; err != nil {
return nil , err
}
2020-03-24 16:16:58 +00:00
err = d . target . StepOut ( )
2015-03-20 21:11:11 +00:00
case api . SwitchThread :
2018-06-14 18:12:11 +00:00
d . log . Debugf ( "switching to thread %d" , command . ThreadID )
2022-07-14 21:14:45 +00:00
err = d . target . Selected . SwitchThread ( command . ThreadID )
2017-06-06 12:59:52 +00:00
withBreakpointInfo = false
2015-08-28 20:06:29 +00:00
case api . SwitchGoroutine :
2018-06-14 18:12:11 +00:00
d . log . Debugf ( "switching to goroutine %d" , command . GoroutineID )
2021-04-21 20:39:19 +00:00
var g * proc . G
2022-07-14 21:14:45 +00:00
g , err = proc . FindGoroutine ( d . target . Selected , command . GoroutineID )
2020-01-21 20:41:24 +00:00
if err == nil {
2022-07-14 21:14:45 +00:00
err = d . target . Selected . SwitchGoroutine ( g )
2020-01-21 20:41:24 +00:00
}
2017-06-06 12:59:52 +00:00
withBreakpointInfo = false
2015-03-20 21:11:11 +00:00
case api . Halt :
2016-02-25 01:50:11 +00:00
// RequestManualStop already called
2017-06-06 12:59:52 +00:00
withBreakpointInfo = false
2015-03-20 21:11:11 +00:00
}
2017-06-06 12:59:52 +00:00
2015-03-20 21:11:11 +00:00
if err != nil {
2021-06-22 11:35:13 +00:00
if pe , ok := err . ( proc . ErrProcessExited ) ; ok && command . Name != api . SwitchGoroutine && command . Name != api . SwitchThread {
2017-06-06 12:59:52 +00:00
state := & api . DebuggerState { }
2022-07-14 21:14:45 +00:00
state . Pid = d . target . Selected . Pid ( )
2017-06-06 12:59:52 +00:00
state . Exited = true
2021-06-22 11:35:13 +00:00
state . ExitStatus = pe . Status
state . Err = pe
2017-06-06 12:59:52 +00:00
return state , nil
}
2015-06-21 18:08:14 +00:00
return nil , err
2015-03-20 21:11:11 +00:00
}
2018-05-11 12:51:15 +00:00
state , stateErr := d . state ( api . LoadConfigToProc ( command . ReturnInfoLoadConfig ) )
2017-06-06 12:59:52 +00:00
if stateErr != nil {
return state , stateErr
}
if withBreakpointInfo {
err = d . collectBreakpointInformation ( state )
}
2018-10-16 15:49:20 +00:00
for _ , th := range state . Threads {
if th . Breakpoint != nil && th . Breakpoint . TraceReturn {
for _ , v := range th . BreakpointInfo . Arguments {
if ( v . Flags & api . VariableReturnArgument ) != 0 {
th . ReturnValues = append ( th . ReturnValues , v )
}
}
}
}
2022-01-06 17:00:46 +00:00
if bp := state . CurrentThread . Breakpoint ; bp != nil && isBpHitCondNotSatisfiable ( bp ) {
bp . Disabled = true
d . amendBreakpoint ( bp )
}
2017-06-06 12:59:52 +00:00
return state , err
2015-03-20 21:11:11 +00:00
}
2015-06-28 15:00:56 +00:00
func ( d * Debugger ) collectBreakpointInformation ( state * api . DebuggerState ) error {
2022-07-14 21:14:45 +00:00
//TODO(aarzilli): this doesn't work when there are multiple targets because the state.Threads slice will contain threads from all targets, not just d.target .Selected
2015-10-29 09:59:22 +00:00
if state == nil {
2015-06-28 15:00:56 +00:00
return nil
}
2015-10-29 09:59:22 +00:00
for i := range state . Threads {
2018-05-04 17:31:45 +00:00
if state . Threads [ i ] . Breakpoint == nil || state . Threads [ i ] . BreakpointInfo != nil {
2015-10-29 09:59:22 +00:00
continue
}
bp := state . Threads [ i ] . Breakpoint
bpi := & api . BreakpointInfo { }
state . Threads [ i ] . BreakpointInfo = bpi
2015-06-28 15:00:56 +00:00
2015-10-29 09:59:22 +00:00
if bp . Goroutine {
2022-07-14 21:14:45 +00:00
g , err := proc . GetG ( d . target . Selected . CurrentThread ( ) )
2015-10-29 09:59:22 +00:00
if err != nil {
return err
}
2022-07-14 21:14:45 +00:00
bpi . Goroutine = api . ConvertGoroutine ( d . target . Selected , g )
2015-06-28 15:00:56 +00:00
}
2015-10-29 09:59:22 +00:00
if bp . Stacktrace > 0 {
2022-07-14 21:14:45 +00:00
rawlocs , err := proc . ThreadStacktrace ( d . target . Selected . CurrentThread ( ) , bp . Stacktrace )
2015-10-29 09:59:22 +00:00
if err != nil {
return err
}
2016-04-24 17:15:39 +00:00
bpi . Stacktrace , err = d . convertStacktrace ( rawlocs , nil )
2015-10-29 09:59:22 +00:00
if err != nil {
return err
}
2015-06-28 15:00:56 +00:00
}
2015-10-29 09:59:22 +00:00
2022-07-14 21:14:45 +00:00
thread , found := d . target . Selected . FindThread ( state . Threads [ i ] . ID )
2017-02-15 13:41:03 +00:00
if ! found {
return fmt . Errorf ( "could not find thread %d" , state . Threads [ i ] . ID )
}
2017-06-23 12:11:51 +00:00
if len ( bp . Variables ) == 0 && bp . LoadArgs == nil && bp . LoadLocals == nil {
// don't try to create goroutine scope if there is nothing to load
continue
}
2022-07-14 21:14:45 +00:00
s , err := proc . GoroutineScope ( d . target . Selected , thread )
2015-09-17 08:42:34 +00:00
if err != nil {
return err
}
2015-06-28 15:00:56 +00:00
2015-10-29 09:59:22 +00:00
if len ( bp . Variables ) > 0 {
bpi . Variables = make ( [ ] api . Variable , len ( bp . Variables ) )
}
for i := range bp . Variables {
2021-11-26 16:06:23 +00:00
v , err := s . EvalExpression ( bp . Variables [ i ] , proc . LoadConfig { FollowPointers : true , MaxVariableRecurse : 1 , MaxStringLen : 64 , MaxArrayValues : 64 , MaxStructFields : - 1 } )
2015-10-29 09:59:22 +00:00
if err != nil {
2017-06-23 12:11:51 +00:00
bpi . Variables [ i ] = api . Variable { Name : bp . Variables [ i ] , Unreadable : fmt . Sprintf ( "eval error: %v" , err ) }
} else {
bpi . Variables [ i ] = * api . ConvertVar ( v )
2015-10-29 09:59:22 +00:00
}
}
2016-04-24 17:15:39 +00:00
if bp . LoadArgs != nil {
if vars , err := s . FunctionArguments ( * api . LoadConfigToProc ( bp . LoadArgs ) ) ; err == nil {
2020-07-07 13:21:18 +00:00
bpi . Arguments = api . ConvertVars ( vars )
2016-04-24 17:15:39 +00:00
}
}
if bp . LoadLocals != nil {
if locals , err := s . LocalVariables ( * api . LoadConfigToProc ( bp . LoadLocals ) ) ; err == nil {
2020-07-07 13:21:18 +00:00
bpi . Locals = api . ConvertVars ( locals )
2016-04-24 17:15:39 +00:00
}
2015-06-28 15:00:56 +00:00
}
2015-07-12 20:18:14 +00:00
}
2015-10-29 09:59:22 +00:00
2015-06-28 15:00:56 +00:00
return nil
}
2016-01-10 08:57:52 +00:00
// Sources returns a list of the source files for target binary.
2015-03-20 21:11:11 +00:00
func ( d * Debugger ) Sources ( filter string ) ( [ ] string , error ) {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2016-02-15 14:39:29 +00:00
2015-03-20 21:11:11 +00:00
regex , err := regexp . Compile ( filter )
if err != nil {
return nil , fmt . Errorf ( "invalid filter argument: %s" , err . Error ( ) )
}
files := [ ] string { }
2022-07-14 21:14:45 +00:00
for _ , t := range d . target . Targets ( ) {
for _ , f := range t . BinInfo ( ) . Sources {
if regex . Match ( [ ] byte ( f ) ) {
files = append ( files , f )
}
2015-03-20 21:11:11 +00:00
}
2015-06-13 04:47:30 +00:00
}
2022-07-14 21:14:45 +00:00
sort . Strings ( files )
files = uniq ( files )
2015-03-20 21:11:11 +00:00
return files , nil
}
2022-07-14 21:14:45 +00:00
func uniq ( s [ ] string ) [ ] string {
if len ( s ) <= 0 {
return s
}
src , dst := 1 , 1
for src < len ( s ) {
if s [ src ] != s [ dst - 1 ] {
s [ dst ] = s [ src ]
dst ++
}
src ++
}
return s [ : dst ]
}
2016-01-10 08:57:52 +00:00
// Functions returns a list of functions in the target process.
2015-03-20 21:11:11 +00:00
func ( d * Debugger ) Functions ( filter string ) ( [ ] string , error ) {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2016-02-15 14:39:29 +00:00
2020-05-13 06:38:10 +00:00
regex , err := regexp . Compile ( filter )
if err != nil {
return nil , fmt . Errorf ( "invalid filter argument: %s" , err . Error ( ) )
}
funcs := [ ] string { }
2022-07-14 21:14:45 +00:00
for _ , t := range d . target . Targets ( ) {
for _ , f := range t . BinInfo ( ) . Functions {
if regex . MatchString ( f . Name ) {
funcs = append ( funcs , f . Name )
}
2020-05-13 06:38:10 +00:00
}
}
2022-07-14 21:14:45 +00:00
sort . Strings ( funcs )
funcs = uniq ( funcs )
2020-05-13 06:38:10 +00:00
return funcs , nil
2015-08-07 16:50:14 +00:00
}
2018-08-31 18:08:18 +00:00
// Types returns all type information in the binary.
2016-02-22 15:08:45 +00:00
func ( d * Debugger ) Types ( filter string ) ( [ ] string , error ) {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2016-02-22 15:08:45 +00:00
regex , err := regexp . Compile ( filter )
if err != nil {
return nil , fmt . Errorf ( "invalid filter argument: %s" , err . Error ( ) )
}
2022-07-14 21:14:45 +00:00
r := [ ] string { }
2016-02-22 15:08:45 +00:00
2022-07-14 21:14:45 +00:00
for _ , t := range d . target . Targets ( ) {
types , err := t . BinInfo ( ) . Types ( )
if err != nil {
return nil , err
}
for _ , typ := range types {
if regex . Match ( [ ] byte ( typ ) ) {
r = append ( r , typ )
}
2016-02-22 15:08:45 +00:00
}
}
2022-07-14 21:14:45 +00:00
sort . Strings ( r )
r = uniq ( r )
2016-02-22 15:08:45 +00:00
return r , nil
}
2016-01-10 08:57:52 +00:00
// PackageVariables returns a list of package variables for the thread,
// optionally regexp filtered using regexp described in 'filter'.
2020-09-24 15:02:29 +00:00
func ( d * Debugger ) PackageVariables ( filter string , cfg proc . LoadConfig ) ( [ ] * proc . Variable , error ) {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2022-07-14 21:14:45 +00:00
p := d . target . Selected
2016-02-15 14:39:29 +00:00
2015-03-20 21:11:11 +00:00
regex , err := regexp . Compile ( filter )
if err != nil {
return nil , fmt . Errorf ( "invalid filter argument: %s" , err . Error ( ) )
}
2022-07-14 21:14:45 +00:00
scope , err := proc . ThreadScope ( p , p . CurrentThread ( ) )
2015-08-28 20:06:29 +00:00
if err != nil {
return nil , err
}
2016-04-24 17:15:39 +00:00
pv , err := scope . PackageVariables ( cfg )
2015-06-13 04:47:30 +00:00
if err != nil {
return nil , err
}
2020-07-07 13:21:18 +00:00
pvr := pv [ : 0 ]
for i := range pv {
if regex . Match ( [ ] byte ( pv [ i ] . Name ) ) {
pvr = append ( pvr , pv [ i ] )
2015-03-20 21:11:11 +00:00
}
2015-06-13 04:47:30 +00:00
}
2020-07-07 13:21:18 +00:00
return pvr , nil
2015-03-20 21:11:11 +00:00
}
2020-07-07 13:21:18 +00:00
// ThreadRegisters returns registers of the specified thread.
func ( d * Debugger ) ThreadRegisters ( threadID int , floatingPoint bool ) ( * op . DwarfRegisters , error ) {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2016-02-15 14:39:29 +00:00
2022-07-14 21:14:45 +00:00
thread , found := d . target . Selected . FindThread ( threadID )
2020-07-07 13:21:18 +00:00
if ! found {
return nil , fmt . Errorf ( "couldn't find thread %d" , threadID )
2020-02-24 18:47:02 +00:00
}
2020-07-07 13:21:18 +00:00
regs , err := thread . Registers ( )
if err != nil {
return nil , err
2020-05-13 18:56:50 +00:00
}
2022-07-14 21:14:45 +00:00
return d . target . Selected . BinInfo ( ) . Arch . RegistersToDwarfRegisters ( 0 , regs ) , nil
2020-02-24 18:47:02 +00:00
}
2020-07-07 13:21:18 +00:00
// ScopeRegisters returns registers for the specified scope.
func ( d * Debugger ) ScopeRegisters ( goid , frame , deferredCall int , floatingPoint bool ) ( * op . DwarfRegisters , error ) {
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2020-02-24 18:47:02 +00:00
2022-07-14 21:14:45 +00:00
s , err := proc . ConvertEvalScope ( d . target . Selected , goid , frame , deferredCall )
2020-07-07 13:21:18 +00:00
if err != nil {
return nil , err
}
return & s . Regs , nil
2015-06-19 07:20:10 +00:00
}
2020-07-07 13:21:18 +00:00
// DwarfRegisterToString returns the name and value representation of the given register.
func ( d * Debugger ) DwarfRegisterToString ( i int , reg * op . DwarfRegister ) ( string , bool , string ) {
2022-07-14 21:14:45 +00:00
return d . target . Selected . BinInfo ( ) . Arch . DwarfRegisterToString ( i , reg )
2015-09-17 08:42:34 +00:00
}
2016-01-10 08:57:52 +00:00
// LocalVariables returns a list of the local variables.
2020-07-07 13:21:18 +00:00
func ( d * Debugger ) LocalVariables ( goid , frame , deferredCall int , cfg proc . LoadConfig ) ( [ ] * proc . Variable , error ) {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2016-02-15 14:39:29 +00:00
2022-07-14 21:14:45 +00:00
s , err := proc . ConvertEvalScope ( d . target . Selected , goid , frame , deferredCall )
2015-06-13 04:47:30 +00:00
if err != nil {
return nil , err
}
2020-07-07 13:21:18 +00:00
return s . LocalVariables ( cfg )
2015-05-08 20:16:22 +00:00
}
2016-01-10 08:57:52 +00:00
// FunctionArguments returns the arguments to the current function.
2020-07-07 13:21:18 +00:00
func ( d * Debugger ) FunctionArguments ( goid , frame , deferredCall int , cfg proc . LoadConfig ) ( [ ] * proc . Variable , error ) {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2016-02-15 14:39:29 +00:00
2022-07-14 21:14:45 +00:00
s , err := proc . ConvertEvalScope ( d . target . Selected , goid , frame , deferredCall )
2015-06-13 04:47:30 +00:00
if err != nil {
return nil , err
2015-03-20 21:11:11 +00:00
}
2020-07-07 13:21:18 +00:00
return s . FunctionArguments ( cfg )
2015-03-20 21:11:11 +00:00
}
2021-05-17 16:21:15 +00:00
// Function returns the current function.
func ( d * Debugger ) Function ( goid , frame , deferredCall int , cfg proc . LoadConfig ) ( * proc . Function , error ) {
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2022-07-14 21:14:45 +00:00
s , err := proc . ConvertEvalScope ( d . target . Selected , goid , frame , deferredCall )
2021-05-17 16:21:15 +00:00
if err != nil {
return nil , err
}
return s . Fn , nil
}
2016-01-10 08:57:52 +00:00
// EvalVariableInScope will attempt to evaluate the variable represented by 'symbol'
// in the scope provided.
2021-11-26 16:06:23 +00:00
func ( d * Debugger ) EvalVariableInScope ( goid , frame , deferredCall int , expr string , cfg proc . LoadConfig ) ( * proc . Variable , error ) {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2016-02-15 14:39:29 +00:00
2022-07-14 21:14:45 +00:00
s , err := proc . ConvertEvalScope ( d . target . Selected , goid , frame , deferredCall )
2015-08-28 20:06:29 +00:00
if err != nil {
return nil , err
2015-06-13 04:47:30 +00:00
}
2021-11-26 16:06:23 +00:00
return s . EvalExpression ( expr , cfg )
2015-03-20 21:11:11 +00:00
}
2021-06-10 16:34:20 +00:00
// LoadResliced will attempt to 'reslice' a map, array or slice so that the values
// up to cfg.MaxArrayValues children are loaded starting from index start.
func ( d * Debugger ) LoadResliced ( v * proc . Variable , start int , cfg proc . LoadConfig ) ( * proc . Variable , error ) {
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
return v . LoadResliced ( start , cfg )
}
2016-01-10 08:57:52 +00:00
// SetVariableInScope will set the value of the variable represented by
// 'symbol' to the value given, in the given scope.
2020-07-07 13:21:18 +00:00
func ( d * Debugger ) SetVariableInScope ( goid , frame , deferredCall int , symbol , value string ) error {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2016-02-15 14:39:29 +00:00
2022-07-14 21:14:45 +00:00
s , err := proc . ConvertEvalScope ( d . target . Selected , goid , frame , deferredCall )
2015-09-28 10:01:18 +00:00
if err != nil {
return err
}
return s . SetVariable ( symbol , value )
}
2016-01-10 08:57:52 +00:00
// Goroutines will return a list of goroutines in the target process.
2020-07-07 13:21:18 +00:00
func ( d * Debugger ) Goroutines ( start , count int ) ( [ ] * proc . G , int , error ) {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2022-07-14 21:14:45 +00:00
return proc . GoroutinesInfo ( d . target . Selected , start , count )
2015-03-20 21:11:11 +00:00
}
2015-06-17 17:11:57 +00:00
2021-07-01 18:25:33 +00:00
// FilterGoroutines returns the goroutines in gs that satisfy the specified filters.
func ( d * Debugger ) FilterGoroutines ( gs [ ] * proc . G , filters [ ] api . ListGoroutinesFilter ) [ ] * proc . G {
if len ( filters ) == 0 {
return gs
}
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
r := [ ] * proc . G { }
for _ , g := range gs {
ok := true
for i := range filters {
2022-07-14 21:14:45 +00:00
if ! matchGoroutineFilter ( d . target . Selected , g , & filters [ i ] ) {
2021-07-01 18:25:33 +00:00
ok = false
break
}
}
if ok {
r = append ( r , g )
}
}
return r
}
2021-07-08 15:47:53 +00:00
func matchGoroutineFilter ( tgt * proc . Target , g * proc . G , filter * api . ListGoroutinesFilter ) bool {
2021-07-01 18:25:33 +00:00
var val bool
switch filter . Kind {
default :
fallthrough
case api . GoroutineFieldNone :
val = true
case api . GoroutineCurrentLoc :
val = matchGoroutineLocFilter ( g . CurrentLoc , filter . Arg )
case api . GoroutineUserLoc :
val = matchGoroutineLocFilter ( g . UserCurrent ( ) , filter . Arg )
case api . GoroutineGoLoc :
val = matchGoroutineLocFilter ( g . Go ( ) , filter . Arg )
case api . GoroutineStartLoc :
2021-07-08 15:47:53 +00:00
val = matchGoroutineLocFilter ( g . StartLoc ( tgt ) , filter . Arg )
2021-07-01 18:25:33 +00:00
case api . GoroutineLabel :
idx := strings . Index ( filter . Arg , "=" )
if idx >= 0 {
val = g . Labels ( ) [ filter . Arg [ : idx ] ] == filter . Arg [ idx + 1 : ]
} else {
_ , val = g . Labels ( ) [ filter . Arg ]
}
case api . GoroutineRunning :
val = g . Thread != nil
case api . GoroutineUser :
2021-07-08 15:47:53 +00:00
val = ! g . System ( tgt )
2021-07-01 18:25:33 +00:00
}
if filter . Negated {
val = ! val
}
return val
}
func matchGoroutineLocFilter ( loc proc . Location , arg string ) bool {
return strings . Contains ( formatLoc ( loc ) , arg )
}
func formatLoc ( loc proc . Location ) string {
fnname := "?"
if loc . Fn != nil {
fnname = loc . Fn . Name
}
return fmt . Sprintf ( "%s:%d in %s" , loc . File , loc . Line , fnname )
}
// GroupGoroutines divides goroutines in gs into groups as specified by groupBy and groupByArg.
// A maximum of maxGoroutinesPerGroup are saved in each group, but the total
// number of goroutines in each group is recorded.
func ( d * Debugger ) GroupGoroutines ( gs [ ] * proc . G , group * api . GoroutineGroupingOptions ) ( [ ] * proc . G , [ ] api . GoroutineGroup , bool ) {
if group . GroupBy == api . GoroutineFieldNone {
return gs , nil , false
}
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
groupMembers := map [ string ] [ ] * proc . G { }
totals := map [ string ] int { }
for _ , g := range gs {
var key string
switch group . GroupBy {
case api . GoroutineCurrentLoc :
key = formatLoc ( g . CurrentLoc )
case api . GoroutineUserLoc :
key = formatLoc ( g . UserCurrent ( ) )
case api . GoroutineGoLoc :
key = formatLoc ( g . Go ( ) )
case api . GoroutineStartLoc :
2022-07-14 21:14:45 +00:00
key = formatLoc ( g . StartLoc ( d . target . Selected ) )
2021-07-01 18:25:33 +00:00
case api . GoroutineLabel :
key = fmt . Sprintf ( "%s=%s" , group . GroupByKey , g . Labels ( ) [ group . GroupByKey ] )
case api . GoroutineRunning :
key = fmt . Sprintf ( "running=%v" , g . Thread != nil )
case api . GoroutineUser :
2022-07-14 21:14:45 +00:00
key = fmt . Sprintf ( "user=%v" , ! g . System ( d . target . Selected ) )
2021-07-01 18:25:33 +00:00
}
if len ( groupMembers [ key ] ) < group . MaxGroupMembers {
groupMembers [ key ] = append ( groupMembers [ key ] , g )
}
totals [ key ] ++
}
keys := make ( [ ] string , 0 , len ( groupMembers ) )
for key := range groupMembers {
keys = append ( keys , key )
}
sort . Strings ( keys )
tooManyGroups := false
gsout := [ ] * proc . G { }
groups := [ ] api . GoroutineGroup { }
for _ , key := range keys {
if group . MaxGroups > 0 && len ( groups ) >= group . MaxGroups {
tooManyGroups = true
break
}
groups = append ( groups , api . GoroutineGroup { Name : key , Offset : len ( gsout ) , Count : len ( groupMembers [ key ] ) , Total : totals [ key ] } )
gsout = append ( gsout , groupMembers [ key ] ... )
}
return gsout , groups , tooManyGroups
}
2016-01-10 08:57:52 +00:00
// Stacktrace returns a list of Stackframes for the given goroutine. The
// length of the returned list will be min(stack_len, depth).
// If 'full' is true, then local vars, function args, etc will be returned as well.
2020-07-07 13:21:18 +00:00
func ( d * Debugger ) Stacktrace ( goroutineID , depth int , opts api . StacktraceOptions ) ( [ ] proc . Stackframe , error ) {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2016-02-15 14:39:29 +00:00
2018-06-21 11:07:37 +00:00
if _ , err := d . target . Valid ( ) ; err != nil {
return nil , err
2017-07-26 22:57:47 +00:00
}
2022-07-14 21:14:45 +00:00
g , err := proc . FindGoroutine ( d . target . Selected , goroutineID )
2015-09-20 06:45:45 +00:00
if err != nil {
return nil , err
}
2015-06-17 17:11:57 +00:00
2015-09-20 06:45:45 +00:00
if g == nil {
2022-07-14 21:14:45 +00:00
return proc . ThreadStacktrace ( d . target . Selected . CurrentThread ( ) , depth )
2015-09-20 06:45:45 +00:00
} else {
2020-07-07 13:21:18 +00:00
return g . Stacktrace ( depth , proc . StacktraceOptions ( opts ) )
2015-06-17 17:11:57 +00:00
}
2015-06-28 15:00:56 +00:00
}
2019-03-16 13:50:18 +00:00
// Ancestors returns the stacktraces for the ancestors of a goroutine.
func ( d * Debugger ) Ancestors ( goroutineID , numAncestors , depth int ) ( [ ] api . Ancestor , error ) {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2019-03-16 13:50:18 +00:00
if _ , err := d . target . Valid ( ) ; err != nil {
return nil , err
}
2022-07-14 21:14:45 +00:00
g , err := proc . FindGoroutine ( d . target . Selected , goroutineID )
2019-03-16 13:50:18 +00:00
if err != nil {
return nil , err
}
if g == nil {
return nil , errors . New ( "no selected goroutine" )
}
2022-07-14 21:14:45 +00:00
ancestors , err := proc . Ancestors ( d . target . Selected , g , numAncestors )
2019-03-16 13:50:18 +00:00
if err != nil {
return nil , err
}
r := make ( [ ] api . Ancestor , len ( ancestors ) )
for i := range ancestors {
r [ i ] . ID = ancestors [ i ] . ID
if ancestors [ i ] . Unreadable != nil {
r [ i ] . Unreadable = ancestors [ i ] . Unreadable . Error ( )
continue
}
frames , err := ancestors [ i ] . Stack ( depth )
if err != nil {
r [ i ] . Unreadable = fmt . Sprintf ( "could not read ancestor stacktrace: %v" , err )
continue
}
r [ i ] . Stack , err = d . convertStacktrace ( frames , nil )
if err != nil {
r [ i ] . Unreadable = fmt . Sprintf ( "could not read ancestor stacktrace: %v" , err )
}
}
return r , nil
}
2020-07-07 13:21:18 +00:00
// ConvertStacktrace converts a slice of proc.Stackframe into a slice of
// api.Stackframe, loading local variables and arguments of each frame if
// cfg is not nil.
func ( d * Debugger ) ConvertStacktrace ( rawlocs [ ] proc . Stackframe , cfg * proc . LoadConfig ) ( [ ] api . Stackframe , error ) {
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
return d . convertStacktrace ( rawlocs , cfg )
}
2016-04-24 17:15:39 +00:00
func ( d * Debugger ) convertStacktrace ( rawlocs [ ] proc . Stackframe , cfg * proc . LoadConfig ) ( [ ] api . Stackframe , error ) {
2015-09-17 08:42:34 +00:00
locations := make ( [ ] api . Stackframe , 0 , len ( rawlocs ) )
2015-06-17 17:11:57 +00:00
for i := range rawlocs {
2017-06-20 17:39:33 +00:00
frame := api . Stackframe {
2017-08-19 20:20:47 +00:00
Location : api . ConvertLocation ( rawlocs [ i ] . Call ) ,
FrameOffset : rawlocs [ i ] . FrameOffset ( ) ,
FramePointerOffset : rawlocs [ i ] . FramePointerOffset ( ) ,
2018-07-06 07:37:31 +00:00
Defers : d . convertDefers ( rawlocs [ i ] . Defers ) ,
2018-08-18 10:12:39 +00:00
Bottom : rawlocs [ i ] . Bottom ,
2017-06-20 17:39:33 +00:00
}
2017-06-23 11:31:05 +00:00
if rawlocs [ i ] . Err != nil {
frame . Err = rawlocs [ i ] . Err . Error ( )
}
2017-02-16 13:16:00 +00:00
if cfg != nil && rawlocs [ i ] . Current . Fn != nil {
2015-10-18 17:37:13 +00:00
var err error
2022-07-14 21:14:45 +00:00
scope := proc . FrameToScope ( d . target . Selected , d . target . Selected . Memory ( ) , nil , rawlocs [ i : ] ... )
2016-04-24 17:15:39 +00:00
locals , err := scope . LocalVariables ( * cfg )
2015-09-17 08:42:34 +00:00
if err != nil {
return nil , err
}
2016-04-24 17:15:39 +00:00
arguments , err := scope . FunctionArguments ( * cfg )
2015-09-17 08:42:34 +00:00
if err != nil {
return nil , err
}
2015-10-18 17:37:13 +00:00
2020-07-07 13:21:18 +00:00
frame . Locals = api . ConvertVars ( locals )
frame . Arguments = api . ConvertVars ( arguments )
2015-09-17 08:42:34 +00:00
}
locations = append ( locations , frame )
2015-06-17 17:11:57 +00:00
}
2015-09-17 08:42:34 +00:00
return locations , nil
2015-06-17 17:11:57 +00:00
}
2015-08-07 16:50:14 +00:00
2018-07-06 07:37:31 +00:00
func ( d * Debugger ) convertDefers ( defers [ ] * proc . Defer ) [ ] api . Defer {
r := make ( [ ] api . Defer , len ( defers ) )
for i := range defers {
2022-07-14 21:14:45 +00:00
ddf , ddl , ddfn := defers [ i ] . DeferredFunc ( d . target . Selected )
drf , drl , drfn := d . target . Selected . BinInfo ( ) . PCToLine ( defers [ i ] . DeferPC )
2018-07-06 07:37:31 +00:00
r [ i ] = api . Defer {
DeferredLoc : api . ConvertLocation ( proc . Location {
2021-07-08 15:47:53 +00:00
PC : ddfn . Entry ,
2018-07-06 07:37:31 +00:00
File : ddf ,
Line : ddl ,
Fn : ddfn ,
} ) ,
DeferLoc : api . ConvertLocation ( proc . Location {
PC : defers [ i ] . DeferPC ,
File : drf ,
Line : drl ,
Fn : drfn ,
} ) ,
SP : defers [ i ] . SP ,
}
if defers [ i ] . Unreadable != nil {
r [ i ] . Unreadable = defers [ i ] . Unreadable . Error ( )
}
}
return r
}
2020-09-15 20:14:55 +00:00
// CurrentPackage returns the fully qualified name of the
// package corresponding to the function location of the
// current thread.
func ( d * Debugger ) CurrentPackage ( ) ( string , error ) {
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
if _ , err := d . target . Valid ( ) ; err != nil {
return "" , err
}
2022-07-14 21:14:45 +00:00
loc , err := d . target . Selected . CurrentThread ( ) . Location ( )
2020-09-15 20:14:55 +00:00
if err != nil {
return "" , err
}
if loc . Fn == nil {
return "" , fmt . Errorf ( "unable to determine current package due to unspecified function location" )
}
return loc . Fn . PackageName ( ) , nil
}
2016-01-10 08:57:52 +00:00
// FindLocation will find the location specified by 'locStr'.
2020-10-26 12:36:52 +00:00
func ( d * Debugger ) FindLocation ( goid , frame , deferredCall int , locStr string , includeNonExecutableLines bool , substitutePathRules [ ] [ 2 ] string ) ( [ ] api . Location , error ) {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2016-02-15 14:39:29 +00:00
2022-07-14 21:14:45 +00:00
if len ( d . target . Targets ( ) ) != 1 {
//TODO(aarzilli): if there is more than one target process all must be
//searched and the addresses returned need to specify which target process
//they belong to.
panic ( "multiple targets not implemented" )
}
2018-06-21 11:07:37 +00:00
if _ , err := d . target . Valid ( ) ; err != nil {
return nil , err
2017-07-26 22:57:47 +00:00
}
2020-05-13 06:38:10 +00:00
loc , err := locspec . Parse ( locStr )
2015-08-07 16:50:14 +00:00
if err != nil {
return nil , err
}
2022-07-14 21:14:45 +00:00
return d . findLocation ( d . target . Selected , goid , frame , deferredCall , locStr , loc , includeNonExecutableLines , substitutePathRules )
2021-05-18 17:25:16 +00:00
}
// FindLocationSpec will find the location specified by 'locStr' and 'locSpec'.
// 'locSpec' should be the result of calling 'locspec.Parse(locStr)'. 'locStr'
// is also passed, because it made be used to broaden the search criteria, if
// the parsed result did not find anything.
func ( d * Debugger ) FindLocationSpec ( goid , frame , deferredCall int , locStr string , locSpec locspec . LocationSpec , includeNonExecutableLines bool , substitutePathRules [ ] [ 2 ] string ) ( [ ] api . Location , error ) {
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2022-07-14 21:14:45 +00:00
if len ( d . target . Targets ( ) ) != 1 {
//TODO(aarzilli): if there is more than one target process all must be
//searched and the addresses returned need to specify which target process
//they belong to.
panic ( "multiple targets not implemented" )
}
2021-05-18 17:25:16 +00:00
if _ , err := d . target . Valid ( ) ; err != nil {
return nil , err
}
2022-07-14 21:14:45 +00:00
return d . findLocation ( d . target . Selected , goid , frame , deferredCall , locStr , locSpec , includeNonExecutableLines , substitutePathRules )
2021-05-18 17:25:16 +00:00
}
2022-07-14 21:14:45 +00:00
func ( d * Debugger ) findLocation ( p * proc . Target , goid , frame , deferredCall int , locStr string , locSpec locspec . LocationSpec , includeNonExecutableLines bool , substitutePathRules [ ] [ 2 ] string ) ( [ ] api . Location , error ) {
s , _ := proc . ConvertEvalScope ( p , goid , frame , deferredCall )
2015-08-28 20:06:29 +00:00
2022-07-14 21:14:45 +00:00
locs , err := locSpec . Find ( p , d . processArgs , s , locStr , includeNonExecutableLines , substitutePathRules )
2015-08-07 16:50:14 +00:00
for i := range locs {
2019-10-25 16:59:18 +00:00
if locs [ i ] . PC == 0 {
continue
}
2022-07-14 21:14:45 +00:00
file , line , fn := p . BinInfo ( ) . PCToLine ( locs [ i ] . PC )
2015-08-07 16:50:14 +00:00
locs [ i ] . File = file
locs [ i ] . Line = line
locs [ i ] . Function = api . ConvertFunction ( fn )
}
return locs , err
}
2016-02-06 06:00:48 +00:00
2019-08-08 18:54:56 +00:00
// Disassemble code between startPC and endPC.
// if endPC == 0 it will find the function containing startPC and disassemble the whole function.
2020-07-07 13:21:18 +00:00
func ( d * Debugger ) Disassemble ( goroutineID int , addr1 , addr2 uint64 ) ( [ ] proc . AsmInstruction , error ) {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2016-02-15 14:39:29 +00:00
2018-06-21 11:07:37 +00:00
if _ , err := d . target . Valid ( ) ; err != nil {
return nil , err
2017-07-26 22:57:47 +00:00
}
2019-08-08 18:54:56 +00:00
if addr2 == 0 {
2022-07-14 21:14:45 +00:00
fn := d . target . Selected . BinInfo ( ) . PCToFunc ( addr1 )
2016-02-06 06:00:48 +00:00
if fn == nil {
2019-08-08 18:54:56 +00:00
return nil , fmt . Errorf ( "address %#x does not belong to any function" , addr1 )
2016-02-06 06:00:48 +00:00
}
2019-08-08 18:54:56 +00:00
addr1 = fn . Entry
addr2 = fn . End
2016-02-06 06:00:48 +00:00
}
2022-07-14 21:14:45 +00:00
g , err := proc . FindGoroutine ( d . target . Selected , goroutineID )
2017-04-13 23:19:57 +00:00
if err != nil {
return nil , err
2016-02-06 06:00:48 +00:00
}
2022-07-14 21:14:45 +00:00
curthread := d . target . Selected . CurrentThread ( )
2019-10-07 16:32:38 +00:00
if g != nil && g . Thread != nil {
curthread = g . Thread
2019-08-08 18:54:56 +00:00
}
2020-05-13 18:56:50 +00:00
regs , _ := curthread . Registers ( )
2019-10-07 16:32:38 +00:00
2022-07-14 21:14:45 +00:00
return proc . Disassemble ( d . target . Selected . Memory ( ) , regs , d . target . Selected . Breakpoints ( ) , d . target . Selected . BinInfo ( ) , addr1 , addr2 )
2020-07-07 13:21:18 +00:00
}
2016-02-06 06:00:48 +00:00
2020-07-07 13:21:18 +00:00
func ( d * Debugger ) AsmInstructionText ( inst * proc . AsmInstruction , flavour proc . AssemblyFlavour ) string {
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2022-07-14 21:14:45 +00:00
return inst . Text ( flavour , d . target . Selected . BinInfo ( ) )
2016-02-06 06:00:48 +00:00
}
2017-05-05 22:17:52 +00:00
// Recorded returns true if the target is a recording.
func ( d * Debugger ) Recorded ( ) ( recorded bool , tracedir string ) {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2017-05-05 22:17:52 +00:00
return d . target . Recorded ( )
}
2020-11-12 23:24:31 +00:00
// FindThreadReturnValues returns the return values of the function that
// the thread of the given 'id' just stepped out of.
func ( d * Debugger ) FindThreadReturnValues ( id int , cfg proc . LoadConfig ) ( [ ] * proc . Variable , error ) {
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
if _ , err := d . target . Valid ( ) ; err != nil {
return nil , err
}
2022-07-14 21:14:45 +00:00
thread , found := d . target . Selected . FindThread ( id )
2020-11-12 23:24:31 +00:00
if ! found {
return nil , fmt . Errorf ( "could not find thread %d" , id )
}
return thread . Common ( ) . ReturnValues ( cfg ) , nil
}
2018-08-31 18:08:18 +00:00
// Checkpoint will set a checkpoint specified by the locspec.
2017-05-05 22:17:52 +00:00
func ( d * Debugger ) Checkpoint ( where string ) ( int , error ) {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2017-05-05 22:17:52 +00:00
return d . target . Checkpoint ( where )
}
2018-08-31 18:08:18 +00:00
// Checkpoints will return a list of checkpoints.
2020-07-07 13:21:18 +00:00
func ( d * Debugger ) Checkpoints ( ) ( [ ] proc . Checkpoint , error ) {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2020-07-07 13:21:18 +00:00
return d . target . Checkpoints ( )
2017-05-05 22:17:52 +00:00
}
2018-08-31 18:08:18 +00:00
// ClearCheckpoint will clear the checkpoint of the given ID.
2017-05-05 22:17:52 +00:00
func ( d * Debugger ) ClearCheckpoint ( id int ) error {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2017-05-05 22:17:52 +00:00
return d . target . ClearCheckpoint ( id )
}
2018-07-05 10:15:22 +00:00
2019-03-20 17:32:51 +00:00
// ListDynamicLibraries returns a list of loaded dynamic libraries.
2020-07-07 13:21:18 +00:00
func ( d * Debugger ) ListDynamicLibraries ( ) [ ] * proc . Image {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2022-07-14 21:14:45 +00:00
return d . target . Selected . BinInfo ( ) . Images [ 1 : ] // skips the first image because it's the executable file
2020-07-07 13:21:18 +00:00
2019-03-20 17:32:51 +00:00
}
2020-02-13 17:29:21 +00:00
// ExamineMemory returns the raw memory stored at the given address.
// The amount of data to be read is specified by length.
// This function will return an error if it reads less than `length` bytes.
2020-09-09 17:36:15 +00:00
func ( d * Debugger ) ExamineMemory ( address uint64 , length int ) ( [ ] byte , error ) {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2020-02-13 17:29:21 +00:00
2022-07-14 21:14:45 +00:00
mem := d . target . Selected . Memory ( )
2020-02-13 17:29:21 +00:00
data := make ( [ ] byte , length )
2020-11-09 19:28:40 +00:00
n , err := mem . ReadMemory ( data , address )
2020-02-13 17:29:21 +00:00
if err != nil {
return nil , err
}
if length != n {
return nil , errors . New ( "the specific range has exceeded readable area" )
}
return data , nil
}
2019-07-27 23:44:48 +00:00
func ( d * Debugger ) GetVersion ( out * api . GetVersionOut ) error {
if d . config . CoreFile != "" {
if d . config . Backend == "rr" {
out . Backend = "rr"
} else {
out . Backend = "core"
}
} else {
if d . config . Backend == "default" {
if runtime . GOOS == "darwin" {
out . Backend = "lldb"
} else {
out . Backend = "native"
}
} else {
out . Backend = d . config . Backend
}
}
2019-10-22 23:39:22 +00:00
2021-05-04 19:49:52 +00:00
if ! d . isRecording ( ) && ! d . IsRunning ( ) {
2022-07-14 21:14:45 +00:00
out . TargetGoVersion = d . target . Selected . BinInfo ( ) . Producer ( )
2020-03-24 16:09:28 +00:00
}
2019-10-22 23:39:22 +00:00
out . MinSupportedVersionOfGo = fmt . Sprintf ( "%d.%d.0" , goversion . MinSupportedVersionOfGoMajor , goversion . MinSupportedVersionOfGoMinor )
out . MaxSupportedVersionOfGo = fmt . Sprintf ( "%d.%d.0" , goversion . MaxSupportedVersionOfGoMajor , goversion . MaxSupportedVersionOfGoMinor )
2019-07-27 23:44:48 +00:00
return nil
}
2019-12-03 13:00:30 +00:00
// ListPackagesBuildInfo returns the list of packages used by the program along with
// the directory where each package was compiled and optionally the list of
// files constituting the package.
2020-07-07 13:21:18 +00:00
func ( d * Debugger ) ListPackagesBuildInfo ( includeFiles bool ) [ ] * proc . PackageBuildInfo {
2020-04-16 17:40:50 +00:00
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2022-07-14 21:14:45 +00:00
return d . target . Selected . BinInfo ( ) . ListPackagesBuildInfo ( includeFiles )
2019-12-03 13:00:30 +00:00
}
2020-03-24 16:09:28 +00:00
// StopRecording stops a recording (if one is in progress)
func ( d * Debugger ) StopRecording ( ) error {
d . recordMutex . Lock ( )
defer d . recordMutex . Unlock ( )
if d . stopRecording == nil {
return ErrNotRecording
}
return d . stopRecording ( )
}
2020-10-07 15:24:40 +00:00
// StopReason returns the reason the reason why the target process is stopped.
// A process could be stopped for multiple simultaneous reasons, in which
// case only one will be reported.
func ( d * Debugger ) StopReason ( ) proc . StopReason {
d . targetMutex . Lock ( )
defer d . targetMutex . Unlock ( )
2022-07-14 21:14:45 +00:00
return d . target . Selected . StopReason
2020-10-07 15:24:40 +00:00
}
2020-07-07 13:21:18 +00:00
// LockTarget acquires the target mutex.
func ( d * Debugger ) LockTarget ( ) {
d . targetMutex . Lock ( )
}
// UnlockTarget releases the target mutex.
func ( d * Debugger ) UnlockTarget ( ) {
d . targetMutex . Unlock ( )
}
2021-01-29 21:39:33 +00:00
// DumpStart starts a core dump to dest.
func ( d * Debugger ) DumpStart ( dest string ) error {
d . targetMutex . Lock ( )
// targetMutex will only be unlocked when the dump is done
2022-07-14 21:14:45 +00:00
//TODO(aarzilli): what do we do if the user switches to a different target after starting a dump but before it's finished?
if ! d . target . Selected . CanDump {
2021-01-29 21:39:33 +00:00
d . targetMutex . Unlock ( )
return ErrCoreDumpNotSupported
}
d . dumpState . Mutex . Lock ( )
defer d . dumpState . Mutex . Unlock ( )
if d . dumpState . Dumping {
d . targetMutex . Unlock ( )
return ErrCoreDumpInProgress
}
fh , err := os . Create ( dest )
if err != nil {
d . targetMutex . Unlock ( )
return err
}
d . dumpState . Dumping = true
d . dumpState . AllDone = false
d . dumpState . Canceled = false
d . dumpState . DoneChan = make ( chan struct { } )
d . dumpState . ThreadsDone = 0
d . dumpState . ThreadsTotal = 0
d . dumpState . MemDone = 0
d . dumpState . MemTotal = 0
d . dumpState . Err = nil
go func ( ) {
defer d . targetMutex . Unlock ( )
2022-07-14 21:14:45 +00:00
d . target . Selected . Dump ( fh , 0 , & d . dumpState )
2021-01-29 21:39:33 +00:00
} ( )
return nil
}
// DumpWait waits for the dump to finish, or for the duration of wait.
// Returns the state of the dump.
// If wait == 0 returns immediately.
func ( d * Debugger ) DumpWait ( wait time . Duration ) * proc . DumpState {
d . dumpState . Mutex . Lock ( )
if ! d . dumpState . Dumping {
d . dumpState . Mutex . Unlock ( )
return & d . dumpState
}
d . dumpState . Mutex . Unlock ( )
if wait > 0 {
alarm := time . After ( wait )
select {
case <- alarm :
case <- d . dumpState . DoneChan :
}
}
return & d . dumpState
}
// DumpCancel canels a dump in progress
func ( d * Debugger ) DumpCancel ( ) error {
d . dumpState . Mutex . Lock ( )
d . dumpState . Canceled = true
d . dumpState . Mutex . Unlock ( )
return nil
}
2021-07-08 15:47:53 +00:00
func ( d * Debugger ) Target ( ) * proc . Target {
2022-07-14 21:14:45 +00:00
return d . target . Selected
}
func ( d * Debugger ) TargetGroup ( ) * proc . TargetGroup {
2021-07-08 15:47:53 +00:00
return d . target
}
2022-01-30 21:39:30 +00:00
func ( d * Debugger ) BuildID ( ) string {
2022-07-14 21:14:45 +00:00
return d . target . Selected . BinInfo ( ) . BuildID
2022-01-30 21:39:30 +00:00
}
2022-05-18 10:23:47 +00:00
func ( d * Debugger ) AttachPid ( ) int {
return d . config . AttachPid
}
2021-07-31 15:16:26 +00:00
func ( d * Debugger ) GetBufferedTracepoints ( ) [ ] api . TracepointResult {
2022-07-14 21:14:45 +00:00
traces := d . target . Selected . GetBufferedTracepoints ( )
2021-07-31 15:16:26 +00:00
if traces == nil {
return nil
}
results := make ( [ ] api . TracepointResult , len ( traces ) )
for i , trace := range traces {
2022-07-14 21:14:45 +00:00
f , l , fn := d . target . Selected . BinInfo ( ) . PCToLine ( uint64 ( trace . FnAddr ) )
2021-07-31 15:16:26 +00:00
results [ i ] . FunctionName = fn . Name
results [ i ] . Line = l
results [ i ] . File = f
2021-08-24 12:53:27 +00:00
results [ i ] . GoroutineID = trace . GoroutineID
2021-07-31 15:16:26 +00:00
for _ , p := range trace . InputParams {
results [ i ] . InputParams = append ( results [ i ] . InputParams , * api . ConvertVar ( p ) )
}
2021-10-25 19:37:36 +00:00
for _ , p := range trace . ReturnParams {
results [ i ] . ReturnParams = append ( results [ i ] . ReturnParams , * api . ConvertVar ( p ) )
}
2021-07-31 15:16:26 +00:00
}
return results
}
2018-07-05 10:15:22 +00:00
func go11DecodeErrorCheck ( err error ) error {
if _ , isdecodeerr := err . ( dwarf . DecodeError ) ; ! isdecodeerr {
return err
}
gover , ok := goversion . Installed ( )
2020-03-18 16:25:32 +00:00
if ! ok || ! gover . AfterOrEqual ( goversion . GoVersion { Major : 1 , Minor : 11 , Rev : - 1 } ) || goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 11 ) {
2018-07-05 10:15:22 +00:00
return err
}
return fmt . Errorf ( "executables built by Go 1.11 or later need Delve built by Go 1.11 or later" )
}
2019-11-01 19:41:06 +00:00
2022-04-01 17:31:03 +00:00
const NoDebugWarning string = "debuggee must not be built with 'go run' or -ldflags='-s -w', which strip debug info"
func noDebugErrorWarning ( err error ) error {
if _ , isdecodeerr := err . ( dwarf . DecodeError ) ; isdecodeerr || strings . Contains ( err . Error ( ) , "could not open debug info" ) {
return fmt . Errorf ( "%s - %s" , err . Error ( ) , NoDebugWarning )
}
return err
}