proc,service,terminal: read defer list
Adds -defer flag to the stack command that decorates the stack traces by associating each stack frame with its deferred calls. Reworks proc.next to use this feature instead of using proc.DeferPC, laying the groundwork to implement #1240.
This commit is contained in:
parent
932aad9e3d
commit
8f1fc63da8
32
_fixtures/deferstack.go
Normal file
32
_fixtures/deferstack.go
Normal file
@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import "runtime"
|
||||
|
||||
func f1() {
|
||||
}
|
||||
|
||||
func f2() {
|
||||
}
|
||||
|
||||
func f3() {
|
||||
}
|
||||
|
||||
func call1() {
|
||||
defer f2()
|
||||
defer f1()
|
||||
call2()
|
||||
}
|
||||
|
||||
func call2() {
|
||||
defer f3()
|
||||
defer f2()
|
||||
call3()
|
||||
}
|
||||
|
||||
func call3() {
|
||||
runtime.Breakpoint()
|
||||
}
|
||||
|
||||
func main() {
|
||||
call1()
|
||||
}
|
@ -185,7 +185,7 @@ func TestCore(t *testing.T) {
|
||||
var panickingStack []proc.Stackframe
|
||||
for _, g := range gs {
|
||||
t.Logf("Goroutine %d", g.ID)
|
||||
stack, err := g.Stacktrace(10)
|
||||
stack, err := g.Stacktrace(10, false)
|
||||
if err != nil {
|
||||
t.Errorf("Stacktrace() on goroutine %v = %v", g, err)
|
||||
}
|
||||
@ -329,7 +329,7 @@ func TestCoreWithEmptyString(t *testing.T) {
|
||||
var mainFrame *proc.Stackframe
|
||||
mainSearch:
|
||||
for _, g := range gs {
|
||||
stack, err := g.Stacktrace(10)
|
||||
stack, err := g.Stacktrace(10, false)
|
||||
assertNoError(err, t, "Stacktrace()")
|
||||
for _, frame := range stack {
|
||||
if frame.Current.Fn != nil && frame.Current.Fn.Name == "main.main" {
|
||||
|
@ -380,14 +380,11 @@ func StepOut(dbp Process) error {
|
||||
|
||||
var deferpc uint64 = 0
|
||||
if filepath.Ext(topframe.Current.File) == ".go" {
|
||||
if selg != nil {
|
||||
deferPCEntry := selg.DeferPC()
|
||||
if deferPCEntry != 0 {
|
||||
deferfn := dbp.BinInfo().PCToFunc(deferPCEntry)
|
||||
deferpc, err = FirstPCAfterPrologue(dbp, deferfn, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if topframe.TopmostDefer != nil && topframe.TopmostDefer.DeferredPC != 0 {
|
||||
deferfn := dbp.BinInfo().PCToFunc(topframe.TopmostDefer.DeferredPC)
|
||||
deferpc, err = FirstPCAfterPrologue(dbp, deferfn, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -554,7 +551,7 @@ func ConvertEvalScope(dbp Process, gid, frame int) (*EvalScope, error) {
|
||||
thread = g.Thread
|
||||
}
|
||||
|
||||
locs, err := g.Stacktrace(frame + 1)
|
||||
locs, err := g.Stacktrace(frame+1, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -920,7 +920,7 @@ func TestStacktraceGoroutine(t *testing.T) {
|
||||
mainCount := 0
|
||||
|
||||
for i, g := range gs {
|
||||
locations, err := g.Stacktrace(40)
|
||||
locations, err := g.Stacktrace(40, false)
|
||||
if err != nil {
|
||||
// On windows we do not have frame information for goroutines doing system calls.
|
||||
t.Logf("Could not retrieve goroutine stack for goid=%d: %v", g.ID, err)
|
||||
@ -1237,13 +1237,13 @@ func TestFrameEvaluation(t *testing.T) {
|
||||
found := make([]bool, 10)
|
||||
for _, g := range gs {
|
||||
frame := -1
|
||||
frames, err := g.Stacktrace(10)
|
||||
frames, err := g.Stacktrace(10, false)
|
||||
if err != nil {
|
||||
t.Logf("could not stacktrace goroutine %d: %v\n", g.ID, err)
|
||||
continue
|
||||
}
|
||||
t.Logf("Goroutine %d", g.ID)
|
||||
logStacktrace(t, frames)
|
||||
logStacktrace(t, p.BinInfo(), frames)
|
||||
for i := range frames {
|
||||
if frames[i].Call.Fn != nil && frames[i].Call.Fn.Name == "main.agoroutine" {
|
||||
frame = i
|
||||
@ -1957,7 +1957,7 @@ func TestNextParked(t *testing.T) {
|
||||
if g.Thread != nil {
|
||||
continue
|
||||
}
|
||||
frames, _ := g.Stacktrace(5)
|
||||
frames, _ := g.Stacktrace(5, false)
|
||||
for _, frame := range frames {
|
||||
// line 11 is the line where wg.Done is called
|
||||
if frame.Current.Fn != nil && frame.Current.Fn.Name == "main.sayhi" && frame.Current.Line < 11 {
|
||||
@ -2010,7 +2010,7 @@ func TestStepParked(t *testing.T) {
|
||||
}
|
||||
|
||||
t.Logf("Parked g is: %v\n", parkedg)
|
||||
frames, _ := parkedg.Stacktrace(20)
|
||||
frames, _ := parkedg.Stacktrace(20, false)
|
||||
for _, frame := range frames {
|
||||
name := ""
|
||||
if frame.Call.Fn != nil {
|
||||
@ -2714,7 +2714,7 @@ func TestStacktraceWithBarriers(t *testing.T) {
|
||||
goid, _ := constant.Int64Val(goidVar.Value)
|
||||
|
||||
if g := getg(int(goid), gs); g != nil {
|
||||
stack, err := g.Stacktrace(50)
|
||||
stack, err := g.Stacktrace(50, false)
|
||||
assertNoError(err, t, fmt.Sprintf("Stacktrace(goroutine = %d)", goid))
|
||||
for _, frame := range stack {
|
||||
if frame.Current.Fn != nil && frame.Current.Fn.Name == "main.bottomUpTree" {
|
||||
@ -2740,7 +2740,7 @@ func TestStacktraceWithBarriers(t *testing.T) {
|
||||
for _, goid := range stackBarrierGoids {
|
||||
g := getg(goid, gs)
|
||||
|
||||
stack, err := g.Stacktrace(200)
|
||||
stack, err := g.Stacktrace(200, false)
|
||||
assertNoError(err, t, "Stacktrace()")
|
||||
|
||||
// Check that either main.main or main.main.func1 appear in the
|
||||
@ -3142,7 +3142,7 @@ func TestIssue844(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func logStacktrace(t *testing.T, frames []proc.Stackframe) {
|
||||
func logStacktrace(t *testing.T, bi *proc.BinaryInfo, frames []proc.Stackframe) {
|
||||
for j := range frames {
|
||||
name := "?"
|
||||
if frames[j].Current.Fn != nil {
|
||||
@ -3150,6 +3150,23 @@ func logStacktrace(t *testing.T, frames []proc.Stackframe) {
|
||||
}
|
||||
|
||||
t.Logf("\t%#x %#x %#x %s at %s:%d\n", frames[j].Call.PC, frames[j].FrameOffset(), frames[j].FramePointerOffset(), name, filepath.Base(frames[j].Call.File), frames[j].Call.Line)
|
||||
if frames[j].TopmostDefer != nil {
|
||||
f, l, fn := bi.PCToLine(frames[j].TopmostDefer.DeferredPC)
|
||||
fnname := ""
|
||||
if fn != nil {
|
||||
fnname = fn.Name
|
||||
}
|
||||
t.Logf("\t\ttopmost defer: %#x %s at %s:%d\n", frames[j].TopmostDefer.DeferredPC, fnname, f, l)
|
||||
}
|
||||
for deferIdx, _defer := range frames[j].Defers {
|
||||
f, l, fn := bi.PCToLine(_defer.DeferredPC)
|
||||
fnname := ""
|
||||
if fn != nil {
|
||||
fnname = fn.Name
|
||||
}
|
||||
t.Logf("\t\t%d defer: %#x %s at %s:%d\n", deferIdx, _defer.DeferredPC, fnname, f, l)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3260,11 +3277,11 @@ func TestCgoStacktrace(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
frames, err := g.Stacktrace(100)
|
||||
frames, err := g.Stacktrace(100, false)
|
||||
assertNoError(err, t, fmt.Sprintf("Stacktrace at iteration step %d", itidx))
|
||||
|
||||
t.Logf("iteration step %d", itidx)
|
||||
logStacktrace(t, frames)
|
||||
logStacktrace(t, p.BinInfo(), frames)
|
||||
|
||||
m := stacktraceCheck(t, tc, frames)
|
||||
mismatch := (m == nil)
|
||||
@ -3301,7 +3318,7 @@ func TestCgoStacktrace(t *testing.T) {
|
||||
if frames[j].Current.File != threadFrames[j].Current.File || frames[j].Current.Line != threadFrames[j].Current.Line {
|
||||
t.Logf("stack mismatch between goroutine stacktrace and thread stacktrace")
|
||||
t.Logf("thread stacktrace:")
|
||||
logStacktrace(t, threadFrames)
|
||||
logStacktrace(t, p.BinInfo(), threadFrames)
|
||||
mismatch = true
|
||||
break
|
||||
}
|
||||
@ -3348,9 +3365,9 @@ func TestSystemstackStacktrace(t *testing.T) {
|
||||
assertNoError(proc.Continue(p), t, "second continue")
|
||||
g, err := proc.GetG(p.CurrentThread())
|
||||
assertNoError(err, t, "GetG")
|
||||
frames, err := g.Stacktrace(100)
|
||||
frames, err := g.Stacktrace(100, false)
|
||||
assertNoError(err, t, "stacktrace")
|
||||
logStacktrace(t, frames)
|
||||
logStacktrace(t, p.BinInfo(), frames)
|
||||
m := stacktraceCheck(t, []string{"!runtime.startpanic_m", "runtime.gopanic", "main.main"}, frames)
|
||||
if m == nil {
|
||||
t.Fatal("see previous loglines")
|
||||
@ -3383,9 +3400,9 @@ func TestSystemstackOnRuntimeNewstack(t *testing.T) {
|
||||
break
|
||||
}
|
||||
}
|
||||
frames, err := g.Stacktrace(100)
|
||||
frames, err := g.Stacktrace(100, false)
|
||||
assertNoError(err, t, "stacktrace")
|
||||
logStacktrace(t, frames)
|
||||
logStacktrace(t, p.BinInfo(), frames)
|
||||
m := stacktraceCheck(t, []string{"!runtime.newstack", "main.main"}, frames)
|
||||
if m == nil {
|
||||
t.Fatal("see previous loglines")
|
||||
@ -3400,7 +3417,7 @@ func TestIssue1034(t *testing.T) {
|
||||
_, err := setFunctionBreakpoint(p, "main.main")
|
||||
assertNoError(err, t, "setFunctionBreakpoint()")
|
||||
assertNoError(proc.Continue(p), t, "Continue()")
|
||||
frames, err := p.SelectedGoroutine().Stacktrace(10)
|
||||
frames, err := p.SelectedGoroutine().Stacktrace(10, false)
|
||||
assertNoError(err, t, "Stacktrace")
|
||||
scope := proc.FrameToScope(p.BinInfo(), p.CurrentThread(), nil, frames[2:]...)
|
||||
args, _ := scope.FunctionArguments(normalLoadConfig)
|
||||
@ -3873,3 +3890,62 @@ func TestIssue1264(t *testing.T) {
|
||||
assertLineNumber(p, t, 8, "after continue")
|
||||
})
|
||||
}
|
||||
|
||||
func TestReadDefer(t *testing.T) {
|
||||
withTestProcess("deferstack", t, func(p proc.Process, fixture protest.Fixture) {
|
||||
assertNoError(proc.Continue(p), t, "Continue")
|
||||
frames, err := p.SelectedGoroutine().Stacktrace(10, true)
|
||||
assertNoError(err, t, "Stacktrace")
|
||||
|
||||
logStacktrace(t, p.BinInfo(), frames)
|
||||
|
||||
examples := []struct {
|
||||
frameIdx int
|
||||
topmostDefer string
|
||||
defers []string
|
||||
}{
|
||||
// main.call3 (defers nothing, topmost defer main.f2)
|
||||
{0, "main.f2", []string{}},
|
||||
|
||||
// main.call2 (defers main.f2, main.f3, topmost defer main.f2)
|
||||
{1, "main.f2", []string{"main.f2", "main.f3"}},
|
||||
|
||||
// main.call1 (defers main.f1, main.f2, topmost defer main.f1)
|
||||
{2, "main.f1", []string{"main.f1", "main.f2"}},
|
||||
|
||||
// main.main (defers nothing)
|
||||
{3, "", []string{}}}
|
||||
|
||||
defercheck := func(d *proc.Defer, deferName, tgt string, frameIdx int) {
|
||||
if d == nil {
|
||||
t.Fatalf("expected %q as %s of frame %d, got nothing", tgt, deferName, frameIdx)
|
||||
}
|
||||
if d.Unreadable != nil {
|
||||
t.Fatalf("expected %q as %s of frame %d, got unreadable defer: %v", tgt, deferName, frameIdx, d.Unreadable)
|
||||
}
|
||||
_, _, dfn := p.BinInfo().PCToLine(d.DeferredPC)
|
||||
if dfn == nil {
|
||||
t.Fatalf("expected %q as %s of frame %d, got %#x", tgt, deferName, frameIdx, d.DeferredPC)
|
||||
}
|
||||
if dfn.Name != tgt {
|
||||
t.Fatalf("expected %q as %s of frame %d, got %q", tgt, deferName, frameIdx, dfn.Name)
|
||||
}
|
||||
}
|
||||
|
||||
for _, example := range examples {
|
||||
frame := &frames[example.frameIdx]
|
||||
|
||||
if example.topmostDefer != "" {
|
||||
defercheck(frame.TopmostDefer, "topmost defer", example.topmostDefer, example.frameIdx)
|
||||
}
|
||||
|
||||
if len(example.defers) != len(frames[example.frameIdx].Defers) {
|
||||
t.Fatalf("expected %d defers for %d, got %v", len(example.defers), example.frameIdx, frame.Defers)
|
||||
}
|
||||
|
||||
for deferIdx := range example.defers {
|
||||
defercheck(frame.Defers[deferIdx], fmt.Sprintf("defer %d", deferIdx), example.defers[deferIdx], example.frameIdx)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"debug/dwarf"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"strings"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/frame"
|
||||
@ -57,6 +58,15 @@ type Stackframe struct {
|
||||
// pkg/proc.
|
||||
// Use this value to determine active lexical scopes for the stackframe.
|
||||
lastpc uint64
|
||||
|
||||
// TopmostDefer is the defer that would be at the top of the stack when a
|
||||
// panic unwind would get to this call frame, in other words it's the first
|
||||
// deferred function that will be called if the runtime unwinds past this
|
||||
// call frame.
|
||||
TopmostDefer *Defer
|
||||
|
||||
// Defers is the list of functions deferred by this stack frame (so far).
|
||||
Defers []*Defer
|
||||
}
|
||||
|
||||
// FrameOffset returns the address of the stack frame, absolute for system
|
||||
@ -91,7 +101,7 @@ func ThreadStacktrace(thread Thread, depth int) ([]Stackframe, error) {
|
||||
it := newStackIterator(thread.BinInfo(), thread, thread.BinInfo().Arch.RegistersToDwarfRegisters(regs), 0, nil, -1, nil)
|
||||
return it.stacktrace(depth)
|
||||
}
|
||||
return g.Stacktrace(depth)
|
||||
return g.Stacktrace(depth, false)
|
||||
}
|
||||
|
||||
func (g *G) stackIterator() (*stackIterator, error) {
|
||||
@ -112,12 +122,19 @@ func (g *G) stackIterator() (*stackIterator, error) {
|
||||
|
||||
// Stacktrace returns the stack trace for a goroutine.
|
||||
// Note the locations in the array are return addresses not call addresses.
|
||||
func (g *G) Stacktrace(depth int) ([]Stackframe, error) {
|
||||
func (g *G) Stacktrace(depth int, readDefers bool) ([]Stackframe, error) {
|
||||
it, err := g.stackIterator()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return it.stacktrace(depth)
|
||||
frames, err := it.stacktrace(depth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if readDefers {
|
||||
g.readDefers(frames)
|
||||
}
|
||||
return frames, nil
|
||||
}
|
||||
|
||||
// NullAddrError is an error for a null address.
|
||||
@ -568,3 +585,101 @@ func (it *stackIterator) readRegisterAt(regnum uint64, addr uint64) (*op.DwarfRe
|
||||
}
|
||||
return op.DwarfRegisterFromBytes(buf), nil
|
||||
}
|
||||
|
||||
// Defer represents one deferred call
|
||||
type Defer struct {
|
||||
DeferredPC uint64 // Value of field _defer.fn.fn, the deferred function
|
||||
DeferPC uint64 // PC address of instruction that added this defer
|
||||
SP uint64 // Value of SP register when this function was deferred (this field gets adjusted when the stack is moved to match the new stack space)
|
||||
link *Defer // Next deferred function
|
||||
|
||||
variable *Variable
|
||||
Unreadable error
|
||||
}
|
||||
|
||||
// readDefers decorates the frames with the function deferred at each stack frame.
|
||||
func (g *G) readDefers(frames []Stackframe) {
|
||||
curdefer := g.Defer()
|
||||
i := 0
|
||||
|
||||
// scan simultaneously frames and the curdefer linked list, assigning
|
||||
// defers to their associated frames.
|
||||
for {
|
||||
if curdefer == nil || i >= len(frames) {
|
||||
return
|
||||
}
|
||||
if curdefer.Unreadable != nil {
|
||||
// Current defer is unreadable, stick it into the first available frame
|
||||
// (so that it can be reported to the user) and exit
|
||||
frames[i].Defers = append(frames[i].Defers, curdefer)
|
||||
return
|
||||
}
|
||||
if frames[i].Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if frames[i].TopmostDefer == nil {
|
||||
frames[i].TopmostDefer = curdefer
|
||||
}
|
||||
|
||||
if frames[i].SystemStack || curdefer.SP >= uint64(frames[i].Regs.CFA) {
|
||||
// frames[i].Regs.CFA is the value that SP had before the function of
|
||||
// frames[i] was called.
|
||||
// This means that when curdefer.SP == frames[i].Regs.CFA then curdefer
|
||||
// was added by the previous frame.
|
||||
//
|
||||
// curdefer.SP < frames[i].Regs.CFA means curdefer was added by a
|
||||
// function further down the stack.
|
||||
//
|
||||
// SystemStack frames live on a different physical stack and can't be
|
||||
// compared with deferred frames.
|
||||
i++
|
||||
} else {
|
||||
frames[i].Defers = append(frames[i].Defers, curdefer)
|
||||
curdefer = curdefer.Next()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Defer) load() {
|
||||
d.variable.loadValue(LoadConfig{false, 1, 0, 0, -1})
|
||||
if d.variable.Unreadable != nil {
|
||||
d.Unreadable = d.variable.Unreadable
|
||||
return
|
||||
}
|
||||
|
||||
fnvar := d.variable.fieldVariable("fn").maybeDereference()
|
||||
if fnvar.Addr != 0 {
|
||||
fnvar = fnvar.loadFieldNamed("fn")
|
||||
if fnvar.Unreadable == nil {
|
||||
d.DeferredPC, _ = constant.Uint64Val(fnvar.Value)
|
||||
}
|
||||
}
|
||||
|
||||
d.DeferPC, _ = constant.Uint64Val(d.variable.fieldVariable("pc").Value)
|
||||
d.SP, _ = constant.Uint64Val(d.variable.fieldVariable("sp").Value)
|
||||
|
||||
linkvar := d.variable.fieldVariable("link").maybeDereference()
|
||||
if linkvar.Addr != 0 {
|
||||
d.link = &Defer{variable: linkvar}
|
||||
}
|
||||
}
|
||||
|
||||
// spDecreasedErr is used when (*Defer).Next detects a corrupted linked
|
||||
// list, specifically when after followin a link pointer the value of SP
|
||||
// decreases rather than increasing or staying the same (the defer list is a
|
||||
// FIFO list, nodes further down the list have been added by function calls
|
||||
// further down the call stack and therefore the SP should always increase).
|
||||
var spDecreasedErr = errors.New("corrupted defer list: SP decreased")
|
||||
|
||||
// Next returns the next defer in the linked list
|
||||
func (d *Defer) Next() *Defer {
|
||||
if d.link == nil {
|
||||
return nil
|
||||
}
|
||||
d.link.load()
|
||||
if d.link.SP < d.SP {
|
||||
d.link.Unreadable = spDecreasedErr
|
||||
}
|
||||
return d.link
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ func topframe(g *G, thread Thread) (Stackframe, Stackframe, error) {
|
||||
}
|
||||
frames, err = ThreadStacktrace(thread, 1)
|
||||
} else {
|
||||
frames, err = g.Stacktrace(1)
|
||||
frames, err = g.Stacktrace(1, true)
|
||||
}
|
||||
if err != nil {
|
||||
return Stackframe{}, Stackframe{}, err
|
||||
@ -224,15 +224,12 @@ func next(dbp Process, stepInto, inlinedStepOut bool) error {
|
||||
|
||||
// Set breakpoint on the most recently deferred function (if any)
|
||||
var deferpc uint64 = 0
|
||||
if selg != nil {
|
||||
deferPCEntry := selg.DeferPC()
|
||||
if deferPCEntry != 0 {
|
||||
deferfn := dbp.BinInfo().PCToFunc(deferPCEntry)
|
||||
var err error
|
||||
deferpc, err = FirstPCAfterPrologue(dbp, deferfn, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if topframe.TopmostDefer != nil && topframe.TopmostDefer.DeferredPC != 0 {
|
||||
deferfn := dbp.BinInfo().PCToFunc(topframe.TopmostDefer.DeferredPC)
|
||||
var err error
|
||||
deferpc, err = FirstPCAfterPrologue(dbp, deferfn, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if deferpc != 0 && deferpc != topframe.Current.PC {
|
||||
|
@ -489,29 +489,18 @@ func (v *Variable) fieldVariable(name string) *Variable {
|
||||
return nil
|
||||
}
|
||||
|
||||
// PC of entry to top-most deferred function.
|
||||
func (g *G) DeferPC() uint64 {
|
||||
// Defer returns the top-most defer of the goroutine.
|
||||
func (g *G) Defer() *Defer {
|
||||
if g.variable.Unreadable != nil {
|
||||
return 0
|
||||
return nil
|
||||
}
|
||||
d := g.variable.fieldVariable("_defer").maybeDereference()
|
||||
if d.Addr == 0 {
|
||||
return 0
|
||||
dvar := g.variable.fieldVariable("_defer").maybeDereference()
|
||||
if dvar.Addr == 0 {
|
||||
return nil
|
||||
}
|
||||
d.loadValue(LoadConfig{false, 1, 64, 0, -1})
|
||||
if d.Unreadable != nil {
|
||||
return 0
|
||||
}
|
||||
fnvar := d.fieldVariable("fn").maybeDereference()
|
||||
if fnvar.Addr == 0 {
|
||||
return 0
|
||||
}
|
||||
fnvar.loadValue(LoadConfig{false, 1, 64, 0, -1})
|
||||
if fnvar.Unreadable != nil {
|
||||
return 0
|
||||
}
|
||||
deferPC, _ := constant.Int64Val(fnvar.fieldVariable("fn").Value)
|
||||
return uint64(deferPC)
|
||||
d := &Defer{variable: dvar}
|
||||
d.load()
|
||||
return d
|
||||
}
|
||||
|
||||
// From $GOROOT/src/runtime/traceback.go:597
|
||||
|
@ -503,7 +503,7 @@ func threads(t *Term, ctx callContext, args string) error {
|
||||
if th.Function != nil {
|
||||
fmt.Printf("%sThread %d at %#v %s:%d %s\n",
|
||||
prefix, th.ID, th.PC, ShortenFilePath(th.File),
|
||||
th.Line, th.Function.Name)
|
||||
th.Line, th.Function.Name())
|
||||
} else {
|
||||
fmt.Printf("%sThread %s\n", prefix, formatThread(th))
|
||||
}
|
||||
@ -593,7 +593,7 @@ func goroutines(t *Term, ctx callContext, argstr string) error {
|
||||
}
|
||||
fmt.Printf("%sGoroutine %s\n", prefix, formatGoroutine(g, fgl))
|
||||
if bPrintStack {
|
||||
stack, err := t.client.Stacktrace(g.ID, 10, nil)
|
||||
stack, err := t.client.Stacktrace(g.ID, 10, false, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -682,7 +682,7 @@ func (c *Commands) frameCommand(t *Term, ctx callContext, argstr string, directi
|
||||
if frame < 0 {
|
||||
return fmt.Errorf("Invalid frame %d", frame)
|
||||
}
|
||||
stack, err := t.client.Stacktrace(ctx.Scope.GoroutineID, frame, nil)
|
||||
stack, err := t.client.Stacktrace(ctx.Scope.GoroutineID, frame, false, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -731,11 +731,7 @@ const (
|
||||
)
|
||||
|
||||
func formatLocation(loc api.Location) string {
|
||||
fname := ""
|
||||
if loc.Function != nil {
|
||||
fname = loc.Function.Name
|
||||
}
|
||||
return fmt.Sprintf("%s:%d %s (%#v)", ShortenFilePath(loc.File), loc.Line, fname, loc.PC)
|
||||
return fmt.Sprintf("%s:%d %s (%#v)", ShortenFilePath(loc.File), loc.Line, loc.Function.Name(), loc.PC)
|
||||
}
|
||||
|
||||
func formatGoroutine(g *api.Goroutine, fgl formatGoroutineLoc) string {
|
||||
@ -1320,7 +1316,7 @@ func stackCommand(t *Term, ctx callContext, args string) error {
|
||||
if sa.full {
|
||||
cfg = &ShortLoadConfig
|
||||
}
|
||||
stack, err := t.client.Stacktrace(ctx.Scope.GoroutineID, sa.depth, cfg)
|
||||
stack, err := t.client.Stacktrace(ctx.Scope.GoroutineID, sa.depth, sa.readDefers, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -1329,9 +1325,10 @@ func stackCommand(t *Term, ctx callContext, args string) error {
|
||||
}
|
||||
|
||||
type stackArgs struct {
|
||||
depth int
|
||||
full bool
|
||||
offsets bool
|
||||
depth int
|
||||
full bool
|
||||
offsets bool
|
||||
readDefers bool
|
||||
}
|
||||
|
||||
func parseStackArgs(argstr string) (stackArgs, error) {
|
||||
@ -1347,6 +1344,8 @@ func parseStackArgs(argstr string) (stackArgs, error) {
|
||||
r.full = true
|
||||
case "-offsets":
|
||||
r.offsets = true
|
||||
case "-defer":
|
||||
r.readDefers = true
|
||||
default:
|
||||
n, err := strconv.Atoi(args[i])
|
||||
if err != nil {
|
||||
@ -1373,7 +1372,7 @@ func listCommand(t *Term, ctx callContext, args string) error {
|
||||
return printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true)
|
||||
|
||||
case len(args) == 0 && ctx.scoped():
|
||||
locs, err := t.client.Stacktrace(ctx.Scope.GoroutineID, ctx.Scope.Frame, nil)
|
||||
locs, err := t.client.Stacktrace(ctx.Scope.GoroutineID, ctx.Scope.Frame, false, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -1487,6 +1486,15 @@ func printStack(stack []api.Stackframe, ind string, offsets bool) {
|
||||
if len(stack) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
extranl := offsets
|
||||
for i := range stack {
|
||||
if extranl {
|
||||
break
|
||||
}
|
||||
extranl = extranl || (len(stack[i].Defers) > 0) || (len(stack[i].Arguments) > 0) || (len(stack[i].Locals) > 0)
|
||||
}
|
||||
|
||||
d := digits(len(stack) - 1)
|
||||
fmtstr := "%s%" + strconv.Itoa(d) + "d 0x%016x in %s\n"
|
||||
s := ind + strings.Repeat(" ", d+2+len(ind))
|
||||
@ -1496,23 +1504,35 @@ func printStack(stack []api.Stackframe, ind string, offsets bool) {
|
||||
fmt.Printf("%serror: %s\n", s, stack[i].Err)
|
||||
continue
|
||||
}
|
||||
name := "(nil)"
|
||||
if stack[i].Function != nil {
|
||||
name = stack[i].Function.Name
|
||||
}
|
||||
fmt.Printf(fmtstr, ind, i, stack[i].PC, name)
|
||||
fmt.Printf(fmtstr, ind, i, stack[i].PC, stack[i].Function.Name())
|
||||
fmt.Printf("%sat %s:%d\n", s, ShortenFilePath(stack[i].File), stack[i].Line)
|
||||
|
||||
if offsets {
|
||||
fmt.Printf("%sframe: %+#x frame pointer %+#x\n", s, stack[i].FrameOffset, stack[i].FramePointerOffset)
|
||||
}
|
||||
|
||||
for j, d := range stack[i].Defers {
|
||||
deferHeader := fmt.Sprintf("%s defer %d: ", s, j)
|
||||
s2 := strings.Repeat(" ", len(deferHeader))
|
||||
if d.Unreadable != "" {
|
||||
fmt.Printf("%s(unreadable defer: %s)\n", deferHeader, d.Unreadable)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("%s%#016x in %s\n", deferHeader, d.DeferredLoc.PC, d.DeferredLoc.Function.Name())
|
||||
fmt.Printf("%sat %s:%d\n", s2, d.DeferredLoc.File, d.DeferredLoc.Line)
|
||||
fmt.Printf("%sdeferred by %s at %s:%d\n", s2, d.DeferLoc.Function.Name(), d.DeferLoc.File, d.DeferLoc.Line)
|
||||
}
|
||||
|
||||
for j := range stack[i].Arguments {
|
||||
fmt.Printf("%s %s = %s\n", s, stack[i].Arguments[j].Name, stack[i].Arguments[j].SinglelineString())
|
||||
}
|
||||
for j := range stack[i].Locals {
|
||||
fmt.Printf("%s %s = %s\n", s, stack[i].Locals[j].Name, stack[i].Locals[j].SinglelineString())
|
||||
}
|
||||
|
||||
if extranl {
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1563,7 +1583,7 @@ func printcontext(t *Term, state *api.DebuggerState) error {
|
||||
}
|
||||
|
||||
func printcontextLocation(loc api.Location) {
|
||||
fmt.Printf("> %s() %s:%d (PC: %#v)\n", loc.Function.Name, ShortenFilePath(loc.File), loc.Line, loc.PC)
|
||||
fmt.Printf("> %s() %s:%d (PC: %#v)\n", loc.Function.Name(), ShortenFilePath(loc.File), loc.Line, loc.PC)
|
||||
if loc.Function != nil && loc.Function.Optimized {
|
||||
fmt.Println(optimizedFunctionWarning)
|
||||
}
|
||||
@ -1607,7 +1627,7 @@ func printcontextThread(t *Term, th *api.Thread) {
|
||||
if hitCount, ok := th.Breakpoint.HitCount[strconv.Itoa(th.GoroutineID)]; ok {
|
||||
fmt.Printf("> %s%s(%s) %s:%d (hits goroutine(%d):%d total:%d) (PC: %#v)\n",
|
||||
bpname,
|
||||
fn.Name,
|
||||
fn.Name(),
|
||||
args,
|
||||
ShortenFilePath(th.File),
|
||||
th.Line,
|
||||
@ -1618,7 +1638,7 @@ func printcontextThread(t *Term, th *api.Thread) {
|
||||
} else {
|
||||
fmt.Printf("> %s%s(%s) %s:%d (hits total:%d) (PC: %#v)\n",
|
||||
bpname,
|
||||
fn.Name,
|
||||
fn.Name(),
|
||||
args,
|
||||
ShortenFilePath(th.File),
|
||||
th.Line,
|
||||
@ -1829,11 +1849,7 @@ func checkpoint(t *Term, ctx callContext, args string) error {
|
||||
if state.SelectedGoroutine != nil {
|
||||
loc = state.SelectedGoroutine.CurrentLoc
|
||||
}
|
||||
fname := "???"
|
||||
if loc.Function != nil {
|
||||
fname = loc.Function.Name
|
||||
}
|
||||
args = fmt.Sprintf("%s() %s:%d (%#x)", fname, loc.File, loc.Line, loc.PC)
|
||||
args = fmt.Sprintf("%s() %s:%d (%#x)", loc.Function.Name(), loc.File, loc.Line, loc.PC)
|
||||
}
|
||||
|
||||
cpid, err := t.client.Checkpoint(args)
|
||||
|
@ -224,7 +224,7 @@ func TestExecuteFile(t *testing.T) {
|
||||
|
||||
func TestIssue354(t *testing.T) {
|
||||
printStack([]api.Stackframe{}, "", false)
|
||||
printStack([]api.Stackframe{{api.Location{PC: 0, File: "irrelevant.go", Line: 10, Function: nil}, nil, nil, 0, 0, ""}}, "", false)
|
||||
printStack([]api.Stackframe{{api.Location{PC: 0, File: "irrelevant.go", Line: 10, Function: nil}, nil, nil, 0, 0, nil, ""}}, "", false)
|
||||
}
|
||||
|
||||
func TestIssue411(t *testing.T) {
|
||||
|
@ -14,7 +14,7 @@ func DisasmPrint(dv api.AsmInstructions, out io.Writer) {
|
||||
bw := bufio.NewWriter(out)
|
||||
defer bw.Flush()
|
||||
if len(dv) > 0 && dv[0].Loc.Function != nil {
|
||||
fmt.Fprintf(bw, "TEXT %s(SB) %s\n", dv[0].Loc.Function.Name, dv[0].Loc.File)
|
||||
fmt.Fprintf(bw, "TEXT %s(SB) %s\n", dv[0].Loc.Function.Name(), dv[0].Loc.File)
|
||||
}
|
||||
tw := tabwriter.NewWriter(bw, 1, 8, 1, '\t', 0)
|
||||
defer tw.Flush()
|
||||
|
@ -215,7 +215,7 @@ func ConvertFunction(fn *proc.Function) *Function {
|
||||
// those fields is not documented their value was replaced with 0 when
|
||||
// gosym.Func was replaced by debug_info entries.
|
||||
return &Function{
|
||||
Name: fn.Name,
|
||||
Name_: fn.Name,
|
||||
Type: 0,
|
||||
Value: fn.Entry,
|
||||
GoType: 0,
|
||||
|
@ -129,9 +129,18 @@ type Stackframe struct {
|
||||
FrameOffset int64
|
||||
FramePointerOffset int64
|
||||
|
||||
Defers []Defer
|
||||
|
||||
Err string
|
||||
}
|
||||
|
||||
type Defer struct {
|
||||
DeferredLoc Location // deferred function
|
||||
DeferLoc Location // location of the defer statement
|
||||
SP uint64 // value of SP when the function was deferred
|
||||
Unreadable string
|
||||
}
|
||||
|
||||
func (frame *Stackframe) Var(name string) *Variable {
|
||||
for i := range frame.Locals {
|
||||
if frame.Locals[i].Name == name {
|
||||
@ -149,7 +158,7 @@ func (frame *Stackframe) Var(name string) *Variable {
|
||||
// Function represents thread-scoped function information.
|
||||
type Function struct {
|
||||
// Name is the function name.
|
||||
Name string `json:"name"`
|
||||
Name_ string `json:"name"`
|
||||
Value uint64 `json:"value"`
|
||||
Type byte `json:"type"`
|
||||
GoType uint64 `json:"goType"`
|
||||
@ -157,6 +166,13 @@ type Function struct {
|
||||
Optimized bool `json:"optimized"`
|
||||
}
|
||||
|
||||
func (fn *Function) Name() string {
|
||||
if fn == nil {
|
||||
return "???"
|
||||
}
|
||||
return fn.Name_
|
||||
}
|
||||
|
||||
// VariableFlags is the type of the Flags field of Variable.
|
||||
type VariableFlags uint16
|
||||
|
||||
|
@ -98,7 +98,7 @@ type Client interface {
|
||||
ListGoroutines() ([]*api.Goroutine, error)
|
||||
|
||||
// Returns stacktrace
|
||||
Stacktrace(int, int, *api.LoadConfig) ([]api.Stackframe, error)
|
||||
Stacktrace(goroutineID int, depth int, readDefers bool, cfg *api.LoadConfig) ([]api.Stackframe, error)
|
||||
|
||||
// Returns whether we attached to a running process or not
|
||||
AttachedToExistingProcess() bool
|
||||
|
@ -895,7 +895,7 @@ func (d *Debugger) Goroutines() ([]*api.Goroutine, error) {
|
||||
// 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) {
|
||||
func (d *Debugger) Stacktrace(goroutineID, depth int, readDefers bool, cfg *proc.LoadConfig) ([]api.Stackframe, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
@ -913,7 +913,7 @@ func (d *Debugger) Stacktrace(goroutineID, depth int, cfg *proc.LoadConfig) ([]a
|
||||
if g == nil {
|
||||
rawlocs, err = proc.ThreadStacktrace(d.target.CurrentThread(), depth)
|
||||
} else {
|
||||
rawlocs, err = g.Stacktrace(depth)
|
||||
rawlocs, err = g.Stacktrace(depth, readDefers)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -930,6 +930,8 @@ func (d *Debugger) convertStacktrace(rawlocs []proc.Stackframe, cfg *proc.LoadCo
|
||||
|
||||
FrameOffset: rawlocs[i].FrameOffset(),
|
||||
FramePointerOffset: rawlocs[i].FramePointerOffset(),
|
||||
|
||||
Defers: d.convertDefers(rawlocs[i].Defers),
|
||||
}
|
||||
if rawlocs[i].Err != nil {
|
||||
frame.Err = rawlocs[i].Err.Error()
|
||||
@ -955,6 +957,36 @@ func (d *Debugger) convertStacktrace(rawlocs []proc.Stackframe, cfg *proc.LoadCo
|
||||
return locations, nil
|
||||
}
|
||||
|
||||
func (d *Debugger) convertDefers(defers []*proc.Defer) []api.Defer {
|
||||
r := make([]api.Defer, len(defers))
|
||||
for i := range defers {
|
||||
ddf, ddl, ddfn := d.target.BinInfo().PCToLine(defers[i].DeferredPC)
|
||||
drf, drl, drfn := d.target.BinInfo().PCToLine(defers[i].DeferPC)
|
||||
|
||||
r[i] = api.Defer{
|
||||
DeferredLoc: api.ConvertLocation(proc.Location{
|
||||
PC: defers[i].DeferredPC,
|
||||
File: ddf,
|
||||
Line: ddl,
|
||||
Fn: ddfn,
|
||||
}),
|
||||
DeferLoc: api.ConvertLocation(proc.Location{
|
||||
PC: defers[i].DeferPC,
|
||||
File: drf,
|
||||
Line: drl,
|
||||
Fn: drfn,
|
||||
}),
|
||||
SP: defers[i].SP,
|
||||
}
|
||||
|
||||
if defers[i].Unreadable != nil {
|
||||
r[i].Unreadable = defers[i].Unreadable.Error()
|
||||
}
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// FindLocation will find the location specified by 'locStr'.
|
||||
func (d *Debugger) FindLocation(scope api.EvalScope, locStr string) ([]api.Location, error) {
|
||||
d.processMutex.Lock()
|
||||
|
@ -316,7 +316,7 @@ 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)
|
||||
candidates = append(candidates, ale.CandidatesLocation[i].Function.Name())
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -87,7 +87,7 @@ func (s *RPCServer) StacktraceGoroutine(args *StacktraceGoroutineArgs, locations
|
||||
if args.Full {
|
||||
loadcfg = &defaultLoadConfig
|
||||
}
|
||||
locs, err := s.debugger.Stacktrace(args.Id, args.Depth, loadcfg)
|
||||
locs, err := s.debugger.Stacktrace(args.Id, args.Depth, false, loadcfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -295,9 +295,9 @@ func (c *RPCClient) ListGoroutines() ([]*api.Goroutine, error) {
|
||||
return out.Goroutines, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) Stacktrace(goroutineId, depth int, cfg *api.LoadConfig) ([]api.Stackframe, error) {
|
||||
func (c *RPCClient) Stacktrace(goroutineId, depth int, readDefers bool, cfg *api.LoadConfig) ([]api.Stackframe, error) {
|
||||
var out StacktraceOut
|
||||
err := c.call("Stacktrace", StacktraceIn{goroutineId, depth, false, cfg}, &out)
|
||||
err := c.call("Stacktrace", StacktraceIn{goroutineId, depth, false, readDefers, cfg}, &out)
|
||||
return out.Locations, err
|
||||
}
|
||||
|
||||
|
@ -152,10 +152,11 @@ func (s *RPCServer) GetBreakpoint(arg GetBreakpointIn, out *GetBreakpointOut) er
|
||||
}
|
||||
|
||||
type StacktraceIn struct {
|
||||
Id int
|
||||
Depth int
|
||||
Full bool
|
||||
Cfg *api.LoadConfig
|
||||
Id int
|
||||
Depth int
|
||||
Full bool
|
||||
Defers bool // read deferred functions
|
||||
Cfg *api.LoadConfig
|
||||
}
|
||||
|
||||
type StacktraceOut struct {
|
||||
@ -171,7 +172,7 @@ func (s *RPCServer) Stacktrace(arg StacktraceIn, out *StacktraceOut) error {
|
||||
if cfg == nil && arg.Full {
|
||||
cfg = &api.LoadConfig{true, 1, 64, 64, -1}
|
||||
}
|
||||
locs, err := s.debugger.Stacktrace(arg.Id, arg.Depth, api.LoadConfigToProc(cfg))
|
||||
locs, err := s.debugger.Stacktrace(arg.Id, arg.Depth, arg.Defers, api.LoadConfigToProc(cfg))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -732,7 +732,7 @@ func Test1ClientServer_FullStacktrace(t *testing.T) {
|
||||
if frame.Function == nil {
|
||||
continue
|
||||
}
|
||||
if frame.Function.Name != "main.agoroutine" {
|
||||
if frame.Function.Name() != "main.agoroutine" {
|
||||
continue
|
||||
}
|
||||
t.Logf("frame %d: %v", i, frame)
|
||||
@ -887,7 +887,7 @@ func Test1Disasm(t *testing.T) {
|
||||
// look for static call to afunction() on line 29
|
||||
found := false
|
||||
for i := range d3 {
|
||||
if d3[i].Loc.Line == 29 && strings.HasPrefix(d3[i].Text, "call") && d3[i].DestLoc != nil && d3[i].DestLoc.Function != nil && d3[i].DestLoc.Function.Name == "main.afunction" {
|
||||
if d3[i].Loc.Line == 29 && strings.HasPrefix(d3[i].Text, "call") && d3[i].DestLoc != nil && d3[i].DestLoc.Function != nil && d3[i].DestLoc.Function.Name() == "main.afunction" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
@ -937,7 +937,7 @@ func Test1Disasm(t *testing.T) {
|
||||
if curinstr.DestLoc == nil || curinstr.DestLoc.Function == nil {
|
||||
t.Fatalf("Call instruction does not have destination: %v", curinstr)
|
||||
}
|
||||
if curinstr.DestLoc.Function.Name != "main.afunction" {
|
||||
if curinstr.DestLoc.Function.Name() != "main.afunction" {
|
||||
t.Fatalf("Call instruction destination not main.afunction: %v", curinstr)
|
||||
}
|
||||
break
|
||||
|
@ -798,13 +798,13 @@ func TestClientServer_FullStacktrace(t *testing.T) {
|
||||
assertNoError(err, t, "GoroutinesInfo()")
|
||||
found := make([]bool, 10)
|
||||
for _, g := range gs {
|
||||
frames, err := c.Stacktrace(g.ID, 10, &normalLoadConfig)
|
||||
frames, err := c.Stacktrace(g.ID, 10, false, &normalLoadConfig)
|
||||
assertNoError(err, t, fmt.Sprintf("Stacktrace(%d)", g.ID))
|
||||
for i, frame := range frames {
|
||||
if frame.Function == nil {
|
||||
continue
|
||||
}
|
||||
if frame.Function.Name != "main.agoroutine" {
|
||||
if frame.Function.Name() != "main.agoroutine" {
|
||||
continue
|
||||
}
|
||||
t.Logf("frame %d: %v", i, frame)
|
||||
@ -832,7 +832,7 @@ func TestClientServer_FullStacktrace(t *testing.T) {
|
||||
t.Fatalf("Continue(): %v\n", state.Err)
|
||||
}
|
||||
|
||||
frames, err := c.Stacktrace(-1, 10, &normalLoadConfig)
|
||||
frames, err := c.Stacktrace(-1, 10, false, &normalLoadConfig)
|
||||
assertNoError(err, t, "Stacktrace")
|
||||
|
||||
cur := 3
|
||||
@ -911,7 +911,7 @@ func TestIssue355(t *testing.T) {
|
||||
assertError(err, t, "ListRegisters()")
|
||||
_, err = c.ListGoroutines()
|
||||
assertError(err, t, "ListGoroutines()")
|
||||
_, err = c.Stacktrace(gid, 10, &normalLoadConfig)
|
||||
_, err = c.Stacktrace(gid, 10, false, &normalLoadConfig)
|
||||
assertError(err, t, "Stacktrace()")
|
||||
_, err = c.FindLocation(api.EvalScope{gid, 0}, "+1")
|
||||
assertError(err, t, "FindLocation()")
|
||||
@ -964,7 +964,7 @@ func TestDisasm(t *testing.T) {
|
||||
// look for static call to afunction() on line 29
|
||||
found := false
|
||||
for i := range d3 {
|
||||
if d3[i].Loc.Line == 29 && strings.HasPrefix(d3[i].Text, "call") && d3[i].DestLoc != nil && d3[i].DestLoc.Function != nil && d3[i].DestLoc.Function.Name == "main.afunction" {
|
||||
if d3[i].Loc.Line == 29 && strings.HasPrefix(d3[i].Text, "call") && d3[i].DestLoc != nil && d3[i].DestLoc.Function != nil && d3[i].DestLoc.Function.Name() == "main.afunction" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
@ -1014,7 +1014,7 @@ func TestDisasm(t *testing.T) {
|
||||
if curinstr.DestLoc == nil || curinstr.DestLoc.Function == nil {
|
||||
t.Fatalf("Call instruction does not have destination: %v", curinstr)
|
||||
}
|
||||
if curinstr.DestLoc.Function.Name != "main.afunction" {
|
||||
if curinstr.DestLoc.Function.Name() != "main.afunction" {
|
||||
t.Fatalf("Call instruction destination not main.afunction: %v", curinstr)
|
||||
}
|
||||
break
|
||||
@ -1034,7 +1034,7 @@ func TestNegativeStackDepthBug(t *testing.T) {
|
||||
ch := c.Continue()
|
||||
state := <-ch
|
||||
assertNoError(state.Err, t, "Continue()")
|
||||
_, err = c.Stacktrace(-1, -2, &normalLoadConfig)
|
||||
_, err = c.Stacktrace(-1, -2, false, &normalLoadConfig)
|
||||
assertError(err, t, "Stacktrace()")
|
||||
})
|
||||
}
|
||||
@ -1493,7 +1493,7 @@ func TestAcceptMulticlient(t *testing.T) {
|
||||
|
||||
client2 := rpc2.NewClient(listener.Addr().String())
|
||||
state := <-client2.Continue()
|
||||
if state.CurrentThread.Function.Name != "main.main" {
|
||||
if state.CurrentThread.Function.Name() != "main.main" {
|
||||
t.Fatalf("bad state after continue: %v\n", state)
|
||||
}
|
||||
client2.Detach(true)
|
||||
@ -1516,12 +1516,12 @@ func TestClientServerFunctionCall(t *testing.T) {
|
||||
c.SetReturnValuesLoadConfig(&normalLoadConfig)
|
||||
state := <-c.Continue()
|
||||
assertNoError(state.Err, t, "Continue()")
|
||||
beforeCallFn := state.CurrentThread.Function.Name
|
||||
beforeCallFn := state.CurrentThread.Function.Name()
|
||||
state, err := c.Call("call1(one, two)")
|
||||
assertNoError(err, t, "Call()")
|
||||
t.Logf("returned to %q", state.CurrentThread.Function.Name)
|
||||
if state.CurrentThread.Function.Name != beforeCallFn {
|
||||
t.Fatalf("did not return to the calling function %q %q", beforeCallFn, state.CurrentThread.Function.Name)
|
||||
t.Logf("returned to %q", state.CurrentThread.Function.Name())
|
||||
if state.CurrentThread.Function.Name() != beforeCallFn {
|
||||
t.Fatalf("did not return to the calling function %q %q", beforeCallFn, state.CurrentThread.Function.Name())
|
||||
}
|
||||
if state.CurrentThread.ReturnValues == nil {
|
||||
t.Fatal("no return values on return from call")
|
||||
|
Loading…
Reference in New Issue
Block a user