2015-06-12 19:49:23 +00:00
|
|
|
package proc
|
2014-12-05 22:17:10 +00:00
|
|
|
|
|
|
|
import (
|
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"
|
|
|
|
"go/token"
|
2016-04-14 09:58:59 +00:00
|
|
|
"path/filepath"
|
2016-01-12 08:01:42 +00:00
|
|
|
"strconv"
|
2014-12-05 22:17:10 +00:00
|
|
|
)
|
|
|
|
|
2016-04-21 10:19:21 +00:00
|
|
|
var NotExecutableErr = errors.New("not an executable file")
|
2017-05-05 22:17:52 +00:00
|
|
|
var NotRecordedErr = errors.New("not a recording")
|
2016-04-21 10:19:21 +00:00
|
|
|
|
2017-09-20 12:19:59 +00:00
|
|
|
const UnrecoveredPanic = "unrecovered-panic"
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
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.
|
2017-04-28 17:15:39 +00:00
|
|
|
func FindFileLocation(p Process, fileName string, lineno int) (uint64, error) {
|
2017-09-01 13:30:45 +00:00
|
|
|
pc, fn, err := p.BinInfo().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 {
|
2017-04-28 17:15:39 +00:00
|
|
|
pc, _ = FirstPCAfterPrologue(p, fn, true)
|
2016-02-11 07:18:39 +00:00
|
|
|
}
|
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
|
2017-04-28 17:15:39 +00:00
|
|
|
func FindFunctionLocation(p Process, funcName string, firstLine bool, lineOffset int) (uint64, error) {
|
|
|
|
bi := p.BinInfo()
|
2017-09-01 13:30:45 +00:00
|
|
|
origfn := bi.LookupFunc[funcName]
|
2015-10-22 17:07:24 +00:00
|
|
|
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 {
|
2017-04-28 17:15:39 +00:00
|
|
|
return FirstPCAfterPrologue(p, origfn, false)
|
2015-08-07 16:50:14 +00:00
|
|
|
} else if lineOffset > 0 {
|
2017-08-18 17:49:29 +00:00
|
|
|
filename, lineno := origfn.cu.lineInfo.PCToLine(origfn.Entry, origfn.Entry)
|
2017-09-01 13:30:45 +00:00
|
|
|
breakAddr, _, err := bi.LineToPC(filename, lineno+lineOffset)
|
2015-08-07 16:50:14 +00:00
|
|
|
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-10 08:57:52 +00:00
|
|
|
// Next continues execution until the next source line.
|
2017-04-21 07:50:38 +00:00
|
|
|
func Next(dbp Process) (err error) {
|
2017-02-22 08:35:21 +00:00
|
|
|
if dbp.Exited() {
|
2017-07-26 22:57:47 +00:00
|
|
|
return &ProcessExitedError{Pid: dbp.Pid()}
|
2016-01-30 06:23:14 +00:00
|
|
|
}
|
2017-09-25 06:29:13 +00:00
|
|
|
if dbp.Breakpoints().HasInternalBreakpoints() {
|
|
|
|
return fmt.Errorf("next while nexting")
|
2015-04-25 02:56:57 +00:00
|
|
|
}
|
|
|
|
|
2017-02-22 08:35:21 +00:00
|
|
|
if err = next(dbp, false); err != nil {
|
2017-07-24 08:58:12 +00:00
|
|
|
dbp.ClearInternalBreakpoints()
|
|
|
|
return
|
2015-08-22 03:13:54 +00:00
|
|
|
}
|
|
|
|
|
2017-02-22 08:35:21 +00:00
|
|
|
return Continue(dbp)
|
|
|
|
}
|
|
|
|
|
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.
|
2017-04-21 07:50:38 +00:00
|
|
|
func Continue(dbp Process) error {
|
2017-07-26 22:57:47 +00:00
|
|
|
if dbp.Exited() {
|
|
|
|
return &ProcessExitedError{Pid: dbp.Pid()}
|
|
|
|
}
|
2017-07-07 23:29:37 +00:00
|
|
|
dbp.ManualStopRequested()
|
2015-11-18 09:07:08 +00:00
|
|
|
for {
|
2017-07-07 23:29:37 +00:00
|
|
|
if dbp.ManualStopRequested() {
|
|
|
|
return nil
|
|
|
|
}
|
2017-02-22 08:35:21 +00:00
|
|
|
trapthread, err := dbp.ContinueOnce()
|
|
|
|
if err != nil {
|
2015-11-18 09:07:08 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-02-22 08:35:21 +00:00
|
|
|
threads := dbp.ThreadList()
|
2015-11-18 09:07:08 +00:00
|
|
|
|
2017-02-22 08:35:21 +00:00
|
|
|
if err := pickCurrentThread(dbp, trapthread, threads); err != nil {
|
2015-07-10 19:48:45 +00:00
|
|
|
return err
|
|
|
|
}
|
2016-01-16 08:13:15 +00:00
|
|
|
|
2017-02-22 08:35:21 +00:00
|
|
|
curthread := dbp.CurrentThread()
|
2017-09-25 06:29:13 +00:00
|
|
|
curbp := curthread.Breakpoint()
|
2017-02-22 08:35:21 +00:00
|
|
|
|
2015-11-24 21:48:28 +00:00
|
|
|
switch {
|
2017-09-25 06:29:13 +00:00
|
|
|
case curbp.Breakpoint == nil:
|
2015-11-24 21:48:28 +00:00
|
|
|
// runtime.Breakpoint or manual stop
|
2017-05-05 22:17:52 +00:00
|
|
|
if recorded, _ := dbp.Recorded(); onRuntimeBreakpoint(curthread) && !recorded {
|
2017-02-07 21:07:18 +00:00
|
|
|
// Single-step current thread until we exit runtime.breakpoint and
|
|
|
|
// runtime.Breakpoint.
|
|
|
|
// On go < 1.8 it was sufficient to single-step twice on go1.8 a change
|
|
|
|
// to the compiler requires 4 steps.
|
|
|
|
for {
|
2017-02-22 08:35:21 +00:00
|
|
|
if err = curthread.StepInstruction(); err != nil {
|
2015-11-24 21:48:28 +00:00
|
|
|
return err
|
|
|
|
}
|
2017-02-22 08:35:21 +00:00
|
|
|
loc, err := curthread.Location()
|
2017-02-07 21:07:18 +00:00
|
|
|
if err != nil || loc.Fn == nil || (loc.Fn.Name != "runtime.breakpoint" && loc.Fn.Name != "runtime.Breakpoint") {
|
|
|
|
break
|
|
|
|
}
|
2015-07-10 19:48:45 +00:00
|
|
|
}
|
2015-03-01 04:03:26 +00:00
|
|
|
}
|
2017-02-22 08:35:21 +00:00
|
|
|
return conditionErrors(threads)
|
2017-09-25 06:29:13 +00:00
|
|
|
case curbp.Active && curbp.Internal:
|
2017-02-22 08:35:21 +00:00
|
|
|
if curbp.Kind == StepBreakpoint {
|
2016-07-26 13:34:31 +00:00
|
|
|
// See description of proc.(*Process).next for the meaning of StepBreakpoints
|
2017-02-22 08:35:21 +00:00
|
|
|
if err := conditionErrors(threads); err != nil {
|
2016-07-26 13:34:31 +00:00
|
|
|
return err
|
|
|
|
}
|
2017-02-22 08:35:21 +00:00
|
|
|
regs, err := curthread.Registers(false)
|
2017-04-13 23:19:57 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-02-22 08:35:21 +00:00
|
|
|
pc := regs.PC()
|
2017-11-06 17:34:59 +00:00
|
|
|
text, err := disassemble(curthread, regs, dbp.Breakpoints(), dbp.BinInfo(), pc, pc+maxInstructionLength, true)
|
2016-07-26 13:34:31 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// here we either set a breakpoint into the destination of the CALL
|
|
|
|
// instruction or we determined that the called function is hidden,
|
|
|
|
// either way we need to resume execution
|
2017-04-21 06:55:53 +00:00
|
|
|
if err = setStepIntoBreakpoint(dbp, text, SameGoroutineCondition(dbp.SelectedGoroutine())); err != nil {
|
2016-07-26 13:34:31 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
2016-09-30 06:35:29 +00:00
|
|
|
if err := dbp.ClearInternalBreakpoints(); err != nil {
|
2016-07-26 13:34:31 +00:00
|
|
|
return err
|
|
|
|
}
|
2017-02-22 08:35:21 +00:00
|
|
|
return conditionErrors(threads)
|
2016-01-12 08:01:42 +00:00
|
|
|
}
|
2017-09-25 06:29:13 +00:00
|
|
|
case curbp.Active:
|
2017-02-22 08:35:21 +00:00
|
|
|
onNextGoroutine, err := onNextGoroutine(curthread, dbp.Breakpoints())
|
2016-01-12 08:01:42 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
2016-01-16 08:13:15 +00:00
|
|
|
}
|
2016-01-12 08:01:42 +00:00
|
|
|
if onNextGoroutine {
|
2016-09-30 06:35:29 +00:00
|
|
|
err := dbp.ClearInternalBreakpoints()
|
2016-01-12 08:01:42 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2017-09-20 12:19:59 +00:00
|
|
|
if curbp.Name == UnrecoveredPanic {
|
|
|
|
dbp.ClearInternalBreakpoints()
|
|
|
|
}
|
2017-02-22 08:35:21 +00:00
|
|
|
return conditionErrors(threads)
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-21 07:50:38 +00:00
|
|
|
func conditionErrors(threads []Thread) error {
|
2016-01-12 08:01:42 +00:00
|
|
|
var condErr error
|
2017-02-22 08:35:21 +00:00
|
|
|
for _, th := range threads {
|
2017-09-25 06:29:13 +00:00
|
|
|
if bp := th.Breakpoint(); bp.Breakpoint != nil && bp.CondError != nil {
|
2016-01-12 08:01:42 +00:00
|
|
|
if condErr == nil {
|
2017-09-25 06:29:13 +00:00
|
|
|
condErr = bp.CondError
|
2016-01-12 08:01:42 +00:00
|
|
|
} else {
|
|
|
|
return fmt.Errorf("multiple errors evaluating conditions")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return condErr
|
|
|
|
}
|
|
|
|
|
2017-02-08 00:23:47 +00:00
|
|
|
// pick a new dbp.currentThread, with the following priority:
|
2016-09-30 06:35:29 +00:00
|
|
|
// - a thread with onTriggeredInternalBreakpoint() == true
|
2015-11-24 21:48:28 +00:00
|
|
|
// - a thread with onTriggeredBreakpoint() == true (prioritizing trapthread)
|
|
|
|
// - trapthread
|
2017-04-21 07:50:38 +00:00
|
|
|
func pickCurrentThread(dbp Process, trapthread Thread, threads []Thread) error {
|
2017-02-22 08:35:21 +00:00
|
|
|
for _, th := range threads {
|
2017-09-25 06:29:13 +00:00
|
|
|
if bp := th.Breakpoint(); bp.Active && bp.Internal {
|
2017-02-22 08:35:21 +00:00
|
|
|
return dbp.SwitchThread(th.ThreadID())
|
2015-11-24 21:48:28 +00:00
|
|
|
}
|
|
|
|
}
|
2017-09-25 06:29:13 +00:00
|
|
|
if bp := trapthread.Breakpoint(); bp.Active {
|
2017-02-22 08:35:21 +00:00
|
|
|
return dbp.SwitchThread(trapthread.ThreadID())
|
2015-11-24 21:48:28 +00:00
|
|
|
}
|
2017-02-22 08:35:21 +00:00
|
|
|
for _, th := range threads {
|
2017-09-25 06:29:13 +00:00
|
|
|
if bp := th.Breakpoint(); bp.Active {
|
2017-02-22 08:35:21 +00:00
|
|
|
return dbp.SwitchThread(th.ThreadID())
|
2015-11-24 21:48:28 +00:00
|
|
|
}
|
|
|
|
}
|
2017-02-22 08:35:21 +00:00
|
|
|
return dbp.SwitchThread(trapthread.ThreadID())
|
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.
|
2017-04-21 07:50:38 +00:00
|
|
|
func Step(dbp Process) (err error) {
|
2017-02-22 08:35:21 +00:00
|
|
|
if dbp.Exited() {
|
2017-07-26 22:57:47 +00:00
|
|
|
return &ProcessExitedError{Pid: dbp.Pid()}
|
2016-01-24 05:25:46 +00:00
|
|
|
}
|
2017-09-25 06:29:13 +00:00
|
|
|
if dbp.Breakpoints().HasInternalBreakpoints() {
|
|
|
|
return fmt.Errorf("next while nexting")
|
2016-04-24 23:20:02 +00:00
|
|
|
}
|
2016-04-13 13:25:23 +00:00
|
|
|
|
2017-02-22 08:35:21 +00:00
|
|
|
if err = next(dbp, true); err != nil {
|
2016-07-26 13:34:31 +00:00
|
|
|
switch err.(type) {
|
2018-01-20 15:29:18 +00:00
|
|
|
case ThreadBlockedError: // Noop
|
2016-07-26 13:34:31 +00:00
|
|
|
default:
|
2016-09-30 06:35:29 +00:00
|
|
|
dbp.ClearInternalBreakpoints()
|
2016-07-26 13:34:31 +00:00
|
|
|
return
|
|
|
|
}
|
2016-02-12 07:43:22 +00:00
|
|
|
}
|
2016-07-26 13:34:31 +00:00
|
|
|
|
2017-02-22 08:35:21 +00:00
|
|
|
return Continue(dbp)
|
2016-02-12 07:43:22 +00:00
|
|
|
}
|
|
|
|
|
2017-04-21 06:55:53 +00:00
|
|
|
// SameGoroutineCondition returns an expression that evaluates to true when
|
|
|
|
// the current goroutine is g.
|
|
|
|
func SameGoroutineCondition(g *G) ast.Expr {
|
2016-04-11 11:50:01 +00:00
|
|
|
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)},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-16 18:23:33 +00:00
|
|
|
func frameoffCondition(frameoff int64) ast.Expr {
|
|
|
|
return &ast.BinaryExpr{
|
|
|
|
Op: token.EQL,
|
|
|
|
X: &ast.SelectorExpr{
|
|
|
|
X: &ast.Ident{Name: "runtime"},
|
|
|
|
Sel: &ast.Ident{Name: "frameoff"},
|
|
|
|
},
|
|
|
|
Y: &ast.BasicLit{Kind: token.INT, Value: strconv.FormatInt(frameoff, 10)},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func andFrameoffCondition(cond ast.Expr, frameoff int64) ast.Expr {
|
|
|
|
if cond == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return &ast.BinaryExpr{
|
|
|
|
Op: token.LAND,
|
|
|
|
X: cond,
|
|
|
|
Y: frameoffCondition(frameoff),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-14 09:58:59 +00:00
|
|
|
// StepOut will continue until the current goroutine exits the
|
|
|
|
// function currently being executed or a deferred function is executed
|
2017-04-21 07:50:38 +00:00
|
|
|
func StepOut(dbp Process) error {
|
2017-07-26 22:57:47 +00:00
|
|
|
if dbp.Exited() {
|
|
|
|
return &ProcessExitedError{Pid: dbp.Pid()}
|
|
|
|
}
|
2017-02-22 08:35:21 +00:00
|
|
|
selg := dbp.SelectedGoroutine()
|
|
|
|
curthread := dbp.CurrentThread()
|
2016-04-14 09:58:59 +00:00
|
|
|
|
2017-05-16 18:23:33 +00:00
|
|
|
topframe, retframe, err := topframe(selg, curthread)
|
2016-04-14 09:58:59 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-05-16 18:23:33 +00:00
|
|
|
sameGCond := SameGoroutineCondition(selg)
|
2017-09-01 13:34:13 +00:00
|
|
|
retFrameCond := andFrameoffCondition(sameGCond, retframe.FrameOffset())
|
2016-04-14 09:58:59 +00:00
|
|
|
|
|
|
|
var deferpc uint64 = 0
|
|
|
|
if filepath.Ext(topframe.Current.File) == ".go" {
|
2017-02-22 08:35:21 +00:00
|
|
|
if selg != nil {
|
|
|
|
deferPCEntry := selg.DeferPC()
|
2017-02-07 21:08:11 +00:00
|
|
|
if deferPCEntry != 0 {
|
2017-02-22 08:35:21 +00:00
|
|
|
_, _, deferfn := dbp.BinInfo().PCToLine(deferPCEntry)
|
2017-04-28 17:15:39 +00:00
|
|
|
deferpc, err = FirstPCAfterPrologue(dbp, deferfn, false)
|
2017-02-07 21:08:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-04-14 09:58:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if topframe.Ret == 0 && deferpc == 0 {
|
|
|
|
return errors.New("nothing to stepout to")
|
|
|
|
}
|
|
|
|
|
|
|
|
if deferpc != 0 && deferpc != topframe.Current.PC {
|
2017-05-16 18:23:33 +00:00
|
|
|
bp, err := dbp.SetBreakpoint(deferpc, NextDeferBreakpoint, sameGCond)
|
2016-04-14 09:58:59 +00:00
|
|
|
if err != nil {
|
|
|
|
if _, ok := err.(BreakpointExistsError); !ok {
|
|
|
|
dbp.ClearInternalBreakpoints()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if bp != nil {
|
|
|
|
// For StepOut we do not want to step into the deferred function
|
|
|
|
// when it's called by runtime.deferreturn so we do not populate
|
|
|
|
// DeferReturns.
|
|
|
|
bp.DeferReturns = []uint64{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if topframe.Ret != 0 {
|
2017-05-16 18:23:33 +00:00
|
|
|
_, err := dbp.SetBreakpoint(topframe.Ret, NextBreakpoint, retFrameCond)
|
|
|
|
if err != nil {
|
|
|
|
if _, isexists := err.(BreakpointExistsError); !isexists {
|
|
|
|
dbp.ClearInternalBreakpoints()
|
|
|
|
return err
|
|
|
|
}
|
2016-04-14 09:58:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-25 06:29:13 +00:00
|
|
|
if bp := curthread.Breakpoint(); bp.Breakpoint == nil {
|
2017-05-16 18:23:33 +00:00
|
|
|
curthread.SetCurrentBreakpoint()
|
|
|
|
}
|
|
|
|
|
2017-02-22 08:35:21 +00:00
|
|
|
return Continue(dbp)
|
2016-04-14 09:58:59 +00:00
|
|
|
}
|
|
|
|
|
2017-02-22 08:35:21 +00:00
|
|
|
// If the argument of GoroutinesInfo implements AllGCache GoroutinesInfo
|
|
|
|
// will use the pointer returned by AllGCache as a cache.
|
|
|
|
type AllGCache interface {
|
|
|
|
AllGCache() *[]*G
|
|
|
|
}
|
|
|
|
|
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.
|
2017-04-21 07:50:38 +00:00
|
|
|
func GoroutinesInfo(dbp Process) ([]*G, error) {
|
2017-02-22 08:35:21 +00:00
|
|
|
if dbp.Exited() {
|
2017-07-26 22:57:47 +00:00
|
|
|
return nil, &ProcessExitedError{Pid: dbp.Pid()}
|
2016-01-30 06:23:14 +00:00
|
|
|
}
|
2017-02-22 08:35:21 +00:00
|
|
|
if dbp, ok := dbp.(AllGCache); ok {
|
|
|
|
if allGCache := dbp.AllGCache(); *allGCache != nil {
|
|
|
|
return *allGCache, nil
|
|
|
|
}
|
2015-09-17 17:33:42 +00:00
|
|
|
}
|
|
|
|
|
2015-04-09 14:53:02 +00:00
|
|
|
var (
|
2017-09-01 13:34:13 +00:00
|
|
|
threadg = map[int]*G{}
|
2015-06-17 17:11:57 +00:00
|
|
|
allg []*G
|
2017-02-22 08:35:21 +00:00
|
|
|
rdr = dbp.BinInfo().DwarfReader()
|
2015-04-09 14:53:02 +00:00
|
|
|
)
|
|
|
|
|
2017-02-22 08:35:21 +00:00
|
|
|
threads := dbp.ThreadList()
|
|
|
|
for _, th := range threads {
|
2017-04-21 06:55:53 +00:00
|
|
|
if th.Blocked() {
|
2015-06-17 17:11:57 +00:00
|
|
|
continue
|
|
|
|
}
|
2017-02-22 08:35:21 +00:00
|
|
|
g, _ := GetG(th)
|
2015-06-17 17:11:57 +00:00
|
|
|
if g != nil {
|
2017-09-01 13:34:13 +00:00
|
|
|
threadg[g.ID] = g
|
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
|
|
|
|
}
|
2017-04-18 14:24:45 +00:00
|
|
|
allglenBytes := make([]byte, 8)
|
|
|
|
_, err = dbp.CurrentThread().ReadMemory(allglenBytes, uintptr(addr))
|
2015-05-09 16:25:26 +00:00
|
|
|
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
|
|
|
}
|
2017-04-21 06:55:53 +00:00
|
|
|
faddr := make([]byte, dbp.BinInfo().Arch.PtrSize())
|
2017-04-18 14:24:45 +00:00
|
|
|
_, err = dbp.CurrentThread().ReadMemory(faddr, uintptr(allgentryaddr))
|
2017-06-29 18:15:59 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-04-09 14:53:02 +00:00
|
|
|
allgptr := binary.LittleEndian.Uint64(faddr)
|
|
|
|
|
|
|
|
for i := uint64(0); i < allglen; i++ {
|
2017-04-21 06:55:53 +00:00
|
|
|
gvar, err := newGVariable(dbp.CurrentThread(), uintptr(allgptr+(i*uint64(dbp.BinInfo().Arch.PtrSize()))), true)
|
2016-01-10 17:29:14 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
g, err := gvar.parseG()
|
2015-04-09 14:53:02 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-09-01 13:34:13 +00:00
|
|
|
if thg, allocated := threadg[g.ID]; allocated {
|
|
|
|
loc, err := thg.Thread.Location()
|
2015-07-08 02:21:47 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-09-01 13:34:13 +00:00
|
|
|
g.Thread = thg.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
|
2017-09-01 13:34:13 +00:00
|
|
|
g.SystemStack = thg.SystemStack
|
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
|
|
|
}
|
2017-02-22 08:35:21 +00:00
|
|
|
if dbp, ok := dbp.(AllGCache); ok {
|
|
|
|
allGCache := dbp.AllGCache()
|
|
|
|
*allGCache = allg
|
|
|
|
}
|
|
|
|
|
2015-04-09 14:53:02 +00:00
|
|
|
return allg, nil
|
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// FindGoroutine returns a G struct representing the goroutine
|
|
|
|
// specified by `gid`.
|
2017-04-21 07:50:38 +00:00
|
|
|
func FindGoroutine(dbp Process, gid int) (*G, error) {
|
2015-08-28 20:06:29 +00:00
|
|
|
if gid == -1 {
|
2017-02-15 13:41:03 +00:00
|
|
|
return dbp.SelectedGoroutine(), nil
|
2015-08-28 20:06:29 +00:00
|
|
|
}
|
|
|
|
|
2017-02-22 08:35:21 +00:00
|
|
|
gs, err := GoroutinesInfo(dbp)
|
2015-08-28 20:06:29 +00:00
|
|
|
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.
|
2017-04-21 07:50:38 +00:00
|
|
|
func ConvertEvalScope(dbp Process, gid, frame int) (*EvalScope, error) {
|
2017-07-26 22:57:47 +00:00
|
|
|
if dbp.Exited() {
|
|
|
|
return nil, &ProcessExitedError{Pid: dbp.Pid()}
|
|
|
|
}
|
2017-02-15 13:41:03 +00:00
|
|
|
ct := dbp.CurrentThread()
|
|
|
|
g, err := FindGoroutine(dbp, gid)
|
2015-08-28 20:06:29 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if g == nil {
|
2017-02-15 13:41:03 +00:00
|
|
|
return ThreadScope(ct)
|
2015-08-28 20:06:29 +00:00
|
|
|
}
|
|
|
|
|
2017-04-21 06:55:53 +00:00
|
|
|
var thread MemoryReadWriter
|
|
|
|
if g.Thread == nil {
|
2017-02-15 13:41:03 +00:00
|
|
|
thread = ct
|
2015-08-28 20:06:29 +00:00
|
|
|
} else {
|
2017-04-21 06:55:53 +00:00
|
|
|
thread = g.Thread
|
2015-08-28 20:06:29 +00:00
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2017-09-01 13:34:13 +00:00
|
|
|
return &EvalScope{locs[frame].Current.PC, locs[frame].Regs, thread, g.variable, dbp.BinInfo(), locs[frame].FrameOffset()}, nil
|
2015-08-28 20:06:29 +00:00
|
|
|
}
|
2015-10-09 23:30:28 +00:00
|
|
|
|
2017-02-15 13:41:03 +00:00
|
|
|
// FrameToScope returns a new EvalScope for this frame
|
2017-04-21 07:50:38 +00:00
|
|
|
func FrameToScope(p Process, frame Stackframe) *EvalScope {
|
2017-09-01 13:34:13 +00:00
|
|
|
return &EvalScope{frame.Current.PC, frame.Regs, p.CurrentThread(), nil, p.BinInfo(), frame.FrameOffset()}
|
2017-02-15 13:41:03 +00:00
|
|
|
}
|