mirror of
https://github.com/beego/bee.git
synced 2025-06-23 18:50:25 +00:00
Update vendor folder (Delve support)
This commit is contained in:
835
vendor/github.com/derekparker/delve/service/debugger/debugger.go
generated
vendored
Normal file
835
vendor/github.com/derekparker/delve/service/debugger/debugger.go
generated
vendored
Normal file
@ -0,0 +1,835 @@
|
||||
package debugger
|
||||
|
||||
import (
|
||||
"debug/gosym"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/parser"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
"github.com/derekparker/delve/pkg/target"
|
||||
"github.com/derekparker/delve/service/api"
|
||||
)
|
||||
|
||||
// Debugger service.
|
||||
//
|
||||
// Debugger provides a higher level of
|
||||
// abstraction over proc.Process.
|
||||
// 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.
|
||||
type Debugger struct {
|
||||
config *Config
|
||||
// TODO(DO NOT MERGE WITHOUT) rename to targetMutex
|
||||
processMutex sync.Mutex
|
||||
target target.Interface
|
||||
}
|
||||
|
||||
// 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 {
|
||||
// ProcessArgs are the arguments to launch a new process.
|
||||
ProcessArgs []string
|
||||
// WorkingDir is working directory of the new process. This field is used
|
||||
// only when launching a new process.
|
||||
WorkingDir string
|
||||
|
||||
// AttachPid is the PID of an existing process to which the debugger should
|
||||
// attach.
|
||||
AttachPid int
|
||||
}
|
||||
|
||||
// New creates a new Debugger.
|
||||
func New(config *Config) (*Debugger, error) {
|
||||
d := &Debugger{
|
||||
config: config,
|
||||
}
|
||||
|
||||
// Create the process by either attaching or launching.
|
||||
if d.config.AttachPid > 0 {
|
||||
log.Printf("attaching to pid %d", d.config.AttachPid)
|
||||
p, err := proc.Attach(d.config.AttachPid)
|
||||
if err != nil {
|
||||
return nil, attachErrorMessage(d.config.AttachPid, err)
|
||||
}
|
||||
d.target = p
|
||||
} else {
|
||||
log.Printf("launching process with args: %v", d.config.ProcessArgs)
|
||||
p, err := proc.Launch(d.config.ProcessArgs, d.config.WorkingDir)
|
||||
if err != nil {
|
||||
if err != proc.NotExecutableErr && err != proc.UnsupportedArchErr {
|
||||
err = fmt.Errorf("could not launch process: %s", err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
d.target = p
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// ProcessPid returns the PID of the process
|
||||
// the debugger is debugging.
|
||||
func (d *Debugger) ProcessPid() int {
|
||||
return d.target.Pid()
|
||||
}
|
||||
|
||||
// LastModified returns the time that the process' executable was last
|
||||
// modified.
|
||||
func (d *Debugger) LastModified() time.Time {
|
||||
return d.target.LastModified()
|
||||
}
|
||||
|
||||
// Detach detaches from the target process.
|
||||
// If `kill` is true we will kill the process after
|
||||
// detaching.
|
||||
func (d *Debugger) Detach(kill bool) error {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
return d.detach(kill)
|
||||
}
|
||||
|
||||
func (d *Debugger) detach(kill bool) error {
|
||||
if d.config.AttachPid != 0 {
|
||||
return d.target.Detach(kill)
|
||||
}
|
||||
return d.target.Kill()
|
||||
}
|
||||
|
||||
// Restart will restart the target process, first killing
|
||||
// and then exec'ing it again.
|
||||
func (d *Debugger) Restart() ([]api.DiscardedBreakpoint, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
if !d.target.Exited() {
|
||||
if d.target.Running() {
|
||||
d.target.Halt()
|
||||
}
|
||||
// Ensure the process is in a PTRACE_STOP.
|
||||
if err := stopProcess(d.ProcessPid()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := d.detach(true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
p, err := proc.Launch(d.config.ProcessArgs, d.config.WorkingDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not launch process: %s", err)
|
||||
}
|
||||
discarded := []api.DiscardedBreakpoint{}
|
||||
for _, oldBp := range d.breakpoints() {
|
||||
if oldBp.ID < 0 {
|
||||
continue
|
||||
}
|
||||
if len(oldBp.File) > 0 {
|
||||
oldBp.Addr, err = p.FindFileLocation(oldBp.File, oldBp.Line)
|
||||
if err != nil {
|
||||
discarded = append(discarded, api.DiscardedBreakpoint{oldBp, err.Error()})
|
||||
continue
|
||||
}
|
||||
}
|
||||
newBp, err := p.SetBreakpoint(oldBp.Addr, proc.UserBreakpoint, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := copyBreakpointInfo(newBp, oldBp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
d.target = p
|
||||
return discarded, nil
|
||||
}
|
||||
|
||||
// State returns the current state of the debugger.
|
||||
func (d *Debugger) State() (*api.DebuggerState, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
return d.state()
|
||||
}
|
||||
|
||||
func (d *Debugger) state() (*api.DebuggerState, error) {
|
||||
if d.target.Exited() {
|
||||
return nil, proc.ProcessExitedError{Pid: d.ProcessPid()}
|
||||
}
|
||||
|
||||
var (
|
||||
state *api.DebuggerState
|
||||
goroutine *api.Goroutine
|
||||
)
|
||||
|
||||
if d.target.SelectedGoroutine() != nil {
|
||||
goroutine = api.ConvertGoroutine(d.target.SelectedGoroutine())
|
||||
}
|
||||
|
||||
state = &api.DebuggerState{
|
||||
SelectedGoroutine: goroutine,
|
||||
Exited: d.target.Exited(),
|
||||
}
|
||||
|
||||
for i := range d.target.Threads() {
|
||||
th := api.ConvertThread(d.target.Threads()[i])
|
||||
state.Threads = append(state.Threads, th)
|
||||
if i == d.target.CurrentThread().ID {
|
||||
state.CurrentThread = th
|
||||
}
|
||||
}
|
||||
|
||||
for _, bp := range d.target.Breakpoints() {
|
||||
if bp.Internal() {
|
||||
state.NextInProgress = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return state, nil
|
||||
}
|
||||
|
||||
// CreateBreakpoint creates a breakpoint.
|
||||
func (d *Debugger) CreateBreakpoint(requestedBp *api.Breakpoint) (*api.Breakpoint, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
var (
|
||||
createdBp *api.Breakpoint
|
||||
addr uint64
|
||||
err error
|
||||
)
|
||||
|
||||
if requestedBp.Name != "" {
|
||||
if err = api.ValidBreakpointName(requestedBp.Name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if d.findBreakpointByName(requestedBp.Name) != nil {
|
||||
return nil, errors.New("breakpoint name already exists")
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(requestedBp.File) > 0:
|
||||
fileName := requestedBp.File
|
||||
if runtime.GOOS == "windows" {
|
||||
// Accept fileName which is case-insensitive and slash-insensitive match
|
||||
fileNameNormalized := strings.ToLower(filepath.ToSlash(fileName))
|
||||
for symFile := range d.target.Sources() {
|
||||
if fileNameNormalized == strings.ToLower(filepath.ToSlash(symFile)) {
|
||||
fileName = symFile
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
addr, err = d.target.FindFileLocation(fileName, requestedBp.Line)
|
||||
case len(requestedBp.FunctionName) > 0:
|
||||
if requestedBp.Line >= 0 {
|
||||
addr, err = d.target.FindFunctionLocation(requestedBp.FunctionName, false, requestedBp.Line)
|
||||
} else {
|
||||
addr, err = d.target.FindFunctionLocation(requestedBp.FunctionName, true, 0)
|
||||
}
|
||||
default:
|
||||
addr = requestedBp.Addr
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bp, err := d.target.SetBreakpoint(addr, proc.UserBreakpoint, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := copyBreakpointInfo(bp, requestedBp); err != nil {
|
||||
if _, err1 := d.target.ClearBreakpoint(bp.Addr); err1 != nil {
|
||||
err = fmt.Errorf("error while creating breakpoint: %v, additionally the breakpoint could not be properly rolled back: %v", err, err1)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
createdBp = api.ConvertBreakpoint(bp)
|
||||
log.Printf("created breakpoint: %#v", createdBp)
|
||||
return createdBp, nil
|
||||
}
|
||||
|
||||
func (d *Debugger) AmendBreakpoint(amend *api.Breakpoint) error {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
original := d.findBreakpoint(amend.ID)
|
||||
if original == nil {
|
||||
return fmt.Errorf("no breakpoint with ID %d", amend.ID)
|
||||
}
|
||||
if err := api.ValidBreakpointName(amend.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
return copyBreakpointInfo(original, amend)
|
||||
}
|
||||
|
||||
func (d *Debugger) CancelNext() error {
|
||||
return d.target.ClearInternalBreakpoints()
|
||||
}
|
||||
|
||||
func copyBreakpointInfo(bp *proc.Breakpoint, requested *api.Breakpoint) (err error) {
|
||||
bp.Name = requested.Name
|
||||
bp.Tracepoint = requested.Tracepoint
|
||||
bp.Goroutine = requested.Goroutine
|
||||
bp.Stacktrace = requested.Stacktrace
|
||||
bp.Variables = requested.Variables
|
||||
bp.LoadArgs = api.LoadConfigToProc(requested.LoadArgs)
|
||||
bp.LoadLocals = api.LoadConfigToProc(requested.LoadLocals)
|
||||
bp.Cond = nil
|
||||
if requested.Cond != "" {
|
||||
bp.Cond, err = parser.ParseExpr(requested.Cond)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// ClearBreakpoint clears a breakpoint.
|
||||
func (d *Debugger) ClearBreakpoint(requestedBp *api.Breakpoint) (*api.Breakpoint, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
var clearedBp *api.Breakpoint
|
||||
bp, err := d.target.ClearBreakpoint(requestedBp.Addr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Can't clear breakpoint @%x: %s", requestedBp.Addr, err)
|
||||
}
|
||||
clearedBp = api.ConvertBreakpoint(bp)
|
||||
log.Printf("cleared breakpoint: %#v", clearedBp)
|
||||
return clearedBp, err
|
||||
}
|
||||
|
||||
// Breakpoints returns the list of current breakpoints.
|
||||
func (d *Debugger) Breakpoints() []*api.Breakpoint {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
return d.breakpoints()
|
||||
}
|
||||
|
||||
func (d *Debugger) breakpoints() []*api.Breakpoint {
|
||||
bps := []*api.Breakpoint{}
|
||||
for _, bp := range d.target.Breakpoints() {
|
||||
if bp.Internal() {
|
||||
continue
|
||||
}
|
||||
bps = append(bps, api.ConvertBreakpoint(bp))
|
||||
}
|
||||
return bps
|
||||
}
|
||||
|
||||
// FindBreakpoint returns the breakpoint specified by 'id'.
|
||||
func (d *Debugger) FindBreakpoint(id int) *api.Breakpoint {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
bp := d.findBreakpoint(id)
|
||||
if bp == nil {
|
||||
return nil
|
||||
}
|
||||
return api.ConvertBreakpoint(bp)
|
||||
}
|
||||
|
||||
func (d *Debugger) findBreakpoint(id int) *proc.Breakpoint {
|
||||
for _, bp := range d.target.Breakpoints() {
|
||||
if bp.ID == id {
|
||||
return bp
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindBreakpointByName returns the breakpoint specified by 'name'
|
||||
func (d *Debugger) FindBreakpointByName(name string) *api.Breakpoint {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
return d.findBreakpointByName(name)
|
||||
}
|
||||
|
||||
func (d *Debugger) findBreakpointByName(name string) *api.Breakpoint {
|
||||
for _, bp := range d.breakpoints() {
|
||||
if bp.Name == name {
|
||||
return bp
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Threads returns the threads of the target process.
|
||||
func (d *Debugger) Threads() ([]*api.Thread, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
if d.target.Exited() {
|
||||
return nil, &proc.ProcessExitedError{}
|
||||
}
|
||||
threads := []*api.Thread{}
|
||||
for _, th := range d.target.Threads() {
|
||||
threads = append(threads, api.ConvertThread(th))
|
||||
}
|
||||
return threads, nil
|
||||
}
|
||||
|
||||
// FindThread returns the thread for the given 'id'.
|
||||
func (d *Debugger) FindThread(id int) (*api.Thread, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
if d.target.Exited() {
|
||||
return nil, &proc.ProcessExitedError{}
|
||||
}
|
||||
|
||||
for _, th := range d.target.Threads() {
|
||||
if th.ID == id {
|
||||
return api.ConvertThread(th), nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Command handles commands which control the debugger lifecycle
|
||||
func (d *Debugger) Command(command *api.DebuggerCommand) (*api.DebuggerState, error) {
|
||||
var err error
|
||||
|
||||
if command.Name == api.Halt {
|
||||
// RequestManualStop does not invoke any ptrace syscalls, so it's safe to
|
||||
// access the process directly.
|
||||
log.Print("halting")
|
||||
err = d.target.RequestManualStop()
|
||||
}
|
||||
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
switch command.Name {
|
||||
case api.Continue:
|
||||
log.Print("continuing")
|
||||
err = d.target.Continue()
|
||||
if err != nil {
|
||||
if exitedErr, exited := err.(proc.ProcessExitedError); exited {
|
||||
state := &api.DebuggerState{}
|
||||
state.Exited = true
|
||||
state.ExitStatus = exitedErr.Status
|
||||
state.Err = errors.New(exitedErr.Error())
|
||||
return state, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
state, stateErr := d.state()
|
||||
if stateErr != nil {
|
||||
return state, stateErr
|
||||
}
|
||||
err = d.collectBreakpointInformation(state)
|
||||
return state, err
|
||||
|
||||
case api.Next:
|
||||
log.Print("nexting")
|
||||
err = d.target.Next()
|
||||
case api.Step:
|
||||
log.Print("stepping")
|
||||
err = d.target.Step()
|
||||
case api.StepInstruction:
|
||||
log.Print("single stepping")
|
||||
err = d.target.StepInstruction()
|
||||
case api.StepOut:
|
||||
log.Print("step out")
|
||||
err = d.target.StepOut()
|
||||
case api.SwitchThread:
|
||||
log.Printf("switching to thread %d", command.ThreadID)
|
||||
err = d.target.SwitchThread(command.ThreadID)
|
||||
case api.SwitchGoroutine:
|
||||
log.Printf("switching to goroutine %d", command.GoroutineID)
|
||||
err = d.target.SwitchGoroutine(command.GoroutineID)
|
||||
case api.Halt:
|
||||
// RequestManualStop already called
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.state()
|
||||
}
|
||||
|
||||
func (d *Debugger) collectBreakpointInformation(state *api.DebuggerState) error {
|
||||
if state == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := range state.Threads {
|
||||
if state.Threads[i].Breakpoint == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
bp := state.Threads[i].Breakpoint
|
||||
bpi := &api.BreakpointInfo{}
|
||||
state.Threads[i].BreakpointInfo = bpi
|
||||
|
||||
if bp.Goroutine {
|
||||
g, err := d.target.CurrentThread().GetG()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bpi.Goroutine = api.ConvertGoroutine(g)
|
||||
}
|
||||
|
||||
if bp.Stacktrace > 0 {
|
||||
rawlocs, err := d.target.CurrentThread().Stacktrace(bp.Stacktrace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bpi.Stacktrace, err = d.convertStacktrace(rawlocs, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
s, err := d.target.Threads()[state.Threads[i].ID].Scope()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(bp.Variables) > 0 {
|
||||
bpi.Variables = make([]api.Variable, len(bp.Variables))
|
||||
}
|
||||
for i := range bp.Variables {
|
||||
v, err := s.EvalVariable(bp.Variables[i], proc.LoadConfig{true, 1, 64, 64, -1})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bpi.Variables[i] = *api.ConvertVar(v)
|
||||
}
|
||||
if bp.LoadArgs != nil {
|
||||
if vars, err := s.FunctionArguments(*api.LoadConfigToProc(bp.LoadArgs)); err == nil {
|
||||
bpi.Arguments = convertVars(vars)
|
||||
}
|
||||
}
|
||||
if bp.LoadLocals != nil {
|
||||
if locals, err := s.LocalVariables(*api.LoadConfigToProc(bp.LoadLocals)); err == nil {
|
||||
bpi.Locals = convertVars(locals)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sources returns a list of the source files for target binary.
|
||||
func (d *Debugger) Sources(filter string) ([]string, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
regex, err := regexp.Compile(filter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid filter argument: %s", err.Error())
|
||||
}
|
||||
|
||||
files := []string{}
|
||||
for f := range d.target.Sources() {
|
||||
if regex.Match([]byte(f)) {
|
||||
files = append(files, f)
|
||||
}
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
// Functions returns a list of functions in the target process.
|
||||
func (d *Debugger) Functions(filter string) ([]string, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
return regexFilterFuncs(filter, d.target.Funcs())
|
||||
}
|
||||
|
||||
func (d *Debugger) Types(filter string) ([]string, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
regex, err := regexp.Compile(filter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid filter argument: %s", err.Error())
|
||||
}
|
||||
|
||||
types, err := d.target.Types()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r := make([]string, 0, len(types))
|
||||
for _, typ := range types {
|
||||
if regex.Match([]byte(typ)) {
|
||||
r = append(r, typ)
|
||||
}
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func regexFilterFuncs(filter string, allFuncs []gosym.Func) ([]string, error) {
|
||||
regex, err := regexp.Compile(filter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid filter argument: %s", err.Error())
|
||||
}
|
||||
|
||||
funcs := []string{}
|
||||
for _, f := range allFuncs {
|
||||
if f.Sym != nil && regex.Match([]byte(f.Name)) {
|
||||
funcs = append(funcs, f.Name)
|
||||
}
|
||||
}
|
||||
return funcs, nil
|
||||
}
|
||||
|
||||
// PackageVariables returns a list of package variables for the thread,
|
||||
// optionally regexp filtered using regexp described in 'filter'.
|
||||
func (d *Debugger) PackageVariables(threadID int, filter string, cfg proc.LoadConfig) ([]api.Variable, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
regex, err := regexp.Compile(filter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid filter argument: %s", err.Error())
|
||||
}
|
||||
|
||||
vars := []api.Variable{}
|
||||
thread, found := d.target.Threads()[threadID]
|
||||
if !found {
|
||||
return nil, fmt.Errorf("couldn't find thread %d", threadID)
|
||||
}
|
||||
scope, err := thread.Scope()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pv, err := scope.PackageVariables(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, v := range pv {
|
||||
if regex.Match([]byte(v.Name)) {
|
||||
vars = append(vars, *api.ConvertVar(v))
|
||||
}
|
||||
}
|
||||
return vars, err
|
||||
}
|
||||
|
||||
// Registers returns string representation of the CPU registers.
|
||||
func (d *Debugger) Registers(threadID int, floatingPoint bool) (api.Registers, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
thread, found := d.target.Threads()[threadID]
|
||||
if !found {
|
||||
return nil, fmt.Errorf("couldn't find thread %d", threadID)
|
||||
}
|
||||
regs, err := thread.Registers(floatingPoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return api.ConvertRegisters(regs.Slice()), err
|
||||
}
|
||||
|
||||
func convertVars(pv []*proc.Variable) []api.Variable {
|
||||
vars := make([]api.Variable, 0, len(pv))
|
||||
for _, v := range pv {
|
||||
vars = append(vars, *api.ConvertVar(v))
|
||||
}
|
||||
return vars
|
||||
}
|
||||
|
||||
// LocalVariables returns a list of the local variables.
|
||||
func (d *Debugger) LocalVariables(scope api.EvalScope, cfg proc.LoadConfig) ([]api.Variable, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
s, err := d.target.ConvertEvalScope(scope.GoroutineID, scope.Frame)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pv, err := s.LocalVariables(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertVars(pv), err
|
||||
}
|
||||
|
||||
// FunctionArguments returns the arguments to the current function.
|
||||
func (d *Debugger) FunctionArguments(scope api.EvalScope, cfg proc.LoadConfig) ([]api.Variable, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
s, err := d.target.ConvertEvalScope(scope.GoroutineID, scope.Frame)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pv, err := s.FunctionArguments(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertVars(pv), nil
|
||||
}
|
||||
|
||||
// EvalVariableInScope will attempt to evaluate the variable represented by 'symbol'
|
||||
// in the scope provided.
|
||||
func (d *Debugger) EvalVariableInScope(scope api.EvalScope, symbol string, cfg proc.LoadConfig) (*api.Variable, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
s, err := d.target.ConvertEvalScope(scope.GoroutineID, scope.Frame)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v, err := s.EvalVariable(symbol, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return api.ConvertVar(v), err
|
||||
}
|
||||
|
||||
// SetVariableInScope will set the value of the variable represented by
|
||||
// 'symbol' to the value given, in the given scope.
|
||||
func (d *Debugger) SetVariableInScope(scope api.EvalScope, symbol, value string) error {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
s, err := d.target.ConvertEvalScope(scope.GoroutineID, scope.Frame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.SetVariable(symbol, value)
|
||||
}
|
||||
|
||||
// Goroutines will return a list of goroutines in the target process.
|
||||
func (d *Debugger) Goroutines() ([]*api.Goroutine, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
goroutines := []*api.Goroutine{}
|
||||
gs, err := d.target.GoroutinesInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, g := range gs {
|
||||
goroutines = append(goroutines, api.ConvertGoroutine(g))
|
||||
}
|
||||
return goroutines, err
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (d *Debugger) Stacktrace(goroutineID, depth int, cfg *proc.LoadConfig) ([]api.Stackframe, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
var rawlocs []proc.Stackframe
|
||||
|
||||
g, err := d.target.FindGoroutine(goroutineID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if g == nil {
|
||||
rawlocs, err = d.target.CurrentThread().Stacktrace(depth)
|
||||
} else {
|
||||
rawlocs, err = g.Stacktrace(depth)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return d.convertStacktrace(rawlocs, cfg)
|
||||
}
|
||||
|
||||
func (d *Debugger) convertStacktrace(rawlocs []proc.Stackframe, cfg *proc.LoadConfig) ([]api.Stackframe, error) {
|
||||
locations := make([]api.Stackframe, 0, len(rawlocs))
|
||||
for i := range rawlocs {
|
||||
frame := api.Stackframe{Location: api.ConvertLocation(rawlocs[i].Call)}
|
||||
if cfg != nil && rawlocs[i].Current.Fn != nil {
|
||||
var err error
|
||||
scope := rawlocs[i].Scope(d.target.CurrentThread())
|
||||
locals, err := scope.LocalVariables(*cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
arguments, err := scope.FunctionArguments(*cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
frame.Locals = convertVars(locals)
|
||||
frame.Arguments = convertVars(arguments)
|
||||
}
|
||||
locations = append(locations, frame)
|
||||
}
|
||||
|
||||
return locations, nil
|
||||
}
|
||||
|
||||
// FindLocation will find the location specified by 'locStr'.
|
||||
func (d *Debugger) FindLocation(scope api.EvalScope, locStr string) ([]api.Location, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
loc, err := parseLocationSpec(locStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s, _ := d.target.ConvertEvalScope(scope.GoroutineID, scope.Frame)
|
||||
|
||||
locs, err := loc.Find(d, s, locStr)
|
||||
for i := range locs {
|
||||
file, line, fn := d.target.PCToLine(locs[i].PC)
|
||||
locs[i].File = file
|
||||
locs[i].Line = line
|
||||
locs[i].Function = api.ConvertFunction(fn)
|
||||
}
|
||||
return locs, err
|
||||
}
|
||||
|
||||
// Disassembles code between startPC and endPC
|
||||
// if endPC == 0 it will find the function containing startPC and disassemble the whole function
|
||||
func (d *Debugger) Disassemble(scope api.EvalScope, startPC, endPC uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
if endPC == 0 {
|
||||
_, _, fn := d.target.PCToLine(startPC)
|
||||
if fn == nil {
|
||||
return nil, fmt.Errorf("Address 0x%x does not belong to any function", startPC)
|
||||
}
|
||||
startPC = fn.Entry
|
||||
endPC = fn.End
|
||||
}
|
||||
|
||||
currentGoroutine := true
|
||||
thread := d.target.CurrentThread()
|
||||
|
||||
if s, err := d.target.ConvertEvalScope(scope.GoroutineID, scope.Frame); err == nil {
|
||||
thread = s.Thread
|
||||
if scope.GoroutineID != -1 {
|
||||
g, _ := s.Thread.GetG()
|
||||
if g == nil || g.ID != scope.GoroutineID {
|
||||
currentGoroutine = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
insts, err := thread.Disassemble(startPC, endPC, currentGoroutine)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
disass := make(api.AsmInstructions, len(insts))
|
||||
|
||||
for i := range insts {
|
||||
disass[i] = api.ConvertAsmInstruction(insts[i], insts[i].Text(proc.AssemblyFlavour(flavour)))
|
||||
}
|
||||
|
||||
return disass, nil
|
||||
}
|
15
vendor/github.com/derekparker/delve/service/debugger/debugger_darwin.go
generated
vendored
Normal file
15
vendor/github.com/derekparker/delve/service/debugger/debugger_darwin.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
package debugger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
sys "golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func attachErrorMessage(pid int, err error) error {
|
||||
//TODO: mention certificates?
|
||||
return fmt.Errorf("could not attach to pid %d: %s", pid, err)
|
||||
}
|
||||
|
||||
func stopProcess(pid int) error {
|
||||
return sys.Kill(pid, sys.SIGSTOP)
|
||||
}
|
35
vendor/github.com/derekparker/delve/service/debugger/debugger_linux.go
generated
vendored
Normal file
35
vendor/github.com/derekparker/delve/service/debugger/debugger_linux.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
package debugger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
sys "golang.org/x/sys/unix"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func attachErrorMessage(pid int, err error) error {
|
||||
fallbackerr := fmt.Errorf("could not attach to pid %d: %s", pid, err)
|
||||
if serr, ok := err.(syscall.Errno); ok {
|
||||
switch serr {
|
||||
case syscall.EPERM:
|
||||
bs, err := ioutil.ReadFile("/proc/sys/kernel/yama/ptrace_scope")
|
||||
if err == nil && len(bs) >= 1 && bs[0] != '0' {
|
||||
// Yama documentation: https://www.kernel.org/doc/Documentation/security/Yama.txt
|
||||
return fmt.Errorf("Could not attach to pid %d: set /proc/sys/kernel/yama/ptrace_scope to 0", pid)
|
||||
}
|
||||
fi, err := os.Stat(fmt.Sprintf("/proc/%d", pid))
|
||||
if err != nil {
|
||||
return fallbackerr
|
||||
}
|
||||
if fi.Sys().(*syscall.Stat_t).Uid != uint32(os.Getuid()) {
|
||||
return fmt.Errorf("Could not attach to pid %d: current user does not own the process", pid)
|
||||
}
|
||||
}
|
||||
}
|
||||
return fallbackerr
|
||||
}
|
||||
|
||||
func stopProcess(pid int) error {
|
||||
return sys.Kill(pid, sys.SIGSTOP)
|
||||
}
|
16
vendor/github.com/derekparker/delve/service/debugger/debugger_windows.go
generated
vendored
Normal file
16
vendor/github.com/derekparker/delve/service/debugger/debugger_windows.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
package debugger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func attachErrorMessage(pid int, err error) error {
|
||||
return fmt.Errorf("could not attach to pid %d: %s", pid, err)
|
||||
}
|
||||
|
||||
func stopProcess(pid int) error {
|
||||
// We cannot gracefully stop a process on Windows,
|
||||
// so just ignore this request and let `Detach` kill
|
||||
// the process.
|
||||
return nil
|
||||
}
|
411
vendor/github.com/derekparker/delve/service/debugger/locations.go
generated
vendored
Normal file
411
vendor/github.com/derekparker/delve/service/debugger/locations.go
generated
vendored
Normal file
@ -0,0 +1,411 @@
|
||||
package debugger
|
||||
|
||||
import (
|
||||
"debug/gosym"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
"github.com/derekparker/delve/service/api"
|
||||
)
|
||||
|
||||
const maxFindLocationCandidates = 5
|
||||
|
||||
type LocationSpec interface {
|
||||
Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error)
|
||||
}
|
||||
|
||||
type NormalLocationSpec struct {
|
||||
Base string
|
||||
FuncBase *FuncLocationSpec
|
||||
LineOffset int
|
||||
}
|
||||
|
||||
type RegexLocationSpec struct {
|
||||
FuncRegex string
|
||||
}
|
||||
|
||||
type AddrLocationSpec struct {
|
||||
AddrExpr string
|
||||
}
|
||||
|
||||
type OffsetLocationSpec struct {
|
||||
Offset int
|
||||
}
|
||||
|
||||
type LineLocationSpec struct {
|
||||
Line int
|
||||
}
|
||||
|
||||
type FuncLocationSpec struct {
|
||||
PackageName string
|
||||
AbsolutePackage bool
|
||||
ReceiverName string
|
||||
PackageOrReceiverName string
|
||||
BaseName string
|
||||
}
|
||||
|
||||
func parseLocationSpec(locStr string) (LocationSpec, error) {
|
||||
rest := locStr
|
||||
|
||||
malformed := func(reason string) error {
|
||||
return fmt.Errorf("Malformed breakpoint location \"%s\" at %d: %s", locStr, len(locStr)-len(rest), reason)
|
||||
}
|
||||
|
||||
if len(rest) <= 0 {
|
||||
return nil, malformed("empty string")
|
||||
}
|
||||
|
||||
switch rest[0] {
|
||||
case '+', '-':
|
||||
offset, err := strconv.Atoi(rest)
|
||||
if err != nil {
|
||||
return nil, malformed(err.Error())
|
||||
}
|
||||
return &OffsetLocationSpec{offset}, nil
|
||||
|
||||
case '/':
|
||||
if rest[len(rest)-1] == '/' {
|
||||
rx, rest := readRegex(rest[1:])
|
||||
if len(rest) < 0 {
|
||||
return nil, malformed("non-terminated regular expression")
|
||||
}
|
||||
if len(rest) > 1 {
|
||||
return nil, malformed("no line offset can be specified for regular expression locations")
|
||||
}
|
||||
return &RegexLocationSpec{rx}, nil
|
||||
} else {
|
||||
return parseLocationSpecDefault(locStr, rest)
|
||||
}
|
||||
|
||||
case '*':
|
||||
return &AddrLocationSpec{rest[1:]}, nil
|
||||
|
||||
default:
|
||||
return parseLocationSpecDefault(locStr, rest)
|
||||
}
|
||||
}
|
||||
|
||||
func parseLocationSpecDefault(locStr, rest string) (LocationSpec, error) {
|
||||
malformed := func(reason string) error {
|
||||
return fmt.Errorf("Malformed breakpoint location \"%s\" at %d: %s", locStr, len(locStr)-len(rest), reason)
|
||||
}
|
||||
|
||||
v := strings.Split(rest, ":")
|
||||
if len(v) > 2 {
|
||||
// On Windows, path may contain ":", so split only on last ":"
|
||||
v = []string{strings.Join(v[0:len(v)-1], ":"), v[len(v)-1]}
|
||||
}
|
||||
|
||||
if len(v) == 1 {
|
||||
n, err := strconv.ParseInt(v[0], 0, 64)
|
||||
if err == nil {
|
||||
return &LineLocationSpec{int(n)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
spec := &NormalLocationSpec{}
|
||||
|
||||
spec.Base = v[0]
|
||||
spec.FuncBase = parseFuncLocationSpec(spec.Base)
|
||||
|
||||
if len(v) < 2 {
|
||||
spec.LineOffset = -1
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
rest = v[1]
|
||||
|
||||
var err error
|
||||
spec.LineOffset, err = strconv.Atoi(rest)
|
||||
if err != nil || spec.LineOffset < 0 {
|
||||
return nil, malformed("line offset negative or not a number")
|
||||
}
|
||||
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
func readRegex(in string) (rx string, rest string) {
|
||||
out := make([]rune, 0, len(in))
|
||||
escaped := false
|
||||
for i, ch := range in {
|
||||
if escaped {
|
||||
if ch == '/' {
|
||||
out = append(out, '/')
|
||||
} else {
|
||||
out = append(out, '\\')
|
||||
out = append(out, ch)
|
||||
}
|
||||
escaped = false
|
||||
} else {
|
||||
switch ch {
|
||||
case '\\':
|
||||
escaped = true
|
||||
case '/':
|
||||
return string(out), in[i:]
|
||||
default:
|
||||
out = append(out, ch)
|
||||
}
|
||||
}
|
||||
}
|
||||
return string(out), ""
|
||||
}
|
||||
|
||||
func parseFuncLocationSpec(in string) *FuncLocationSpec {
|
||||
var v []string
|
||||
pathend := strings.LastIndex(in, "/")
|
||||
if pathend < 0 {
|
||||
v = strings.Split(in, ".")
|
||||
} else {
|
||||
v = strings.Split(in[pathend:], ".")
|
||||
if len(v) > 0 {
|
||||
v[0] = in[:pathend] + v[0]
|
||||
}
|
||||
}
|
||||
|
||||
var spec FuncLocationSpec
|
||||
switch len(v) {
|
||||
case 1:
|
||||
spec.BaseName = v[0]
|
||||
|
||||
case 2:
|
||||
spec.BaseName = v[1]
|
||||
r := stripReceiverDecoration(v[0])
|
||||
if r != v[0] {
|
||||
spec.ReceiverName = r
|
||||
} else if strings.Index(r, "/") >= 0 {
|
||||
spec.PackageName = r
|
||||
} else {
|
||||
spec.PackageOrReceiverName = r
|
||||
}
|
||||
|
||||
case 3:
|
||||
spec.BaseName = v[2]
|
||||
spec.ReceiverName = stripReceiverDecoration(v[1])
|
||||
spec.PackageName = v[0]
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
if strings.HasPrefix(spec.PackageName, "/") {
|
||||
spec.PackageName = spec.PackageName[1:]
|
||||
spec.AbsolutePackage = true
|
||||
}
|
||||
|
||||
if strings.Index(spec.BaseName, "/") >= 0 || strings.Index(spec.ReceiverName, "/") >= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &spec
|
||||
}
|
||||
|
||||
func stripReceiverDecoration(in string) string {
|
||||
if len(in) < 3 {
|
||||
return in
|
||||
}
|
||||
if (in[0] != '(') || (in[1] != '*') || (in[len(in)-1] != ')') {
|
||||
return in
|
||||
}
|
||||
|
||||
return in[2 : len(in)-1]
|
||||
}
|
||||
|
||||
func (spec *FuncLocationSpec) Match(sym *gosym.Sym) bool {
|
||||
if spec.BaseName != sym.BaseName() {
|
||||
return false
|
||||
}
|
||||
|
||||
recv := stripReceiverDecoration(sym.ReceiverName())
|
||||
if spec.ReceiverName != "" && spec.ReceiverName != recv {
|
||||
return false
|
||||
}
|
||||
if spec.PackageName != "" {
|
||||
if spec.AbsolutePackage {
|
||||
if spec.PackageName != sym.PackageName() {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
if !partialPathMatch(spec.PackageName, sym.PackageName()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
if spec.PackageOrReceiverName != "" && !partialPathMatch(spec.PackageOrReceiverName, sym.PackageName()) && spec.PackageOrReceiverName != recv {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (loc *RegexLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) {
|
||||
funcs := d.target.Funcs()
|
||||
matches, err := regexFilterFuncs(loc.FuncRegex, funcs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r := make([]api.Location, 0, len(matches))
|
||||
for i := range matches {
|
||||
addr, err := d.target.FindFunctionLocation(matches[i], true, 0)
|
||||
if err == nil {
|
||||
r = append(r, api.Location{PC: addr})
|
||||
}
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (loc *AddrLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) {
|
||||
if scope == nil {
|
||||
addr, err := strconv.ParseInt(loc.AddrExpr, 0, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not determine current location (scope is nil)")
|
||||
}
|
||||
return []api.Location{{PC: uint64(addr)}}, nil
|
||||
} else {
|
||||
v, err := scope.EvalExpression(loc.AddrExpr, proc.LoadConfig{true, 0, 0, 0, 0})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if v.Unreadable != nil {
|
||||
return nil, v.Unreadable
|
||||
}
|
||||
switch v.Kind {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
addr, _ := constant.Uint64Val(v.Value)
|
||||
return []api.Location{{PC: addr}}, nil
|
||||
case reflect.Func:
|
||||
_, _, fn := d.target.PCToLine(uint64(v.Base))
|
||||
pc, err := d.target.FirstPCAfterPrologue(fn, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []api.Location{{PC: uint64(pc)}}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("wrong expression kind: %v", v.Kind)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (loc *NormalLocationSpec) FileMatch(path string) bool {
|
||||
return partialPathMatch(loc.Base, path)
|
||||
}
|
||||
|
||||
func partialPathMatch(expr, path string) bool {
|
||||
if runtime.GOOS == "windows" {
|
||||
// Accept `expr` which is case-insensitive and slash-insensitive match to `path`
|
||||
expr = strings.ToLower(filepath.ToSlash(expr))
|
||||
path = strings.ToLower(filepath.ToSlash(path))
|
||||
}
|
||||
if len(expr) < len(path)-1 {
|
||||
return strings.HasSuffix(path, expr) && (path[len(path)-len(expr)-1] == '/')
|
||||
} else {
|
||||
return expr == path
|
||||
}
|
||||
}
|
||||
|
||||
type AmbiguousLocationError struct {
|
||||
Location string
|
||||
CandidatesString []string
|
||||
CandidatesLocation []api.Location
|
||||
}
|
||||
|
||||
func (ale AmbiguousLocationError) Error() string {
|
||||
var candidates []string
|
||||
if ale.CandidatesLocation != nil {
|
||||
for i := range ale.CandidatesLocation {
|
||||
candidates = append(candidates, ale.CandidatesLocation[i].Function.Name)
|
||||
}
|
||||
|
||||
} else {
|
||||
candidates = ale.CandidatesString
|
||||
}
|
||||
return fmt.Sprintf("Location \"%s\" ambiguous: %s…", ale.Location, strings.Join(candidates, ", "))
|
||||
}
|
||||
|
||||
func (loc *NormalLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) {
|
||||
funcs := d.target.Funcs()
|
||||
files := d.target.Sources()
|
||||
|
||||
candidates := []string{}
|
||||
for file := range files {
|
||||
if loc.FileMatch(file) {
|
||||
candidates = append(candidates, file)
|
||||
if len(candidates) >= maxFindLocationCandidates {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if loc.FuncBase != nil {
|
||||
for _, f := range funcs {
|
||||
if f.Sym == nil {
|
||||
continue
|
||||
}
|
||||
if loc.FuncBase.Match(f.Sym) {
|
||||
if loc.Base == f.Name {
|
||||
// if an exact match for the function name is found use it
|
||||
candidates = []string{f.Name}
|
||||
break
|
||||
}
|
||||
if len(candidates) < maxFindLocationCandidates {
|
||||
candidates = append(candidates, f.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch len(candidates) {
|
||||
case 1:
|
||||
var addr uint64
|
||||
var err error
|
||||
if filepath.IsAbs(candidates[0]) {
|
||||
if loc.LineOffset < 0 {
|
||||
return nil, fmt.Errorf("Malformed breakpoint location, no line offset specified")
|
||||
}
|
||||
addr, err = d.target.FindFileLocation(candidates[0], loc.LineOffset)
|
||||
} else {
|
||||
if loc.LineOffset < 0 {
|
||||
addr, err = d.target.FindFunctionLocation(candidates[0], true, 0)
|
||||
} else {
|
||||
addr, err = d.target.FindFunctionLocation(candidates[0], false, loc.LineOffset)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []api.Location{{PC: addr}}, nil
|
||||
|
||||
case 0:
|
||||
return nil, fmt.Errorf("Location \"%s\" not found", locStr)
|
||||
default:
|
||||
return nil, AmbiguousLocationError{Location: locStr, CandidatesString: candidates}
|
||||
}
|
||||
}
|
||||
|
||||
func (loc *OffsetLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) {
|
||||
if scope == nil {
|
||||
return nil, fmt.Errorf("could not determine current location (scope is nil)")
|
||||
}
|
||||
file, line, fn := d.target.PCToLine(scope.PC)
|
||||
if fn == nil {
|
||||
return nil, fmt.Errorf("could not determine current location")
|
||||
}
|
||||
addr, err := d.target.FindFileLocation(file, line+loc.Offset)
|
||||
return []api.Location{{PC: addr}}, err
|
||||
}
|
||||
|
||||
func (loc *LineLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) {
|
||||
if scope == nil {
|
||||
return nil, fmt.Errorf("could not determine current location (scope is nil)")
|
||||
}
|
||||
file, _, fn := d.target.PCToLine(scope.PC)
|
||||
if fn == nil {
|
||||
return nil, fmt.Errorf("could not determine current location")
|
||||
}
|
||||
addr, err := d.target.FindFileLocation(file, loc.Line)
|
||||
return []api.Location{{PC: addr}}, err
|
||||
}
|
Reference in New Issue
Block a user