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:
parent
e994047355
commit
efd628616b
@ -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()")
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user