2015-06-12 19:49:23 +00:00
|
|
|
package proc
|
2014-12-05 22:17:10 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"debug/gosym"
|
2015-04-09 14:53:02 +00:00
|
|
|
"encoding/binary"
|
2016-01-20 20:22:39 +00:00
|
|
|
"errors"
|
2014-12-05 22:17:10 +00:00
|
|
|
"fmt"
|
2016-01-12 08:01:42 +00:00
|
|
|
"go/ast"
|
2015-10-21 07:06:36 +00:00
|
|
|
"go/constant"
|
2016-01-12 08:01:42 +00:00
|
|
|
"go/token"
|
2014-12-05 22:17:10 +00:00
|
|
|
"os"
|
2015-04-29 17:07:27 +00:00
|
|
|
"runtime"
|
2016-01-12 08:01:42 +00:00
|
|
|
"strconv"
|
2015-01-01 13:23:55 +00:00
|
|
|
"strings"
|
2015-03-28 01:12:07 +00:00
|
|
|
"sync"
|
2014-12-05 22:17:10 +00:00
|
|
|
|
|
|
|
"github.com/derekparker/delve/dwarf/frame"
|
2015-03-28 01:12:07 +00:00
|
|
|
"github.com/derekparker/delve/dwarf/line"
|
2014-12-31 13:34:41 +00:00
|
|
|
"github.com/derekparker/delve/dwarf/reader"
|
2016-09-06 17:27:07 +00:00
|
|
|
"golang.org/x/debug/dwarf"
|
2014-12-05 22:17:10 +00:00
|
|
|
)
|
|
|
|
|
2015-06-20 22:54:52 +00:00
|
|
|
// Process represents all of the information the debugger
|
2015-06-12 19:12:50 +00:00
|
|
|
// is holding onto regarding the process we are debugging.
|
2015-06-20 22:54:52 +00:00
|
|
|
type Process struct {
|
2015-06-12 19:30:59 +00:00
|
|
|
Pid int // Process Pid
|
|
|
|
Process *os.Process // Pointer to process struct for the actual process we are debugging
|
|
|
|
|
2015-09-26 20:56:24 +00:00
|
|
|
// Breakpoint table, holds information on breakpoints.
|
2015-06-20 23:07:32 +00:00
|
|
|
// Maps instruction address to Breakpoint struct.
|
2015-06-12 19:32:32 +00:00
|
|
|
Breakpoints map[uint64]*Breakpoint
|
2015-06-12 19:30:59 +00:00
|
|
|
|
2015-06-12 19:51:23 +00:00
|
|
|
// List of threads mapped as such: pid -> *Thread
|
|
|
|
Threads map[int]*Thread
|
2015-06-12 19:12:50 +00:00
|
|
|
|
2015-08-28 20:06:29 +00:00
|
|
|
// Active thread
|
2015-06-12 19:51:23 +00:00
|
|
|
CurrentThread *Thread
|
2015-06-12 19:12:50 +00:00
|
|
|
|
2015-08-28 20:06:29 +00:00
|
|
|
// Goroutine that will be used by default to set breakpoint, eval variables, etc...
|
|
|
|
// Normally SelectedGoroutine is CurrentThread.GetG, it will not be only if SwitchGoroutine is called with a goroutine that isn't attached to a thread
|
|
|
|
SelectedGoroutine *G
|
|
|
|
|
2015-11-26 13:10:58 +00:00
|
|
|
// Maps package names to package paths, needed to lookup types inside DWARF info
|
|
|
|
packageMap map[string]string
|
|
|
|
|
2015-09-17 17:33:42 +00:00
|
|
|
allGCache []*G
|
2015-04-29 13:17:35 +00:00
|
|
|
dwarf *dwarf.Data
|
|
|
|
goSymTable *gosym.Table
|
|
|
|
frameEntries frame.FrameDescriptionEntries
|
2015-08-28 08:06:22 +00:00
|
|
|
lineInfo line.DebugLines
|
2015-04-29 13:17:35 +00:00
|
|
|
os *OSProcessDetails
|
2015-04-29 17:07:27 +00:00
|
|
|
arch Arch
|
2015-04-29 13:17:35 +00:00
|
|
|
breakpointIDCounter int
|
|
|
|
tempBreakpointIDCounter int
|
2016-03-09 15:11:58 +00:00
|
|
|
firstStart bool
|
2015-04-29 13:17:35 +00:00
|
|
|
halt bool
|
|
|
|
exited bool
|
2015-06-13 04:47:30 +00:00
|
|
|
ptraceChan chan func()
|
|
|
|
ptraceDoneChan chan interface{}
|
2016-03-05 12:04:11 +00:00
|
|
|
types map[string]dwarf.Offset
|
2016-05-29 19:20:09 +00:00
|
|
|
|
|
|
|
loadModuleDataOnce sync.Once
|
|
|
|
moduleData []moduleData
|
2015-06-13 04:47:30 +00:00
|
|
|
}
|
|
|
|
|
2016-04-21 10:19:21 +00:00
|
|
|
var NotExecutableErr = errors.New("not an executable file")
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// New returns an initialized Process struct. Before returning,
|
|
|
|
// it will also launch a goroutine in order to handle ptrace(2)
|
|
|
|
// functions. For more information, see the documentation on
|
|
|
|
// `handlePtraceFuncs`.
|
2015-06-20 22:54:52 +00:00
|
|
|
func New(pid int) *Process {
|
|
|
|
dbp := &Process{
|
2015-06-13 04:47:30 +00:00
|
|
|
Pid: pid,
|
|
|
|
Threads: make(map[int]*Thread),
|
|
|
|
Breakpoints: make(map[uint64]*Breakpoint),
|
|
|
|
firstStart: true,
|
|
|
|
os: new(OSProcessDetails),
|
|
|
|
ptraceChan: make(chan func()),
|
|
|
|
ptraceDoneChan: make(chan interface{}),
|
|
|
|
}
|
2016-08-04 04:44:34 +00:00
|
|
|
// TODO: find better way to determine proc arch (perhaps use executable file info)
|
|
|
|
switch runtime.GOARCH {
|
|
|
|
case "amd64":
|
|
|
|
dbp.arch = AMD64Arch()
|
|
|
|
}
|
2015-06-13 04:47:30 +00:00
|
|
|
go dbp.handlePtraceFuncs()
|
|
|
|
return dbp
|
2014-12-08 23:15:52 +00:00
|
|
|
}
|
|
|
|
|
2015-03-05 22:59:51 +00:00
|
|
|
// ProcessExitedError indicates that the process has exited and contains both
|
|
|
|
// process id and exit status.
|
|
|
|
type ProcessExitedError struct {
|
|
|
|
Pid int
|
|
|
|
Status int
|
|
|
|
}
|
|
|
|
|
|
|
|
func (pe ProcessExitedError) Error() string {
|
2015-06-21 18:08:14 +00:00
|
|
|
return fmt.Sprintf("Process %d has exited with status %d", pe.Pid, pe.Status)
|
2015-03-05 22:59:51 +00:00
|
|
|
}
|
|
|
|
|
2015-07-10 20:57:32 +00:00
|
|
|
// Detach from the process being debugged, optionally killing it.
|
2015-06-20 22:54:52 +00:00
|
|
|
func (dbp *Process) Detach(kill bool) (err error) {
|
2015-07-13 21:34:51 +00:00
|
|
|
if dbp.Running() {
|
|
|
|
if err = dbp.Halt(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2015-07-11 06:43:47 +00:00
|
|
|
if !kill {
|
|
|
|
// Clean up any breakpoints we've set.
|
|
|
|
for _, bp := range dbp.Breakpoints {
|
|
|
|
if bp != nil {
|
|
|
|
_, err := dbp.ClearBreakpoint(bp.Addr)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-06-21 03:47:44 +00:00
|
|
|
}
|
2015-06-13 04:47:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
dbp.execPtraceFunc(func() {
|
2015-09-26 12:38:57 +00:00
|
|
|
err = PtraceDetach(dbp.Pid, 0)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2015-06-13 04:47:30 +00:00
|
|
|
if kill {
|
2016-01-15 05:26:54 +00:00
|
|
|
err = killProcess(dbp.Pid)
|
2015-06-13 04:47:30 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
return
|
2015-05-27 22:31:25 +00:00
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// Exited returns whether the debugged
|
2015-03-21 22:31:53 +00:00
|
|
|
// process has exited.
|
2015-06-20 22:54:52 +00:00
|
|
|
func (dbp *Process) Exited() bool {
|
2015-03-21 22:31:53 +00:00
|
|
|
return dbp.exited
|
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// Running returns whether the debugged
|
2015-02-02 21:09:56 +00:00
|
|
|
// process is currently executing.
|
2015-06-20 22:54:52 +00:00
|
|
|
func (dbp *Process) Running() bool {
|
2015-06-26 14:14:21 +00:00
|
|
|
for _, th := range dbp.Threads {
|
|
|
|
if th.running {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
2015-01-09 22:24:33 +00:00
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// LoadInformation finds the executable and then uses it
|
2015-03-28 01:12:07 +00:00
|
|
|
// to parse the following information:
|
|
|
|
// * Dwarf .debug_frame section
|
|
|
|
// * Dwarf .debug_line section
|
|
|
|
// * Go symbol table.
|
2015-06-20 22:54:52 +00:00
|
|
|
func (dbp *Process) LoadInformation(path string) error {
|
2015-03-28 01:12:07 +00:00
|
|
|
var wg sync.WaitGroup
|
|
|
|
|
2015-04-25 14:46:16 +00:00
|
|
|
exe, err := dbp.findExecutable(path)
|
2015-03-28 01:12:07 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-03-05 12:04:11 +00:00
|
|
|
wg.Add(5)
|
2015-10-04 18:16:39 +00:00
|
|
|
go dbp.loadProcessInformation(&wg)
|
2015-03-28 01:12:07 +00:00
|
|
|
go dbp.parseDebugFrame(exe, &wg)
|
|
|
|
go dbp.obtainGoSymbols(exe, &wg)
|
|
|
|
go dbp.parseDebugLineInfo(exe, &wg)
|
2016-03-05 12:04:11 +00:00
|
|
|
go dbp.loadTypeMap(&wg)
|
2015-03-28 01:12:07 +00:00
|
|
|
wg.Wait()
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// FindFileLocation returns the PC for a given file:line.
|
2016-02-11 07:18:39 +00:00
|
|
|
// Assumes that `file` is normailzed to lower case and '/' on Windows.
|
2015-08-07 16:50:14 +00:00
|
|
|
func (dbp *Process) FindFileLocation(fileName string, lineno int) (uint64, error) {
|
2016-02-11 07:18:39 +00:00
|
|
|
pc, fn, err := dbp.goSymTable.LineToPC(fileName, lineno)
|
2015-08-07 16:50:14 +00:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
2016-02-11 07:18:39 +00:00
|
|
|
if fn.Entry == pc {
|
|
|
|
pc, _ = dbp.FirstPCAfterPrologue(fn, true)
|
|
|
|
}
|
2015-08-07 16:50:14 +00:00
|
|
|
return pc, nil
|
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// FindFunctionLocation finds address of a function's line
|
2015-08-07 16:50:14 +00:00
|
|
|
// If firstLine == true is passed FindFunctionLocation will attempt to find the first line of the function
|
|
|
|
// If lineOffset is passed FindFunctionLocation will return the address of that line
|
|
|
|
// Pass lineOffset == 0 and firstLine == false if you want the address for the function's entry point
|
|
|
|
// Note that setting breakpoints at that address will cause surprising behavior:
|
|
|
|
// https://github.com/derekparker/delve/issues/170
|
|
|
|
func (dbp *Process) FindFunctionLocation(funcName string, firstLine bool, lineOffset int) (uint64, error) {
|
2015-10-22 17:07:24 +00:00
|
|
|
origfn := dbp.goSymTable.LookupFunc(funcName)
|
|
|
|
if origfn == nil {
|
2015-08-07 16:50:14 +00:00
|
|
|
return 0, fmt.Errorf("Could not find function %s\n", funcName)
|
|
|
|
}
|
2015-01-01 13:23:55 +00:00
|
|
|
|
2015-08-07 16:50:14 +00:00
|
|
|
if firstLine {
|
2016-02-11 07:18:39 +00:00
|
|
|
return dbp.FirstPCAfterPrologue(origfn, false)
|
2015-08-07 16:50:14 +00:00
|
|
|
} else if lineOffset > 0 {
|
2015-10-22 17:07:24 +00:00
|
|
|
filename, lineno, _ := dbp.goSymTable.PCToLine(origfn.Entry)
|
2015-08-07 16:50:14 +00:00
|
|
|
breakAddr, _, err := dbp.goSymTable.LineToPC(filename, lineno+lineOffset)
|
|
|
|
return breakAddr, err
|
2015-03-24 13:31:56 +00:00
|
|
|
}
|
|
|
|
|
2015-10-22 17:07:24 +00:00
|
|
|
return origfn.Entry, nil
|
2015-01-01 13:23:55 +00:00
|
|
|
}
|
|
|
|
|
2016-01-24 05:25:46 +00:00
|
|
|
// CurrentLocation returns the location of the current thread.
|
|
|
|
func (dbp *Process) CurrentLocation() (*Location, error) {
|
|
|
|
return dbp.CurrentThread.Location()
|
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// RequestManualStop sets the `halt` flag and
|
|
|
|
// sends SIGSTOP to all threads.
|
2015-06-20 22:54:52 +00:00
|
|
|
func (dbp *Process) RequestManualStop() error {
|
2016-01-30 06:23:14 +00:00
|
|
|
if dbp.exited {
|
|
|
|
return &ProcessExitedError{}
|
|
|
|
}
|
2015-01-09 22:24:33 +00:00
|
|
|
dbp.halt = true
|
2015-07-07 20:51:19 +00:00
|
|
|
return dbp.requestManualStop()
|
2015-01-09 22:24:33 +00:00
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// SetBreakpoint sets a breakpoint at addr, and stores it in the process wide
|
2015-01-14 02:37:10 +00:00
|
|
|
// break point table. Setting a break point must be thread specific due to
|
|
|
|
// ptrace actions needing the thread to be in a signal-delivery-stop.
|
2015-06-20 23:01:06 +00:00
|
|
|
func (dbp *Process) SetBreakpoint(addr uint64) (*Breakpoint, error) {
|
2016-01-30 06:23:14 +00:00
|
|
|
if dbp.exited {
|
|
|
|
return nil, &ProcessExitedError{}
|
|
|
|
}
|
2016-01-10 08:57:52 +00:00
|
|
|
return dbp.setBreakpoint(dbp.CurrentThread.ID, addr, false)
|
2015-04-19 22:11:33 +00:00
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// SetTempBreakpoint sets a temp breakpoint. Used during 'next' operations.
|
2016-04-11 11:50:01 +00:00
|
|
|
func (dbp *Process) SetTempBreakpoint(addr uint64, cond ast.Expr) (*Breakpoint, error) {
|
|
|
|
bp, err := dbp.setBreakpoint(dbp.CurrentThread.ID, addr, true)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
bp.Cond = cond
|
|
|
|
return bp, nil
|
2015-01-01 13:23:55 +00:00
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// ClearBreakpoint clears the breakpoint at addr.
|
2015-06-20 23:01:06 +00:00
|
|
|
func (dbp *Process) ClearBreakpoint(addr uint64) (*Breakpoint, error) {
|
2016-01-30 06:23:14 +00:00
|
|
|
if dbp.exited {
|
|
|
|
return nil, &ProcessExitedError{}
|
|
|
|
}
|
2015-09-26 20:56:24 +00:00
|
|
|
bp, ok := dbp.FindBreakpoint(addr)
|
2015-06-29 09:35:53 +00:00
|
|
|
if !ok {
|
|
|
|
return nil, NoBreakpointError{addr: addr}
|
|
|
|
}
|
|
|
|
|
2015-09-26 20:56:24 +00:00
|
|
|
if _, err := bp.Clear(dbp.CurrentThread); err != nil {
|
|
|
|
return nil, err
|
2015-06-29 09:35:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
delete(dbp.Breakpoints, addr)
|
|
|
|
|
|
|
|
return bp, nil
|
2015-01-01 13:23:55 +00:00
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// Status returns the status of the current main thread context.
|
2016-01-15 05:26:54 +00:00
|
|
|
func (dbp *Process) Status() *WaitStatus {
|
2014-12-05 22:17:10 +00:00
|
|
|
return dbp.CurrentThread.Status
|
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// Next continues execution until the next source line.
|
2015-11-18 09:07:08 +00:00
|
|
|
func (dbp *Process) Next() (err error) {
|
2016-01-30 06:23:14 +00:00
|
|
|
if dbp.exited {
|
|
|
|
return &ProcessExitedError{}
|
|
|
|
}
|
2015-11-18 09:07:08 +00:00
|
|
|
for i := range dbp.Breakpoints {
|
|
|
|
if dbp.Breakpoints[i].Temp {
|
|
|
|
return fmt.Errorf("next while nexting")
|
2015-08-28 13:17:12 +00:00
|
|
|
}
|
2015-04-25 02:56:57 +00:00
|
|
|
}
|
|
|
|
|
2016-04-11 11:50:01 +00:00
|
|
|
if err = dbp.setNextBreakpoints(); err != nil {
|
|
|
|
switch err.(type) {
|
2015-08-22 03:13:54 +00:00
|
|
|
case ThreadBlockedError, NoReturnAddr: // Noop
|
|
|
|
default:
|
2016-04-24 23:20:02 +00:00
|
|
|
dbp.ClearTempBreakpoints()
|
2015-08-28 13:17:12 +00:00
|
|
|
return
|
2015-08-22 03:13:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-18 09:07:08 +00:00
|
|
|
return dbp.Continue()
|
2014-12-05 22:17:10 +00:00
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// Continue continues execution of the debugged
|
|
|
|
// process. It will continue until it hits a breakpoint
|
|
|
|
// or is otherwise stopped.
|
2015-06-20 22:54:52 +00:00
|
|
|
func (dbp *Process) Continue() error {
|
2016-01-30 06:23:14 +00:00
|
|
|
if dbp.exited {
|
|
|
|
return &ProcessExitedError{}
|
|
|
|
}
|
2015-11-18 09:07:08 +00:00
|
|
|
for {
|
2015-11-24 21:48:28 +00:00
|
|
|
if err := dbp.resume(); err != nil {
|
2015-11-18 09:07:08 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-11-24 21:48:28 +00:00
|
|
|
var trapthread *Thread
|
|
|
|
var err error
|
2015-11-18 09:07:08 +00:00
|
|
|
|
2015-11-24 21:48:28 +00:00
|
|
|
dbp.run(func() error {
|
|
|
|
trapthread, err = dbp.trapWait(-1)
|
|
|
|
return nil
|
|
|
|
})
|
2015-07-10 19:48:45 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := dbp.Halt(); err != nil {
|
2015-10-02 16:17:07 +00:00
|
|
|
return dbp.exitGuard(err)
|
2015-07-10 19:48:45 +00:00
|
|
|
}
|
2015-11-24 21:48:28 +00:00
|
|
|
if err := dbp.setCurrentBreakpoints(trapthread); err != nil {
|
2015-11-14 12:28:12 +00:00
|
|
|
return err
|
2015-10-02 16:17:07 +00:00
|
|
|
}
|
2015-11-24 21:48:28 +00:00
|
|
|
if err := dbp.pickCurrentThread(trapthread); err != nil {
|
2015-07-10 19:48:45 +00:00
|
|
|
return err
|
|
|
|
}
|
2016-01-16 08:13:15 +00:00
|
|
|
|
2015-11-24 21:48:28 +00:00
|
|
|
switch {
|
|
|
|
case dbp.CurrentThread.CurrentBreakpoint == nil:
|
|
|
|
// runtime.Breakpoint or manual stop
|
|
|
|
if dbp.CurrentThread.onRuntimeBreakpoint() {
|
|
|
|
for i := 0; i < 2; i++ {
|
2016-01-24 05:25:46 +00:00
|
|
|
if err = dbp.CurrentThread.StepInstruction(); err != nil {
|
2015-11-24 21:48:28 +00:00
|
|
|
return err
|
|
|
|
}
|
2015-07-10 19:48:45 +00:00
|
|
|
}
|
2015-03-01 04:03:26 +00:00
|
|
|
}
|
2016-01-12 08:01:42 +00:00
|
|
|
return dbp.conditionErrors()
|
2015-11-24 21:48:28 +00:00
|
|
|
case dbp.CurrentThread.onTriggeredTempBreakpoint():
|
2016-04-24 23:20:02 +00:00
|
|
|
err := dbp.ClearTempBreakpoints()
|
2016-01-12 08:01:42 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return dbp.conditionErrors()
|
2015-11-24 21:48:28 +00:00
|
|
|
case dbp.CurrentThread.onTriggeredBreakpoint():
|
2016-01-12 08:01:42 +00:00
|
|
|
onNextGoroutine, err := dbp.CurrentThread.onNextGoroutine()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2016-01-16 08:13:15 +00:00
|
|
|
}
|
2016-01-12 08:01:42 +00:00
|
|
|
if onNextGoroutine {
|
2016-04-24 23:20:02 +00:00
|
|
|
err := dbp.ClearTempBreakpoints()
|
2016-01-12 08:01:42 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return dbp.conditionErrors()
|
2015-11-24 21:48:28 +00:00
|
|
|
default:
|
|
|
|
// not a manual stop, not on runtime.Breakpoint, not on a breakpoint, just repeat
|
2015-03-01 04:03:26 +00:00
|
|
|
}
|
2015-11-24 21:48:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-12 08:01:42 +00:00
|
|
|
func (dbp *Process) conditionErrors() error {
|
|
|
|
var condErr error
|
|
|
|
for _, th := range dbp.Threads {
|
|
|
|
if th.CurrentBreakpoint != nil && th.BreakpointConditionError != nil {
|
|
|
|
if condErr == nil {
|
|
|
|
condErr = th.BreakpointConditionError
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("multiple errors evaluating conditions")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return condErr
|
|
|
|
}
|
|
|
|
|
2015-11-24 21:48:28 +00:00
|
|
|
// pick a new dbp.CurrentThread, with the following priority:
|
|
|
|
// - a thread with onTriggeredTempBreakpoint() == true
|
|
|
|
// - a thread with onTriggeredBreakpoint() == true (prioritizing trapthread)
|
|
|
|
// - trapthread
|
|
|
|
func (dbp *Process) pickCurrentThread(trapthread *Thread) error {
|
|
|
|
for _, th := range dbp.Threads {
|
|
|
|
if th.onTriggeredTempBreakpoint() {
|
2016-01-10 08:57:52 +00:00
|
|
|
return dbp.SwitchThread(th.ID)
|
2015-11-24 21:48:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if trapthread.onTriggeredBreakpoint() {
|
2016-01-10 08:57:52 +00:00
|
|
|
return dbp.SwitchThread(trapthread.ID)
|
2015-11-24 21:48:28 +00:00
|
|
|
}
|
|
|
|
for _, th := range dbp.Threads {
|
|
|
|
if th.onTriggeredBreakpoint() {
|
2016-01-10 08:57:52 +00:00
|
|
|
return dbp.SwitchThread(th.ID)
|
2015-11-24 21:48:28 +00:00
|
|
|
}
|
|
|
|
}
|
2016-01-10 08:57:52 +00:00
|
|
|
return dbp.SwitchThread(trapthread.ID)
|
2014-12-05 22:17:10 +00:00
|
|
|
}
|
|
|
|
|
2016-01-24 05:25:46 +00:00
|
|
|
// Step will continue until another source line is reached.
|
|
|
|
// Will step into functions.
|
|
|
|
func (dbp *Process) Step() (err error) {
|
2016-04-13 13:25:23 +00:00
|
|
|
if dbp.SelectedGoroutine != nil && dbp.SelectedGoroutine.thread == nil {
|
|
|
|
// Step called on parked goroutine
|
|
|
|
return dbp.stepToPC(dbp.SelectedGoroutine.PC)
|
|
|
|
}
|
2016-01-24 05:25:46 +00:00
|
|
|
fn := func() error {
|
|
|
|
var nloc *Location
|
|
|
|
th := dbp.CurrentThread
|
|
|
|
loc, err := th.Location()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for {
|
2016-02-12 07:43:22 +00:00
|
|
|
pc, err := dbp.CurrentThread.PC()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
text, err := dbp.CurrentThread.Disassemble(pc, pc+maxInstructionLength, true)
|
|
|
|
if err == nil && len(text) > 0 && text[0].IsCall() && text[0].DestLoc != nil && text[0].DestLoc.Fn != nil {
|
2016-07-21 20:10:35 +00:00
|
|
|
fn := text[0].DestLoc.Fn
|
|
|
|
// Ensure PC and Entry match, otherwise StepInto is likely to set
|
|
|
|
// its breakpoint before DestLoc.PC and hence run too far ahead.
|
|
|
|
// Calls to runtime.duffzero and duffcopy have this problem.
|
|
|
|
if fn.Entry == text[0].DestLoc.PC {
|
|
|
|
return dbp.StepInto(fn)
|
|
|
|
}
|
2016-02-12 07:43:22 +00:00
|
|
|
}
|
|
|
|
|
2016-01-27 00:45:26 +00:00
|
|
|
err = dbp.CurrentThread.StepInstruction()
|
2016-01-24 05:25:46 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
nloc, err = th.Location()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if nloc.File != loc.File {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if nloc.File == loc.File && nloc.Line != loc.Line {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return dbp.run(fn)
|
|
|
|
}
|
|
|
|
|
2016-02-12 07:43:22 +00:00
|
|
|
// StepInto sets a temp breakpoint after the prologue of fn and calls Continue
|
|
|
|
func (dbp *Process) StepInto(fn *gosym.Func) error {
|
2016-04-24 23:20:02 +00:00
|
|
|
for i := range dbp.Breakpoints {
|
|
|
|
if dbp.Breakpoints[i].Temp {
|
|
|
|
return fmt.Errorf("next while nexting")
|
|
|
|
}
|
|
|
|
}
|
2016-02-12 07:43:22 +00:00
|
|
|
pc, _ := dbp.FirstPCAfterPrologue(fn, false)
|
2016-04-13 13:25:23 +00:00
|
|
|
return dbp.stepToPC(pc)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (dbp *Process) stepToPC(pc uint64) error {
|
2016-04-11 11:50:01 +00:00
|
|
|
if _, err := dbp.SetTempBreakpoint(pc, sameGoroutineCondition(dbp.SelectedGoroutine)); err != nil {
|
2016-02-12 07:43:22 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
return dbp.Continue()
|
|
|
|
}
|
|
|
|
|
2016-04-11 11:50:01 +00:00
|
|
|
// Returns an expression that evaluates to true when the current goroutine is g
|
|
|
|
func sameGoroutineCondition(g *G) ast.Expr {
|
|
|
|
if g == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return &ast.BinaryExpr{
|
|
|
|
Op: token.EQL,
|
|
|
|
X: &ast.SelectorExpr{
|
|
|
|
X: &ast.SelectorExpr{
|
|
|
|
X: &ast.Ident{Name: "runtime"},
|
|
|
|
Sel: &ast.Ident{Name: "curg"},
|
|
|
|
},
|
|
|
|
Sel: &ast.Ident{Name: "goid"},
|
|
|
|
},
|
|
|
|
Y: &ast.BasicLit{Kind: token.INT, Value: strconv.Itoa(g.ID)},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-24 05:25:46 +00:00
|
|
|
// StepInstruction will continue the current thread for exactly
|
2016-01-20 20:22:39 +00:00
|
|
|
// one instruction. This method affects only the thread
|
|
|
|
// asssociated with the selected goroutine. All other
|
|
|
|
// threads will remain stopped.
|
2016-01-24 05:25:46 +00:00
|
|
|
func (dbp *Process) StepInstruction() (err error) {
|
2016-01-20 20:22:39 +00:00
|
|
|
if dbp.SelectedGoroutine == nil {
|
|
|
|
return errors.New("cannot single step: no selected goroutine")
|
2015-01-14 02:37:10 +00:00
|
|
|
}
|
2016-01-20 20:22:39 +00:00
|
|
|
if dbp.SelectedGoroutine.thread == nil {
|
2016-04-14 10:01:48 +00:00
|
|
|
// Step called on parked goroutine
|
|
|
|
return dbp.stepToPC(dbp.SelectedGoroutine.PC)
|
2016-01-20 20:22:39 +00:00
|
|
|
}
|
2016-01-24 05:25:46 +00:00
|
|
|
return dbp.run(dbp.SelectedGoroutine.thread.StepInstruction)
|
2015-01-14 02:37:10 +00:00
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// SwitchThread changes from current thread to the thread specified by `tid`.
|
2015-06-20 22:54:52 +00:00
|
|
|
func (dbp *Process) SwitchThread(tid int) error {
|
2016-01-30 06:23:14 +00:00
|
|
|
if dbp.exited {
|
|
|
|
return &ProcessExitedError{}
|
|
|
|
}
|
2015-03-13 19:47:04 +00:00
|
|
|
if th, ok := dbp.Threads[tid]; ok {
|
2015-06-12 03:16:18 +00:00
|
|
|
dbp.CurrentThread = th
|
2015-08-28 20:06:29 +00:00
|
|
|
dbp.SelectedGoroutine, _ = dbp.CurrentThread.GetG()
|
2015-03-13 19:47:04 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return fmt.Errorf("thread %d does not exist", tid)
|
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// SwitchGoroutine changes from current thread to the thread
|
|
|
|
// running the specified goroutine.
|
2015-08-28 20:06:29 +00:00
|
|
|
func (dbp *Process) SwitchGoroutine(gid int) error {
|
2016-01-30 06:23:14 +00:00
|
|
|
if dbp.exited {
|
|
|
|
return &ProcessExitedError{}
|
|
|
|
}
|
2015-09-20 06:45:45 +00:00
|
|
|
g, err := dbp.FindGoroutine(gid)
|
2015-08-28 20:06:29 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-09-20 06:45:45 +00:00
|
|
|
if g == nil {
|
|
|
|
// user specified -1 and SelectedGoroutine is nil
|
|
|
|
return nil
|
2015-08-28 20:06:29 +00:00
|
|
|
}
|
2015-09-20 06:45:45 +00:00
|
|
|
if g.thread != nil {
|
2016-01-10 08:57:52 +00:00
|
|
|
return dbp.SwitchThread(g.thread.ID)
|
2015-09-20 06:45:45 +00:00
|
|
|
}
|
|
|
|
dbp.SelectedGoroutine = g
|
|
|
|
return nil
|
2015-08-28 20:06:29 +00:00
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// GoroutinesInfo returns an array of G structures representing the information
|
2015-04-23 01:00:42 +00:00
|
|
|
// Delve cares about from the internal runtime G structure.
|
2015-06-20 22:54:52 +00:00
|
|
|
func (dbp *Process) GoroutinesInfo() ([]*G, error) {
|
2016-01-30 06:23:14 +00:00
|
|
|
if dbp.exited {
|
|
|
|
return nil, &ProcessExitedError{}
|
|
|
|
}
|
2015-09-17 17:33:42 +00:00
|
|
|
if dbp.allGCache != nil {
|
|
|
|
return dbp.allGCache, nil
|
|
|
|
}
|
|
|
|
|
2015-04-09 14:53:02 +00:00
|
|
|
var (
|
2015-06-17 17:11:57 +00:00
|
|
|
threadg = map[int]*Thread{}
|
|
|
|
allg []*G
|
|
|
|
rdr = dbp.DwarfReader()
|
2015-04-09 14:53:02 +00:00
|
|
|
)
|
|
|
|
|
2015-06-17 17:11:57 +00:00
|
|
|
for i := range dbp.Threads {
|
|
|
|
if dbp.Threads[i].blocked() {
|
|
|
|
continue
|
|
|
|
}
|
2015-06-28 15:00:56 +00:00
|
|
|
g, _ := dbp.Threads[i].GetG()
|
2015-06-17 17:11:57 +00:00
|
|
|
if g != nil {
|
2016-01-10 08:57:52 +00:00
|
|
|
threadg[g.ID] = dbp.Threads[i]
|
2015-06-17 17:11:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-09 16:25:26 +00:00
|
|
|
addr, err := rdr.AddrFor("runtime.allglen")
|
2015-04-09 14:53:02 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-05-09 16:25:26 +00:00
|
|
|
allglenBytes, err := dbp.CurrentThread.readMemory(uintptr(addr), 8)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
allglen := binary.LittleEndian.Uint64(allglenBytes)
|
|
|
|
|
|
|
|
rdr.Seek(0)
|
2015-10-28 12:17:37 +00:00
|
|
|
allgentryaddr, err := rdr.AddrFor("runtime.allgs")
|
2015-04-09 14:53:02 +00:00
|
|
|
if err != nil {
|
2015-10-28 12:17:37 +00:00
|
|
|
// try old name (pre Go 1.6)
|
|
|
|
allgentryaddr, err = rdr.AddrFor("runtime.allg")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-04-09 14:53:02 +00:00
|
|
|
}
|
2015-04-29 17:07:27 +00:00
|
|
|
faddr, err := dbp.CurrentThread.readMemory(uintptr(allgentryaddr), dbp.arch.PtrSize())
|
2015-04-09 14:53:02 +00:00
|
|
|
allgptr := binary.LittleEndian.Uint64(faddr)
|
|
|
|
|
|
|
|
for i := uint64(0); i < allglen; i++ {
|
2016-01-10 17:29:14 +00:00
|
|
|
gvar, err := dbp.CurrentThread.newGVariable(uintptr(allgptr+(i*uint64(dbp.arch.PtrSize()))), true)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
g, err := gvar.parseG()
|
2015-04-09 14:53:02 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-01-10 08:57:52 +00:00
|
|
|
if thread, allocated := threadg[g.ID]; allocated {
|
2015-07-08 02:21:47 +00:00
|
|
|
loc, err := thread.Location()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-06-17 17:11:57 +00:00
|
|
|
g.thread = thread
|
2015-07-08 02:21:47 +00:00
|
|
|
// Prefer actual thread location information.
|
2015-10-18 22:02:14 +00:00
|
|
|
g.CurrentLoc = *loc
|
2015-06-17 17:11:57 +00:00
|
|
|
}
|
2015-09-17 17:42:05 +00:00
|
|
|
if g.Status != Gdead {
|
|
|
|
allg = append(allg, g)
|
|
|
|
}
|
2015-04-09 14:53:02 +00:00
|
|
|
}
|
2015-09-17 17:33:42 +00:00
|
|
|
dbp.allGCache = allg
|
2015-04-09 14:53:02 +00:00
|
|
|
return allg, nil
|
|
|
|
}
|
|
|
|
|
2016-06-20 17:20:44 +00:00
|
|
|
func (g *G) Thread() *Thread {
|
|
|
|
return g.thread
|
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// Halt stops all threads.
|
2015-06-20 22:54:52 +00:00
|
|
|
func (dbp *Process) Halt() (err error) {
|
2016-01-30 06:23:14 +00:00
|
|
|
if dbp.exited {
|
|
|
|
return &ProcessExitedError{}
|
|
|
|
}
|
2015-04-13 22:17:06 +00:00
|
|
|
for _, th := range dbp.Threads {
|
|
|
|
if err := th.Halt(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// Registers obtains register values from the
|
|
|
|
// "current" thread of the traced process.
|
2015-06-20 22:54:52 +00:00
|
|
|
func (dbp *Process) Registers() (Registers, error) {
|
2014-12-05 22:17:10 +00:00
|
|
|
return dbp.CurrentThread.Registers()
|
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// PC returns the PC of the current thread.
|
2015-06-20 22:54:52 +00:00
|
|
|
func (dbp *Process) PC() (uint64, error) {
|
2015-04-23 15:40:33 +00:00
|
|
|
return dbp.CurrentThread.PC()
|
2014-12-05 22:17:10 +00:00
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// CurrentBreakpoint returns the breakpoint the current thread
|
|
|
|
// is stopped at.
|
2015-06-20 22:54:52 +00:00
|
|
|
func (dbp *Process) CurrentBreakpoint() *Breakpoint {
|
2015-04-20 18:03:22 +00:00
|
|
|
return dbp.CurrentThread.CurrentBreakpoint
|
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// DwarfReader returns a reader for the dwarf data
|
2015-06-20 22:54:52 +00:00
|
|
|
func (dbp *Process) DwarfReader() *reader.Reader {
|
2015-04-03 16:10:35 +00:00
|
|
|
return reader.New(dbp.dwarf)
|
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// Sources returns list of source files that comprise the debugged binary.
|
2015-06-20 22:54:52 +00:00
|
|
|
func (dbp *Process) Sources() map[string]*gosym.Obj {
|
2015-04-03 16:10:35 +00:00
|
|
|
return dbp.goSymTable.Files
|
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// Funcs returns list of functions present in the debugged program.
|
2015-06-20 22:54:52 +00:00
|
|
|
func (dbp *Process) Funcs() []gosym.Func {
|
2015-04-03 16:10:35 +00:00
|
|
|
return dbp.goSymTable.Funcs
|
|
|
|
}
|
|
|
|
|
2016-02-22 15:08:45 +00:00
|
|
|
// Types returns list of types present in the debugged program.
|
|
|
|
func (dbp *Process) Types() ([]string, error) {
|
2016-03-05 12:04:11 +00:00
|
|
|
types := make([]string, 0, len(dbp.types))
|
|
|
|
for k := range dbp.types {
|
|
|
|
types = append(types, k)
|
2016-02-22 15:08:45 +00:00
|
|
|
}
|
|
|
|
return types, nil
|
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// PCToLine converts an instruction address to a file/line/function.
|
2015-06-20 22:54:52 +00:00
|
|
|
func (dbp *Process) PCToLine(pc uint64) (string, int, *gosym.Func) {
|
2015-04-03 16:10:35 +00:00
|
|
|
return dbp.goSymTable.PCToLine(pc)
|
2014-12-31 13:34:41 +00:00
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// FindBreakpointByID finds the breakpoint for the given ID.
|
2015-06-20 22:54:52 +00:00
|
|
|
func (dbp *Process) FindBreakpointByID(id int) (*Breakpoint, bool) {
|
2015-06-13 04:47:30 +00:00
|
|
|
for _, bp := range dbp.Breakpoints {
|
|
|
|
if bp.ID == id {
|
|
|
|
return bp, true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// FindBreakpoint finds the breakpoint for the given pc.
|
2015-06-20 22:54:52 +00:00
|
|
|
func (dbp *Process) FindBreakpoint(pc uint64) (*Breakpoint, bool) {
|
2015-09-26 20:56:24 +00:00
|
|
|
// Check to see if address is past the breakpoint, (i.e. breakpoint was hit).
|
2015-06-17 18:09:55 +00:00
|
|
|
if bp, ok := dbp.Breakpoints[pc-uint64(dbp.arch.BreakpointSize())]; ok {
|
|
|
|
return bp, true
|
2015-03-28 01:12:07 +00:00
|
|
|
}
|
2015-09-26 20:56:24 +00:00
|
|
|
// Directly use addr to lookup breakpoint.
|
2015-06-12 19:32:32 +00:00
|
|
|
if bp, ok := dbp.Breakpoints[pc]; ok {
|
2015-03-28 01:12:07 +00:00
|
|
|
return bp, true
|
|
|
|
}
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
|
2015-06-20 22:54:52 +00:00
|
|
|
// Returns a new Process struct.
|
|
|
|
func initializeDebugProcess(dbp *Process, path string, attach bool) (*Process, error) {
|
2015-02-27 23:11:13 +00:00
|
|
|
if attach {
|
2015-06-13 04:47:30 +00:00
|
|
|
var err error
|
2016-01-15 05:26:54 +00:00
|
|
|
dbp.execPtraceFunc(func() { err = PtraceAttach(dbp.Pid) })
|
2015-02-27 23:11:13 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-10-04 18:16:39 +00:00
|
|
|
_, _, err = dbp.wait(dbp.Pid, 0)
|
2015-02-27 23:11:13 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-25 14:46:16 +00:00
|
|
|
proc, err := os.FindProcess(dbp.Pid)
|
2015-02-27 23:11:13 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
dbp.Process = proc
|
2015-04-25 14:46:16 +00:00
|
|
|
err = dbp.LoadInformation(path)
|
2015-02-27 23:11:13 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2015-08-28 20:06:29 +00:00
|
|
|
if err := dbp.updateThreadList(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2015-07-23 17:08:28 +00:00
|
|
|
ver, isextld, err := dbp.getGoInformation()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2015-07-28 05:33:07 +00:00
|
|
|
dbp.arch.SetGStructOffset(ver, isextld)
|
2015-08-28 20:06:29 +00:00
|
|
|
// SelectedGoroutine can not be set correctly by the call to updateThreadList
|
|
|
|
// because without calling SetGStructOffset we can not read the G struct of CurrentThread
|
|
|
|
// but without calling updateThreadList we can not examine memory to determine
|
|
|
|
// the offset of g struct inside TLS
|
|
|
|
dbp.SelectedGoroutine, _ = dbp.CurrentThread.GetG()
|
2015-07-23 17:08:28 +00:00
|
|
|
|
2016-03-06 17:54:43 +00:00
|
|
|
panicpc, err := dbp.FindFunctionLocation("runtime.startpanic", true, 0)
|
|
|
|
if err == nil {
|
|
|
|
bp, err := dbp.SetBreakpoint(panicpc)
|
|
|
|
if err == nil {
|
|
|
|
bp.Name = "unrecovered-panic"
|
|
|
|
bp.ID = -1
|
|
|
|
dbp.breakpointIDCounter--
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-25 14:46:16 +00:00
|
|
|
return dbp, nil
|
2015-02-27 23:11:13 +00:00
|
|
|
}
|
2015-04-20 18:03:22 +00:00
|
|
|
|
2016-04-24 23:20:02 +00:00
|
|
|
func (dbp *Process) ClearTempBreakpoints() error {
|
2015-06-12 19:32:32 +00:00
|
|
|
for _, bp := range dbp.Breakpoints {
|
2015-03-28 01:12:07 +00:00
|
|
|
if !bp.Temp {
|
|
|
|
continue
|
|
|
|
}
|
2015-06-20 23:01:06 +00:00
|
|
|
if _, err := dbp.ClearBreakpoint(bp.Addr); err != nil {
|
2015-03-28 01:12:07 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2015-11-18 09:07:08 +00:00
|
|
|
for i := range dbp.Threads {
|
|
|
|
if dbp.Threads[i].CurrentBreakpoint != nil && dbp.Threads[i].CurrentBreakpoint.Temp {
|
|
|
|
dbp.Threads[i].CurrentBreakpoint = nil
|
|
|
|
}
|
|
|
|
}
|
2015-03-28 01:12:07 +00:00
|
|
|
return nil
|
|
|
|
}
|
2015-04-20 18:03:22 +00:00
|
|
|
|
2015-06-20 22:54:52 +00:00
|
|
|
func (dbp *Process) run(fn func() error) error {
|
2015-09-17 17:33:42 +00:00
|
|
|
dbp.allGCache = nil
|
2015-03-21 22:31:53 +00:00
|
|
|
if dbp.exited {
|
2015-05-04 22:31:13 +00:00
|
|
|
return fmt.Errorf("process has already exited")
|
2015-03-21 22:31:53 +00:00
|
|
|
}
|
2015-04-20 18:03:22 +00:00
|
|
|
for _, th := range dbp.Threads {
|
|
|
|
th.CurrentBreakpoint = nil
|
2016-01-12 08:01:42 +00:00
|
|
|
th.BreakpointConditionMet = false
|
|
|
|
th.BreakpointConditionError = nil
|
2015-04-20 18:03:22 +00:00
|
|
|
}
|
2015-01-09 22:24:33 +00:00
|
|
|
if err := fn(); err != nil {
|
2015-07-07 20:51:19 +00:00
|
|
|
return err
|
2015-01-09 22:24:33 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2015-06-13 04:47:30 +00:00
|
|
|
|
2015-06-20 22:54:52 +00:00
|
|
|
func (dbp *Process) handlePtraceFuncs() {
|
2015-06-13 04:47:30 +00:00
|
|
|
// We must ensure here that we are running on the same thread during
|
2015-10-06 17:45:36 +00:00
|
|
|
// while invoking the ptrace(2) syscall. This is due to the fact that ptrace(2) expects
|
2015-06-13 04:47:30 +00:00
|
|
|
// all commands after PTRACE_ATTACH to come from the same thread.
|
|
|
|
runtime.LockOSThread()
|
|
|
|
|
|
|
|
for fn := range dbp.ptraceChan {
|
|
|
|
fn()
|
|
|
|
dbp.ptraceDoneChan <- nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-20 22:54:52 +00:00
|
|
|
func (dbp *Process) execPtraceFunc(fn func()) {
|
2015-06-13 04:47:30 +00:00
|
|
|
dbp.ptraceChan <- fn
|
|
|
|
<-dbp.ptraceDoneChan
|
|
|
|
}
|
2015-07-23 17:08:28 +00:00
|
|
|
|
|
|
|
func (dbp *Process) getGoInformation() (ver GoVersion, isextld bool, err error) {
|
2016-04-24 17:15:39 +00:00
|
|
|
vv, err := dbp.EvalPackageVariable("runtime.buildVersion", LoadConfig{true, 0, 64, 0, 0})
|
2015-07-23 17:08:28 +00:00
|
|
|
if err != nil {
|
2015-12-19 12:52:09 +00:00
|
|
|
err = fmt.Errorf("Could not determine version number: %v\n", err)
|
|
|
|
return
|
2015-07-23 17:08:28 +00:00
|
|
|
}
|
2015-11-18 12:08:53 +00:00
|
|
|
if vv.Unreadable != nil {
|
|
|
|
err = fmt.Errorf("Unreadable version number: %v\n", vv.Unreadable)
|
|
|
|
return
|
|
|
|
}
|
2015-07-23 17:08:28 +00:00
|
|
|
|
2016-02-25 09:48:42 +00:00
|
|
|
ver, ok := ParseVersionString(constant.StringVal(vv.Value))
|
2015-07-23 17:08:28 +00:00
|
|
|
if !ok {
|
2015-10-18 17:37:13 +00:00
|
|
|
err = fmt.Errorf("Could not parse version number: %v\n", vv.Value)
|
2015-07-28 12:51:09 +00:00
|
|
|
return
|
2015-07-23 17:08:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
rdr := dbp.DwarfReader()
|
|
|
|
rdr.Seek(0)
|
|
|
|
for entry, err := rdr.NextCompileUnit(); entry != nil; entry, err = rdr.NextCompileUnit() {
|
|
|
|
if err != nil {
|
|
|
|
return ver, isextld, err
|
|
|
|
}
|
|
|
|
if prod, ok := entry.Val(dwarf.AttrProducer).(string); ok && (strings.HasPrefix(prod, "GNU AS")) {
|
|
|
|
isextld = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
2015-08-28 20:06:29 +00:00
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// FindGoroutine returns a G struct representing the goroutine
|
|
|
|
// specified by `gid`.
|
2015-08-28 20:06:29 +00:00
|
|
|
func (dbp *Process) FindGoroutine(gid int) (*G, error) {
|
|
|
|
if gid == -1 {
|
|
|
|
return dbp.SelectedGoroutine, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
gs, err := dbp.GoroutinesInfo()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for i := range gs {
|
2016-01-10 08:57:52 +00:00
|
|
|
if gs[i].ID == gid {
|
2015-08-28 20:06:29 +00:00
|
|
|
return gs[i], nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("Unknown goroutine %d", gid)
|
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// ConvertEvalScope returns a new EvalScope in the context of the
|
|
|
|
// specified goroutine ID and stack frame.
|
2015-08-28 20:06:29 +00:00
|
|
|
func (dbp *Process) ConvertEvalScope(gid, frame int) (*EvalScope, error) {
|
2016-01-30 06:23:14 +00:00
|
|
|
if dbp.exited {
|
|
|
|
return nil, &ProcessExitedError{}
|
|
|
|
}
|
2015-08-28 20:06:29 +00:00
|
|
|
g, err := dbp.FindGoroutine(gid)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if g == nil {
|
|
|
|
return dbp.CurrentThread.Scope()
|
|
|
|
}
|
|
|
|
|
|
|
|
var out EvalScope
|
|
|
|
|
|
|
|
if g.thread == nil {
|
|
|
|
out.Thread = dbp.CurrentThread
|
|
|
|
} else {
|
|
|
|
out.Thread = g.thread
|
|
|
|
}
|
|
|
|
|
2016-03-18 08:51:48 +00:00
|
|
|
locs, err := g.Stacktrace(frame)
|
2015-08-28 20:06:29 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if frame >= len(locs) {
|
|
|
|
return nil, fmt.Errorf("Frame %d does not exist in goroutine %d", frame, gid)
|
|
|
|
}
|
|
|
|
|
2015-09-17 08:42:34 +00:00
|
|
|
out.PC, out.CFA = locs[frame].Current.PC, locs[frame].CFA
|
2015-08-28 20:06:29 +00:00
|
|
|
|
|
|
|
return &out, nil
|
|
|
|
}
|
2015-10-09 23:30:28 +00:00
|
|
|
|
|
|
|
func (dbp *Process) postExit() {
|
|
|
|
dbp.exited = true
|
|
|
|
close(dbp.ptraceChan)
|
|
|
|
close(dbp.ptraceDoneChan)
|
|
|
|
}
|