proc: add options to bypass smart stacktraces (#1686)

Add options to start a stacktrace from the values saved in the
runtime.g struct as well as a way to disable the stackSwitch logic and
just get a normal stacktrace.
This commit is contained in:
Alessandro Arzilli 2019-09-25 19:21:20 +02:00 committed by Derek Parker
parent e994047355
commit efd628616b
17 changed files with 138 additions and 54 deletions

@ -374,13 +374,17 @@ If regex is specified only the source files matching it will be returned.
## stack
Print stack trace.
[goroutine <n>] [frame <m>] stack [<depth>] [-full] [-offsets] [-defer] [-a <n>] [-adepth <depth>]
[goroutine <n>] [frame <m>] stack [<depth>] [-full] [-offsets] [-defer] [-a <n>] [-adepth <depth>] [-mode <mode>]
-full every stackframe is decorated with the value of its local variables and arguments.
-offsets prints frame offset of each frame.
-defer prints deferred function call stack for each frame.
-a <n> prints stacktrace of n ancestors of the selected goroutine (target process must have tracebackancestors enabled)
-adepth <depth> configures depth of ancestor stacktrace
-mode <mode> specifies the stacktrace mode, possible values are:
normal - attempts to automatically switch between cgo frames and go frames
simple - disables automatic switch between cgo and go
fromg - starts from the registers stored in the runtime.g struct
Aliases: bt

@ -51,7 +51,7 @@ process_pid() | Equivalent to API call [ProcessPid](https://godoc.org/github.com
recorded() | Equivalent to API call [Recorded](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.Recorded)
restart(Position, ResetArgs, NewArgs) | Equivalent to API call [Restart](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.Restart)
set_expr(Scope, Symbol, Value) | Equivalent to API call [Set](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.Set)
stacktrace(Id, Depth, Full, Defers, Cfg) | Equivalent to API call [Stacktrace](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.Stacktrace)
stacktrace(Id, Depth, Full, Defers, Opts, Cfg) | Equivalent to API call [Stacktrace](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.Stacktrace)
state(NonBlocking) | Equivalent to API call [State](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.State)
dlv_command(command) | Executes the specified command as if typed at the dlv_prompt
read_file(path) | Reads the file as a string

@ -199,7 +199,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, false)
stack, err := g.Stacktrace(10, 0)
if err != nil {
t.Errorf("Stacktrace() on goroutine %v = %v", g, err)
}
@ -343,7 +343,7 @@ func TestCoreWithEmptyString(t *testing.T) {
var mainFrame *proc.Stackframe
mainSearch:
for _, g := range gs {
stack, err := g.Stacktrace(10, false)
stack, err := g.Stacktrace(10, 0)
assertNoError(err, t, "Stacktrace()")
for _, frame := range stack {
if frame.Current.Fn != nil && frame.Current.Fn.Name == "main.main" {
@ -390,7 +390,7 @@ func TestMinidump(t *testing.T) {
t.Logf("%d goroutines", len(gs))
foundMain, foundTime := false, false
for _, g := range gs {
stack, err := g.Stacktrace(10, false)
stack, err := g.Stacktrace(10, 0)
if err != nil {
t.Errorf("Stacktrace() on goroutine %v = %v", g, err)
}

@ -646,7 +646,12 @@ func ConvertEvalScope(dbp Process, gid, frame, deferCall int) (*EvalScope, error
thread = g.Thread
}
locs, err := g.Stacktrace(frame+1, deferCall > 0)
var opts StacktraceOptions
if deferCall > 0 {
opts = StacktraceReadDefers
}
locs, err := g.Stacktrace(frame+1, opts)
if err != nil {
return nil, err
}

@ -917,7 +917,7 @@ func TestStacktraceGoroutine(t *testing.T) {
mainCount := 0
for i, g := range gs {
locations, err := g.Stacktrace(40, false)
locations, err := g.Stacktrace(40, 0)
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)
@ -1226,7 +1226,7 @@ func TestFrameEvaluation(t *testing.T) {
found := make([]bool, 10)
for _, g := range gs {
frame := -1
frames, err := g.Stacktrace(10, false)
frames, err := g.Stacktrace(10, 0)
if err != nil {
t.Logf("could not stacktrace goroutine %d: %v\n", g.ID, err)
continue
@ -1925,7 +1925,7 @@ func TestNextParked(t *testing.T) {
if g.Thread != nil {
continue
}
frames, _ := g.Stacktrace(5, false)
frames, _ := g.Stacktrace(5, 0)
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 {
@ -1980,7 +1980,7 @@ func TestStepParked(t *testing.T) {
}
t.Logf("Parked g is: %v\n", parkedg)
frames, _ := parkedg.Stacktrace(20, false)
frames, _ := parkedg.Stacktrace(20, 0)
for _, frame := range frames {
name := ""
if frame.Call.Fn != nil {
@ -2686,7 +2686,7 @@ func TestStacktraceWithBarriers(t *testing.T) {
goid, _ := constant.Int64Val(goidVar.Value)
if g := getg(int(goid), gs); g != nil {
stack, err := g.Stacktrace(50, false)
stack, err := g.Stacktrace(50, 0)
assertNoError(err, t, fmt.Sprintf("Stacktrace(goroutine = %d)", goid))
for _, frame := range stack {
if frame.Current.Fn != nil && frame.Current.Fn.Name == "main.bottomUpTree" {
@ -2712,7 +2712,7 @@ func TestStacktraceWithBarriers(t *testing.T) {
for _, goid := range stackBarrierGoids {
g := getg(goid, gs)
stack, err := g.Stacktrace(200, false)
stack, err := g.Stacktrace(200, 0)
assertNoError(err, t, "Stacktrace()")
// Check that either main.main or main.main.func1 appear in the
@ -3260,7 +3260,7 @@ func TestCgoStacktrace(t *testing.T) {
}
}
frames, err := g.Stacktrace(100, false)
frames, err := g.Stacktrace(100, 0)
assertNoError(err, t, fmt.Sprintf("Stacktrace at iteration step %d", itidx))
t.Logf("iteration step %d", itidx)
@ -3347,7 +3347,7 @@ 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, false)
frames, err := g.Stacktrace(100, 0)
assertNoError(err, t, "stacktrace")
logStacktrace(t, p.BinInfo(), frames)
m := stacktraceCheck(t, []string{"!runtime.startpanic_m", "runtime.gopanic", "main.main"}, frames)
@ -3380,7 +3380,7 @@ func TestSystemstackOnRuntimeNewstack(t *testing.T) {
break
}
}
frames, err := g.Stacktrace(100, false)
frames, err := g.Stacktrace(100, 0)
assertNoError(err, t, "stacktrace")
logStacktrace(t, p.BinInfo(), frames)
m := stacktraceCheck(t, []string{"!runtime.newstack", "main.main"}, frames)
@ -3396,7 +3396,7 @@ func TestIssue1034(t *testing.T) {
withTestProcess("cgostacktest/", t, func(p proc.Process, fixture protest.Fixture) {
setFunctionBreakpoint(p, t, "main.main")
assertNoError(proc.Continue(p), t, "Continue()")
frames, err := p.SelectedGoroutine().Stacktrace(10, false)
frames, err := p.SelectedGoroutine().Stacktrace(10, 0)
assertNoError(err, t, "Stacktrace")
scope := proc.FrameToScope(p.BinInfo(), p.CurrentThread(), nil, frames[2:]...)
args, _ := scope.FunctionArguments(normalLoadConfig)
@ -3949,7 +3949,7 @@ func TestIssue1264(t *testing.T) {
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)
frames, err := p.SelectedGoroutine().Stacktrace(10, proc.StacktraceReadDefers)
assertNoError(err, t, "Stacktrace")
logStacktrace(t, p.BinInfo(), frames)

@ -101,13 +101,13 @@ func ThreadStacktrace(thread Thread, depth int) ([]Stackframe, error) {
return nil, err
}
so := thread.BinInfo().PCToImage(regs.PC())
it := newStackIterator(thread.BinInfo(), thread, thread.BinInfo().Arch.RegistersToDwarfRegisters(so.StaticBase, regs), 0, nil, -1, nil)
it := newStackIterator(thread.BinInfo(), thread, thread.BinInfo().Arch.RegistersToDwarfRegisters(so.StaticBase, regs), 0, nil, -1, nil, 0)
return it.stacktrace(depth)
}
return g.Stacktrace(depth, false)
return g.Stacktrace(depth, 0)
}
func (g *G) stackIterator() (*stackIterator, error) {
func (g *G) stackIterator(opts StacktraceOptions) (*stackIterator, error) {
stkbar, err := g.stkbar()
if err != nil {
return nil, err
@ -122,19 +122,35 @@ func (g *G) stackIterator() (*stackIterator, error) {
return newStackIterator(
g.variable.bi, g.Thread,
g.variable.bi.Arch.RegistersToDwarfRegisters(so.StaticBase, regs),
g.stackhi, stkbar, g.stkbarPos, g), nil
g.stackhi, stkbar, g.stkbarPos, g, opts), nil
}
so := g.variable.bi.PCToImage(g.PC)
return newStackIterator(
g.variable.bi, g.variable.mem,
g.variable.bi.Arch.AddrAndStackRegsToDwarfRegisters(so.StaticBase, g.PC, g.SP, g.BP),
g.stackhi, stkbar, g.stkbarPos, g), nil
g.stackhi, stkbar, g.stkbarPos, g, opts), nil
}
type StacktraceOptions uint16
const (
// StacktraceReadDefers requests a stacktrace decorated with deferred calls
// for each frame.
StacktraceReadDefers StacktraceOptions = 1 << iota
// StacktraceSimple requests a stacktrace where no stack switches will be
// attempted.
StacktraceSimple
// StacktraceG requests a stacktrace starting with the register
// values saved in the runtime.g structure.
StacktraceG
)
// 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, readDefers bool) ([]Stackframe, error) {
it, err := g.stackIterator()
func (g *G) Stacktrace(depth int, opts StacktraceOptions) ([]Stackframe, error) {
it, err := g.stackIterator(opts)
if err != nil {
return nil, err
}
@ -142,7 +158,7 @@ func (g *G) Stacktrace(depth int, readDefers bool) ([]Stackframe, error) {
if err != nil {
return nil, err
}
if readDefers {
if opts&StacktraceReadDefers != 0 {
g.readDefers(frames)
}
return frames, nil
@ -177,6 +193,8 @@ type stackIterator struct {
g *G // the goroutine being stacktraced, nil if we are stacktracing a goroutine-less thread
g0_sched_sp uint64 // value of g0.sched.sp (see comments around its use)
opts StacktraceOptions
}
type savedLR struct {
@ -184,7 +202,7 @@ type savedLR struct {
val uint64
}
func newStackIterator(bi *BinaryInfo, mem MemoryReadWriter, regs op.DwarfRegisters, stackhi uint64, stkbar []savedLR, stkbarPos int, g *G) *stackIterator {
func newStackIterator(bi *BinaryInfo, mem MemoryReadWriter, regs op.DwarfRegisters, stackhi uint64, stkbar []savedLR, stkbarPos int, g *G, opts StacktraceOptions) *stackIterator {
stackBarrierFunc := bi.LookupFunc["runtime.stackBarrier"] // stack barriers were removed in Go 1.9
var stackBarrierPC uint64
if stackBarrierFunc != nil && stkbar != nil {
@ -216,7 +234,7 @@ func newStackIterator(bi *BinaryInfo, mem MemoryReadWriter, regs op.DwarfRegiste
}
}
}
return &stackIterator{pc: regs.PC(), regs: regs, top: true, bi: bi, mem: mem, err: nil, atend: false, stackhi: stackhi, stackBarrierPC: stackBarrierPC, stkbar: stkbar, systemstack: systemstack, g: g, g0_sched_sp: g0_sched_sp}
return &stackIterator{pc: regs.PC(), regs: regs, top: true, bi: bi, mem: mem, err: nil, atend: false, stackhi: stackhi, stackBarrierPC: stackBarrierPC, stkbar: stkbar, systemstack: systemstack, g: g, g0_sched_sp: g0_sched_sp, opts: opts}
}
// Next points the iterator to the next stack frame.
@ -233,8 +251,10 @@ func (it *stackIterator) Next() bool {
it.stkbar = it.stkbar[1:]
}
if it.switchStack() {
return true
if it.opts&StacktraceSimple == 0 {
if it.switchStack() {
return true
}
}
if it.frame.Ret <= 0 {
@ -435,6 +455,10 @@ func (it *stackIterator) stacktrace(depth int) ([]Stackframe, error) {
if depth < 0 {
return nil, errors.New("negative maximum stack depth")
}
if it.opts&StacktraceG != 0 && it.g != nil {
it.switchToGoroutineStack()
it.top = true
}
frames := make([]Stackframe, 0, depth+1)
for it.Next() {
frames = it.appendInlineCalls(frames, it.Frame())

@ -90,7 +90,7 @@ func topframe(g *G, thread Thread) (Stackframe, Stackframe, error) {
}
frames, err = ThreadStacktrace(thread, 1)
} else {
frames, err = g.Stacktrace(1, true)
frames, err = g.Stacktrace(1, StacktraceReadDefers)
}
if err != nil {
return Stackframe{}, Stackframe{}, err

@ -229,7 +229,7 @@ func (g *G) Defer() *Defer {
// UserCurrent returns the location the users code is at,
// or was at before entering a runtime function.
func (g *G) UserCurrent() Location {
it, err := g.stackIterator()
it, err := g.stackIterator(0)
if err != nil {
return g.CurrentLoc
}

@ -258,13 +258,17 @@ When connected to a headless instance started with the --accept-multiclient, pas
Show source around current point or provided linespec.`},
{aliases: []string{"stack", "bt"}, allowedPrefixes: onPrefix, cmdFn: stackCommand, helpMsg: `Print stack trace.
[goroutine <n>] [frame <m>] stack [<depth>] [-full] [-offsets] [-defer] [-a <n>] [-adepth <depth>]
[goroutine <n>] [frame <m>] stack [<depth>] [-full] [-offsets] [-defer] [-a <n>] [-adepth <depth>] [-mode <mode>]
-full every stackframe is decorated with the value of its local variables and arguments.
-offsets prints frame offset of each frame.
-defer prints deferred function call stack for each frame.
-a <n> prints stacktrace of n ancestors of the selected goroutine (target process must have tracebackancestors enabled)
-adepth <depth> configures depth of ancestor stacktrace
-mode <mode> specifies the stacktrace mode, possible values are:
normal - attempts to automatically switch between cgo frames and go frames
simple - disables automatic switch between cgo and go
fromg - starts from the registers stored in the runtime.g struct
`},
{aliases: []string{"frame"},
cmdFn: func(t *Term, ctx callContext, arg string) error {
@ -601,7 +605,7 @@ func printGoroutines(t *Term, gs []*api.Goroutine, fgl formatGoroutineLoc, bPrin
}
fmt.Printf("%sGoroutine %s\n", prefix, formatGoroutine(g, fgl))
if bPrintStack {
stack, err := t.client.Stacktrace(g.ID, 10, false, nil)
stack, err := t.client.Stacktrace(g.ID, 10, 0, nil)
if err != nil {
return err
}
@ -745,7 +749,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, false, nil)
stack, err := t.client.Stacktrace(ctx.Scope.GoroutineID, frame, 0, nil)
if err != nil {
return err
}
@ -1473,7 +1477,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, sa.readDefers, cfg)
stack, err := t.client.Stacktrace(ctx.Scope.GoroutineID, sa.depth, sa.opts, cfg)
if err != nil {
return err
}
@ -1496,10 +1500,10 @@ func stackCommand(t *Term, ctx callContext, args string) error {
}
type stackArgs struct {
depth int
full bool
offsets bool
readDefers bool
depth int
full bool
offsets bool
opts api.StacktraceOptions
ancestors int
ancestorDepth int
@ -1530,7 +1534,23 @@ func parseStackArgs(argstr string) (stackArgs, error) {
case "-offsets":
r.offsets = true
case "-defer":
r.readDefers = true
r.opts |= api.StacktraceReadDefers
case "-mode":
i++
if i >= len(args) {
return stackArgs{}, fmt.Errorf("expected normal, simple or fromg after -mode")
}
switch args[i] {
case "normal":
r.opts &^= api.StacktraceSimple
r.opts &^= api.StacktraceG
case "simple":
r.opts |= api.StacktraceSimple
case "fromg":
r.opts |= api.StacktraceG | api.StacktraceSimple
default:
return stackArgs{}, fmt.Errorf("expected normal, simple or fromg after -mode")
}
case "-a":
i++
n, err := numarg("-a")
@ -1578,7 +1598,7 @@ func getLocation(t *Term, ctx callContext, args string, showContext bool) (file
return state.CurrentThread.File, state.CurrentThread.Line, true, nil
case len(args) == 0 && ctx.scoped():
locs, err := t.client.Stacktrace(ctx.Scope.GoroutineID, ctx.Scope.Frame, false, nil)
locs, err := t.client.Stacktrace(ctx.Scope.GoroutineID, ctx.Scope.Frame, 0, nil)
if err != nil {
return "", 0, false, err
}

@ -1099,7 +1099,13 @@ func (env *Env) starlarkPredeclare() starlark.StringDict {
}
}
if len(args) > 4 && args[4] != starlark.None {
err := unmarshalStarlarkValue(args[4], &rpcArgs.Cfg, "Cfg")
err := unmarshalStarlarkValue(args[4], &rpcArgs.Opts, "Opts")
if err != nil {
return starlark.None, decorateError(thread, err)
}
}
if len(args) > 5 && args[5] != starlark.None {
err := unmarshalStarlarkValue(args[5], &rpcArgs.Cfg, "Cfg")
if err != nil {
return starlark.None, decorateError(thread, err)
}
@ -1115,6 +1121,8 @@ func (env *Env) starlarkPredeclare() starlark.StringDict {
err = unmarshalStarlarkValue(kv[1], &rpcArgs.Full, "Full")
case "Defers":
err = unmarshalStarlarkValue(kv[1], &rpcArgs.Defers, "Defers")
case "Opts":
err = unmarshalStarlarkValue(kv[1], &rpcArgs.Opts, "Opts")
case "Cfg":
err = unmarshalStarlarkValue(kv[1], &rpcArgs.Cfg, "Cfg")
default:

@ -479,3 +479,22 @@ type Ancestor struct {
Unreadable string
}
// StacktraceOptions is the type of the Opts field of StacktraceIn that
// configures the stacktrace.
// Tracks proc.StacktraceOptions
type StacktraceOptions uint16
const (
// StacktraceReadDefers requests a stacktrace decorated with deferred calls
// for each frame.
StacktraceReadDefers StacktraceOptions = 1 << iota
// StacktraceSimple requests a stacktrace where no stack switches will be
// attempted.
StacktraceSimple
// StacktraceG requests a stacktrace starting with the register
// values saved in the runtime.g structure.
StacktraceG
)

@ -100,7 +100,7 @@ type Client interface {
ListGoroutines(start, count int) ([]*api.Goroutine, int, error)
// Returns stacktrace
Stacktrace(goroutineID int, depth int, readDefers bool, cfg *api.LoadConfig) ([]api.Stackframe, error)
Stacktrace(goroutineID int, depth int, opts api.StacktraceOptions, cfg *api.LoadConfig) ([]api.Stackframe, error)
// Returns ancestor stacktraces
Ancestors(goroutineID int, numAncestors int, depth int) ([]api.Ancestor, error)

@ -989,7 +989,7 @@ func (d *Debugger) Goroutines(start, count int) ([]*api.Goroutine, int, 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, readDefers bool, cfg *proc.LoadConfig) ([]api.Stackframe, error) {
func (d *Debugger) Stacktrace(goroutineID, depth int, opts api.StacktraceOptions, cfg *proc.LoadConfig) ([]api.Stackframe, error) {
d.processMutex.Lock()
defer d.processMutex.Unlock()
@ -1007,7 +1007,7 @@ func (d *Debugger) Stacktrace(goroutineID, depth int, readDefers bool, cfg *proc
if g == nil {
rawlocs, err = proc.ThreadStacktrace(d.target.CurrentThread(), depth)
} else {
rawlocs, err = g.Stacktrace(depth, readDefers)
rawlocs, err = g.Stacktrace(depth, proc.StacktraceOptions(opts))
}
if err != nil {
return nil, err

@ -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, false, loadcfg)
locs, err := s.debugger.Stacktrace(args.Id, args.Depth, 0, loadcfg)
if err != nil {
return err
}

@ -310,9 +310,9 @@ func (c *RPCClient) ListGoroutines(start, count int) ([]*api.Goroutine, int, err
return out.Goroutines, out.Nextg, err
}
func (c *RPCClient) Stacktrace(goroutineId, depth int, readDefers bool, cfg *api.LoadConfig) ([]api.Stackframe, error) {
func (c *RPCClient) Stacktrace(goroutineId, depth int, opts api.StacktraceOptions, cfg *api.LoadConfig) ([]api.Stackframe, error) {
var out StacktraceOut
err := c.call("Stacktrace", StacktraceIn{goroutineId, depth, false, readDefers, cfg}, &out)
err := c.call("Stacktrace", StacktraceIn{goroutineId, depth, false, false, opts, cfg}, &out)
return out.Locations, err
}

@ -155,7 +155,8 @@ type StacktraceIn struct {
Id int
Depth int
Full bool
Defers bool // read deferred functions
Defers bool // read deferred functions (equivalent to passing StacktraceReadDefers in Opts)
Opts api.StacktraceOptions
Cfg *api.LoadConfig
}
@ -172,8 +173,11 @@ func (s *RPCServer) Stacktrace(arg StacktraceIn, out *StacktraceOut) error {
if cfg == nil && arg.Full {
cfg = &api.LoadConfig{true, 1, 64, 64, -1}
}
if arg.Defers {
arg.Opts |= api.StacktraceReadDefers
}
var err error
out.Locations, err = s.debugger.Stacktrace(arg.Id, arg.Depth, arg.Defers, api.LoadConfigToProc(cfg))
out.Locations, err = s.debugger.Stacktrace(arg.Id, arg.Depth, arg.Opts, api.LoadConfigToProc(cfg))
return err
}

@ -813,7 +813,7 @@ 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, false, &normalLoadConfig)
frames, err := c.Stacktrace(g.ID, 10, 0, &normalLoadConfig)
assertNoError(err, t, fmt.Sprintf("Stacktrace(%d)", g.ID))
for i, frame := range frames {
if frame.Function == nil {
@ -847,7 +847,7 @@ func TestClientServer_FullStacktrace(t *testing.T) {
t.Fatalf("Continue(): %v\n", state.Err)
}
frames, err := c.Stacktrace(-1, 10, false, &normalLoadConfig)
frames, err := c.Stacktrace(-1, 10, 0, &normalLoadConfig)
assertNoError(err, t, "Stacktrace")
cur := 3
@ -926,7 +926,7 @@ func TestIssue355(t *testing.T) {
assertError(err, t, "ListRegisters()")
_, _, err = c.ListGoroutines(0, 0)
assertError(err, t, "ListGoroutines()")
_, err = c.Stacktrace(gid, 10, false, &normalLoadConfig)
_, err = c.Stacktrace(gid, 10, 0, &normalLoadConfig)
assertError(err, t, "Stacktrace()")
_, err = c.FindLocation(api.EvalScope{gid, 0, 0}, "+1")
assertError(err, t, "FindLocation()")
@ -1049,7 +1049,7 @@ func TestNegativeStackDepthBug(t *testing.T) {
ch := c.Continue()
state := <-ch
assertNoError(state.Err, t, "Continue()")
_, err = c.Stacktrace(-1, -2, false, &normalLoadConfig)
_, err = c.Stacktrace(-1, -2, 0, &normalLoadConfig)
assertError(err, t, "Stacktrace()")
})
}