diff --git a/_fixtures/increment.go b/_fixtures/increment.go new file mode 100644 index 0000000000000000000000000000000000000000..3d8da94f84d1d3ede50a7bda8db80701914761ea --- /dev/null +++ b/_fixtures/increment.go @@ -0,0 +1,18 @@ +package main + +import "fmt" + +// Increment Natural number y +func Increment(y uint) uint { + if y == 0 { + return 1 + } + if y%2 == 1 { + return (2 * Increment(y/2)) + } + return y + 1 +} + +func main() { + fmt.Printf("%d\n", Increment(3)) +} diff --git a/pkg/proc/breakpoints.go b/pkg/proc/breakpoints.go index 1d7dab26ae1c23fa636811f17c74280d13e2004b..1e9055991c2e8169a2e942095268e6b625ef5f05 100644 --- a/pkg/proc/breakpoints.go +++ b/pkg/proc/breakpoints.go @@ -117,11 +117,15 @@ func (bp *Breakpoint) CheckCondition(thread Thread) (bool, error) { } } } + return evalBreakpointCondition(thread, bp.Cond) +} + +func evalBreakpointCondition(thread Thread, cond ast.Expr) (bool, error) { scope, err := GoroutineScope(thread) if err != nil { return true, err } - v, err := scope.evalAST(bp.Cond) + v, err := scope.evalAST(cond) if err != nil { return true, fmt.Errorf("error evaluating expression: %v", err) } diff --git a/pkg/proc/core/core.go b/pkg/proc/core/core.go index 5183743114e1c2d987336ac0f67e98dbee2c7d3f..c0cc84a656ebe86b9ed8762cc9cf61a1f098bd1f 100644 --- a/pkg/proc/core/core.go +++ b/pkg/proc/core/core.go @@ -248,6 +248,10 @@ func (t *Thread) Blocked() bool { return false } +func (t *Thread) SetCurrentBreakpoint() error { + return nil +} + func (p *Process) Breakpoints() map[uint64]*proc.Breakpoint { return p.breakpoints } diff --git a/pkg/proc/eval.go b/pkg/proc/eval.go index b44add93758a917dfdb1b82175680a499c877678..532f4bcf6f6d06a10c060caa965bf5bd1252c601 100644 --- a/pkg/proc/eval.go +++ b/pkg/proc/eval.go @@ -71,6 +71,8 @@ func (scope *EvalScope) evalAST(t ast.Expr) (*Variable, error) { return nilVariable, nil } return scope.Gvar.clone(), nil + } else if maybePkg.Name == "runtime" && node.Sel.Name == "frameoff" { + return newConstant(constant.MakeInt64(scope.CFA-int64(scope.StackHi)), scope.Mem), nil } else if v, err := scope.packageVarAddr(maybePkg.Name + "." + node.Sel.Name); err == nil { return v, nil } @@ -851,7 +853,7 @@ func (scope *EvalScope) evalBinary(node *ast.BinaryExpr) (*Variable, error) { r := xv.newVariable("", 0, typ) r.Value = rc if r.Kind == reflect.String { - r.Len = xv.Len+yv.Len + r.Len = xv.Len + yv.Len } return r, nil } diff --git a/pkg/proc/moduledata.go b/pkg/proc/moduledata.go index b7b043e578ab484962c712ae04c1719b4e232ae6..ba8072704b6fe7cea4407942c36935497f854b2a 100644 --- a/pkg/proc/moduledata.go +++ b/pkg/proc/moduledata.go @@ -13,7 +13,7 @@ type moduleData struct { func loadModuleData(bi *BinaryInfo, mem MemoryReadWriter) (err error) { bi.loadModuleDataOnce.Do(func() { - scope := &EvalScope{0, 0, mem, nil, bi} + scope := &EvalScope{0, 0, mem, nil, bi, 0} var md *Variable md, err = scope.packageVarAddr("runtime.firstmoduledata") if err != nil { @@ -119,7 +119,7 @@ func resolveNameOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem MemoryRea } func reflectOffsMapAccess(bi *BinaryInfo, off uintptr, mem MemoryReadWriter) (*Variable, error) { - scope := &EvalScope{0, 0, mem, nil, bi} + scope := &EvalScope{0, 0, mem, nil, bi, 0} reflectOffs, err := scope.packageVarAddr("runtime.reflectOffs") if err != nil { return nil, err diff --git a/pkg/proc/proc.go b/pkg/proc/proc.go index fe27e5bb839b2164f525d14f74d8a2a4f4dbc317..c9ff16f89ccc5ec2546f86521a8d13f6b682aa85 100644 --- a/pkg/proc/proc.go +++ b/pkg/proc/proc.go @@ -254,19 +254,41 @@ func SameGoroutineCondition(g *G) ast.Expr { } } +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), + } +} + // StepOut will continue until the current goroutine exits the // function currently being executed or a deferred function is executed func StepOut(dbp Process) error { selg := dbp.SelectedGoroutine() curthread := dbp.CurrentThread() - cond := SameGoroutineCondition(selg) - topframe, err := topframe(selg, curthread) + topframe, retframe, err := topframe(selg, curthread) if err != nil { return err } - pcs := []uint64{} + sameGCond := SameGoroutineCondition(selg) + retFrameCond := andFrameoffCondition(sameGCond, retframe.CFA-int64(retframe.StackHi)) var deferpc uint64 = 0 if filepath.Ext(topframe.Current.File) == ".go" { @@ -278,7 +300,6 @@ func StepOut(dbp Process) error { if err != nil { return err } - pcs = append(pcs, deferpc) } } } @@ -288,7 +309,7 @@ func StepOut(dbp Process) error { } if deferpc != 0 && deferpc != topframe.Current.PC { - bp, err := dbp.SetBreakpoint(deferpc, NextDeferBreakpoint, cond) + bp, err := dbp.SetBreakpoint(deferpc, NextDeferBreakpoint, sameGCond) if err != nil { if _, ok := err.(BreakpointExistsError); !ok { dbp.ClearInternalBreakpoints() @@ -304,11 +325,19 @@ func StepOut(dbp Process) error { } if topframe.Ret != 0 { - if err := setInternalBreakpoints(dbp, topframe.Current.PC, []uint64{topframe.Ret}, NextBreakpoint, cond); err != nil { - return err + _, err := dbp.SetBreakpoint(topframe.Ret, NextBreakpoint, retFrameCond) + if err != nil { + if _, isexists := err.(BreakpointExistsError); !isexists { + dbp.ClearInternalBreakpoints() + return err + } } } + if bp, _, _ := curthread.Breakpoint(); bp == nil { + curthread.SetCurrentBreakpoint() + } + return Continue(dbp) } @@ -402,7 +431,7 @@ func GoroutinesInfo(dbp Process) ([]*G, error) { } func GetGoInformation(p Process) (ver GoVersion, isextld bool, err error) { - scope := &EvalScope{0, 0, p.CurrentThread(), nil, p.BinInfo()} + scope := &EvalScope{0, 0, p.CurrentThread(), nil, p.BinInfo(), 0} vv, err := scope.packageVarAddr("runtime.buildVersion") if err != nil { return ver, false, fmt.Errorf("Could not determine version number: %v", err) @@ -482,10 +511,10 @@ func ConvertEvalScope(dbp Process, gid, frame int) (*EvalScope, error) { PC, CFA := locs[frame].Current.PC, locs[frame].CFA - return &EvalScope{PC, CFA, thread, g.variable, dbp.BinInfo()}, nil + return &EvalScope{PC, CFA, thread, g.variable, dbp.BinInfo(), g.stackhi}, nil } // FrameToScope returns a new EvalScope for this frame func FrameToScope(p Process, frame Stackframe) *EvalScope { - return &EvalScope{frame.Current.PC, frame.CFA, p.CurrentThread(), nil, p.BinInfo()} + return &EvalScope{frame.Current.PC, frame.CFA, p.CurrentThread(), nil, p.BinInfo(), frame.StackHi} } diff --git a/pkg/proc/proc_test.go b/pkg/proc/proc_test.go index 3e19cacc31288d3c1bbf2768dac4cc70eb258bab..002d10feb2021f734b4e5b4b105ccbaff4812d5a 100644 --- a/pkg/proc/proc_test.go +++ b/pkg/proc/proc_test.go @@ -533,6 +533,7 @@ func TestNextConcurrentVariant2(t *testing.T) { initVval, _ := constant.Int64Val(initV.Value) assertNoError(err, t, "EvalVariable") for _, tc := range testcases { + t.Logf("test case %v", tc) g, err := proc.GetG(p.CurrentThread()) assertNoError(err, t, "GetG()") if p.SelectedGoroutine().ID != g.ID { @@ -2248,7 +2249,7 @@ func TestStepOut(t *testing.T) { f, lno = currentLineNumber(p, t) if lno != 35 { - t.Fatalf("wrong line number %s:%d, expected %d", f, lno, 34) + t.Fatalf("wrong line number %s:%d, expected %d", f, lno, 35) } }) } @@ -2880,3 +2881,56 @@ func TestEnvironment(t *testing.T) { } }) } + +func getFrameOff(p proc.Process, t *testing.T) int64 { + frameoffvar, err := evalVariable(p, "runtime.frameoff") + assertNoError(err, t, "EvalVariable(runtime.frameoff)") + frameoff, _ := constant.Int64Val(frameoffvar.Value) + return frameoff +} + +func TestRecursiveNext(t *testing.T) { + protest.AllowRecording(t) + testcases := []nextTest{ + {6, 7}, + {7, 10}, + {10, 11}, + {11, 17}, + } + testseq("increment", contNext, testcases, "main.Increment", t) + + withTestProcess("increment", t, func(p proc.Process, fixture protest.Fixture) { + bp, err := setFunctionBreakpoint(p, "main.Increment") + assertNoError(err, t, "setFunctionBreakpoint") + assertNoError(proc.Continue(p), t, "Continue") + _, err = p.ClearBreakpoint(bp.Addr) + assertNoError(err, t, "ClearBreakpoint") + assertNoError(proc.Next(p), t, "Next 1") + assertNoError(proc.Next(p), t, "Next 2") + assertNoError(proc.Next(p), t, "Next 3") + frameoff0 := getFrameOff(p, t) + assertNoError(proc.Step(p), t, "Step") + frameoff1 := getFrameOff(p, t) + if frameoff0 == frameoff1 { + t.Fatalf("did not step into function?") + } + _, ln := currentLineNumber(p, t) + if ln != 6 { + t.Fatalf("program did not continue to expected location %d", ln) + } + assertNoError(proc.Next(p), t, "Next 4") + _, ln = currentLineNumber(p, t) + if ln != 7 { + t.Fatalf("program did not continue to expected location %d", ln) + } + assertNoError(proc.StepOut(p), t, "StepOut") + _, ln = currentLineNumber(p, t) + if ln != 11 { + t.Fatalf("program did not continue to expected location %d", ln) + } + frameoff2 := getFrameOff(p, t) + if frameoff0 != frameoff2 { + t.Fatalf("frame offset mismatch %x != %x", frameoff0, frameoff2) + } + }) +} diff --git a/pkg/proc/stack.go b/pkg/proc/stack.go index d7a2729eff2d0e60e1baa86f677f3ccf95ba29bb..ed06c70001a38f42f69c96c2a5e48cccabb202e1 100644 --- a/pkg/proc/stack.go +++ b/pkg/proc/stack.go @@ -30,6 +30,8 @@ type Stackframe struct { Call Location // Start address of the stack frame. CFA int64 + // High address of the stack. + StackHi uint64 // Description of the stack frame. FDE *frame.FrameDescriptionEntry // Return address for this stack frame (as read from the stack frame itself). @@ -45,7 +47,7 @@ func ThreadStacktrace(thread Thread, depth int) ([]Stackframe, error) { if err != nil { return nil, err } - it := newStackIterator(thread.BinInfo(), thread, regs.PC(), regs.SP(), regs.BP(), nil, -1) + it := newStackIterator(thread.BinInfo(), thread, regs.PC(), regs.SP(), regs.BP(), 0, nil, -1) return it.stacktrace(depth) } @@ -59,9 +61,9 @@ func (g *G) stackIterator() (*stackIterator, error) { if err != nil { return nil, err } - return newStackIterator(g.variable.bi, g.Thread, regs.PC(), regs.SP(), regs.BP(), stkbar, g.stkbarPos), nil + return newStackIterator(g.variable.bi, g.Thread, regs.PC(), regs.SP(), regs.BP(), g.stackhi, stkbar, g.stkbarPos), nil } - return newStackIterator(g.variable.bi, g.variable.mem, g.PC, g.SP, 0, stkbar, g.stkbarPos), nil + return newStackIterator(g.variable.bi, g.variable.mem, g.PC, g.SP, 0, g.stackhi, stkbar, g.stkbarPos), nil } // Stacktrace returns the stack trace for a goroutine. @@ -93,6 +95,7 @@ type stackIterator struct { mem MemoryReadWriter err error + stackhi uint64 stackBarrierPC uint64 stkbar []savedLR } @@ -102,7 +105,7 @@ type savedLR struct { val uint64 } -func newStackIterator(bi *BinaryInfo, mem MemoryReadWriter, pc, sp, bp uint64, stkbar []savedLR, stkbarPos int) *stackIterator { +func newStackIterator(bi *BinaryInfo, mem MemoryReadWriter, pc, sp, bp, stackhi uint64, stkbar []savedLR, stkbarPos int) *stackIterator { stackBarrierFunc := bi.goSymTable.LookupFunc(runtimeStackBarrier) // stack barriers were removed in Go 1.9 var stackBarrierPC uint64 if stackBarrierFunc != nil && stkbar != nil { @@ -122,7 +125,7 @@ func newStackIterator(bi *BinaryInfo, mem MemoryReadWriter, pc, sp, bp uint64, s } stkbar = stkbar[stkbarPos:] } - return &stackIterator{pc: pc, sp: sp, bp: bp, top: true, bi: bi, mem: mem, err: nil, atend: false, stackBarrierPC: stackBarrierPC, stkbar: stkbar} + return &stackIterator{pc: pc, sp: sp, bp: bp, top: true, bi: bi, mem: mem, err: nil, atend: false, stackhi: stackhi, stackBarrierPC: stackBarrierPC, stkbar: stkbar} } // Next points the iterator to the next stack frame. @@ -206,7 +209,7 @@ func (it *stackIterator) newStackframe(pc uint64, cfa int64, retaddr uintptr, fd if err != nil { return Stackframe{}, err } - r := Stackframe{Current: Location{PC: pc, File: f, Line: l, Fn: fn}, CFA: cfa, FDE: fde, Ret: ret, addrret: uint64(retaddr)} + r := Stackframe{Current: Location{PC: pc, File: f, Line: l, Fn: fn}, CFA: cfa, FDE: fde, Ret: ret, addrret: uint64(retaddr), StackHi: it.stackhi} if !top { r.Call.File, r.Call.Line, r.Call.Fn = it.bi.PCToLine(pc - 1) r.Call.PC = r.Current.PC diff --git a/pkg/proc/threads.go b/pkg/proc/threads.go index 0dd91280484fcd7cfcbb8acdf55b5a0cbc315cd6..5b4a560f9b1a4890bedca5e5e8bd90c0ff8cbacd 100644 --- a/pkg/proc/threads.go +++ b/pkg/proc/threads.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "go/ast" + "go/token" "path/filepath" "reflect" "strings" @@ -31,6 +32,8 @@ type Thread interface { StepInstruction() error // Blocked returns true if the thread is blocked Blocked() bool + // SetCurrentBreakpoint updates the current breakpoint of this thread + SetCurrentBreakpoint() error } // Location represents the location of a thread. @@ -51,26 +54,30 @@ func (tbe ThreadBlockedError) Error() string { return "" } -// returns topmost frame of g or thread if g is nil -func topframe(g *G, thread Thread) (Stackframe, error) { +// topframe returns the two topmost frames of g, or thread if g is nil. +func topframe(g *G, thread Thread) (Stackframe, Stackframe, error) { var frames []Stackframe var err error if g == nil { if thread.Blocked() { - return Stackframe{}, ThreadBlockedError{} + return Stackframe{}, Stackframe{}, ThreadBlockedError{} } - frames, err = ThreadStacktrace(thread, 0) + frames, err = ThreadStacktrace(thread, 1) } else { - frames, err = g.Stacktrace(0) + frames, err = g.Stacktrace(1) } if err != nil { - return Stackframe{}, err + return Stackframe{}, Stackframe{}, err } - if len(frames) < 1 { - return Stackframe{}, errors.New("empty stack trace") + switch len(frames) { + case 0: + return Stackframe{}, Stackframe{}, errors.New("empty stack trace") + case 1: + return frames[0], Stackframe{}, nil + default: + return frames[0], frames[1], nil } - return frames[0], nil } // Set breakpoints at every line, and the return address. Also look for @@ -80,10 +87,20 @@ func topframe(g *G, thread Thread) (Stackframe, error) { // a breakpoint of kind StepBreakpoint is set on the CALL instruction, // Continue will take care of setting a breakpoint to the destination // once the CALL is reached. +// +// Regardless of stepInto the following breakpoints will be set: +// - a breakpoint on the first deferred function with NextDeferBreakpoint +// kind, the list of all the addresses to deferreturn calls in this function +// and condition checking that we remain on the same goroutine +// - a breakpoint on each line of the function, with a condition checking +// that we stay on the same stack frame and goroutine. +// - a breakpoint on the return address of the function, with a condition +// checking that we move to the previous stack frame and stay on the same +// goroutine. func next(dbp Process, stepInto bool) error { selg := dbp.SelectedGoroutine() curthread := dbp.CurrentThread() - topframe, err := topframe(selg, curthread) + topframe, retframe, err := topframe(selg, curthread) if err != nil { return err } @@ -117,7 +134,21 @@ func next(dbp Process, stepInto bool) error { } } - cond := SameGoroutineCondition(selg) + sameGCond := SameGoroutineCondition(selg) + retFrameCond := andFrameoffCondition(sameGCond, retframe.CFA-int64(retframe.StackHi)) + sameFrameCond := andFrameoffCondition(sameGCond, topframe.CFA-int64(topframe.StackHi)) + var sameOrRetFrameCond ast.Expr + if sameGCond != nil { + sameOrRetFrameCond = &ast.BinaryExpr{ + Op: token.LAND, + X: sameGCond, + Y: &ast.BinaryExpr{ + Op: token.LOR, + X: frameoffCondition(topframe.CFA - int64(topframe.StackHi)), + Y: frameoffCondition(retframe.CFA - int64(retframe.StackHi)), + }, + } + } if stepInto { for _, instr := range text { @@ -126,12 +157,12 @@ func next(dbp Process, stepInto bool) error { } if instr.DestLoc != nil && instr.DestLoc.Fn != nil { - if err := setStepIntoBreakpoint(dbp, []AsmInstruction{instr}, cond); err != nil { + if err := setStepIntoBreakpoint(dbp, []AsmInstruction{instr}, sameGCond); err != nil { return err } } else { // Non-absolute call instruction, set a StepBreakpoint here - if _, err := dbp.SetBreakpoint(instr.Loc.PC, StepBreakpoint, cond); err != nil { + if _, err := dbp.SetBreakpoint(instr.Loc.PC, StepBreakpoint, sameGCond); err != nil { if _, ok := err.(BreakpointExistsError); !ok { return err } @@ -165,7 +196,7 @@ func next(dbp Process, stepInto bool) error { } } if deferpc != 0 && deferpc != topframe.Current.PC { - bp, err := dbp.SetBreakpoint(deferpc, NextDeferBreakpoint, cond) + bp, err := dbp.SetBreakpoint(deferpc, NextDeferBreakpoint, sameGCond) if err != nil { if _, ok := err.(BreakpointExistsError); !ok { return err @@ -201,9 +232,33 @@ func next(dbp Process, stepInto bool) error { } // Add a breakpoint on the return address for the current frame - pcs = append(pcs, topframe.Ret) + for _, pc := range pcs { + if _, err := dbp.SetBreakpoint(pc, NextBreakpoint, sameFrameCond); err != nil { + if _, ok := err.(BreakpointExistsError); !ok { + dbp.ClearInternalBreakpoints() + return err + } + } + + } + if bp, err := dbp.SetBreakpoint(topframe.Ret, NextBreakpoint, retFrameCond); err != nil { + if _, isexists := err.(BreakpointExistsError); isexists { + if bp.Kind == NextBreakpoint { + // If the return address shares the same address with one of the lines + // of the function (because we are stepping through a recursive + // function) then the corresponding breakpoint should be active both on + // this frame and on the return frame. + bp.Cond = sameOrRetFrameCond + } + } else { + return err + } + } + if bp, _, _ := curthread.Breakpoint(); bp == nil { + curthread.SetCurrentBreakpoint() + } success = true - return setInternalBreakpoints(dbp, topframe.Current.PC, pcs, NextBreakpoint, cond) + return nil } func setStepIntoBreakpoint(dbp Process, text []AsmInstruction, cond ast.Expr) error { @@ -246,23 +301,6 @@ func setStepIntoBreakpoint(dbp Process, text []AsmInstruction, cond ast.Expr) er return nil } -// setInternalBreakpoints sets a breakpoint to all addresses specified in pcs -// skipping over curpc and curpc-1 -func setInternalBreakpoints(dbp Process, curpc uint64, pcs []uint64, kind BreakpointKind, cond ast.Expr) error { - for i := range pcs { - if pcs[i] == curpc || pcs[i] == curpc-1 { - continue - } - if _, err := dbp.SetBreakpoint(pcs[i], kind, cond); err != nil { - if _, ok := err.(BreakpointExistsError); !ok { - dbp.ClearInternalBreakpoints() - return err - } - } - } - return nil -} - func getGVariable(thread Thread) (*Variable, error) { arch := thread.Arch() regs, err := thread.Registers(false) @@ -345,7 +383,7 @@ func ThreadScope(thread Thread) (*EvalScope, error) { if len(locations) < 1 { return nil, errors.New("could not decode first frame") } - return &EvalScope{locations[0].Current.PC, locations[0].CFA, thread, nil, thread.BinInfo()}, nil + return &EvalScope{locations[0].Current.PC, locations[0].CFA, thread, nil, thread.BinInfo(), 0}, nil } // GoroutineScope returns an EvalScope for the goroutine running on this thread. @@ -357,11 +395,11 @@ func GoroutineScope(thread Thread) (*EvalScope, error) { if len(locations) < 1 { return nil, errors.New("could not decode first frame") } - gvar, err := getGVariable(thread) + g, err := GetG(thread) if err != nil { return nil, err } - return &EvalScope{locations[0].Current.PC, locations[0].CFA, thread, gvar, thread.BinInfo()}, nil + return &EvalScope{locations[0].Current.PC, locations[0].CFA, thread, g.variable, thread.BinInfo(), g.stackhi}, nil } func onRuntimeBreakpoint(thread Thread) bool { @@ -384,12 +422,30 @@ func onNextGoroutine(thread Thread, breakpoints map[uint64]*Breakpoint) (bool, e if bp == nil { return false, nil } - if bp.Kind == NextDeferBreakpoint { - // we just want to check the condition on the goroutine id here - bp.Kind = NextBreakpoint - defer func() { - bp.Kind = NextDeferBreakpoint - }() + // Internal breakpoint conditions can take multiple different forms: + // Step into breakpoints: + // runtime.curg.goid == X + // Next or StepOut breakpoints: + // runtime.curg.goid == X && runtime.frameoff == Y + // Breakpoints that can be hit either by stepping on a line in the same + // function or by returning from the function: + // runtime.curg.goid == X && (runtime.frameoff == Y || runtime.frameoff == Z) + // Here we are only interested in testing the runtime.curg.goid clause. + w := onNextGoroutineWalker{thread: thread} + ast.Walk(&w, bp.Cond) + return w.ret, w.err +} + +type onNextGoroutineWalker struct { + thread Thread + ret bool + err error +} + +func (w *onNextGoroutineWalker) Visit(n ast.Node) ast.Visitor { + if binx, isbin := n.(*ast.BinaryExpr); isbin && binx.Op == token.EQL && exprToString(binx.X) == "runtime.curg.goid" { + w.ret, w.err = evalBreakpointCondition(w.thread, n.(ast.Expr)) + return nil } - return bp.CheckCondition(thread) + return w } diff --git a/pkg/proc/variables.go b/pkg/proc/variables.go index afa8fb31d416f7a4099461ebb654b2e4ee22ae8b..ef0df6e30412e852316785055bc74a7d72b61d21 100644 --- a/pkg/proc/variables.go +++ b/pkg/proc/variables.go @@ -124,6 +124,7 @@ type G struct { Status uint64 stkbarVar *Variable // stkbar field of g struct stkbarPos int // stkbarPos field of g struct + stackhi uint64 // value of stack.hi // Information on goroutine location CurrentLoc Location @@ -142,6 +143,7 @@ type EvalScope struct { Mem MemoryReadWriter // Target's memory Gvar *Variable BinInfo *BinaryInfo + StackHi uint64 } // IsNilErr is returned when a variable is nil. @@ -377,7 +379,7 @@ func (gvar *Variable) parseG() (*G, error) { } gvar = gvar.maybeDereference() } - gvar.loadValue(LoadConfig{false, 1, 64, 0, -1}) + gvar.loadValue(LoadConfig{false, 2, 64, 0, -1}) if gvar.Unreadable != nil { return nil, gvar.Unreadable } @@ -387,6 +389,12 @@ func (gvar *Variable) parseG() (*G, error) { id, _ := constant.Int64Val(gvar.fieldVariable("goid").Value) gopc, _ := constant.Int64Val(gvar.fieldVariable("gopc").Value) waitReason := constant.StringVal(gvar.fieldVariable("waitreason").Value) + var stackhi uint64 + if stackVar := gvar.fieldVariable("stack"); stackVar != nil { + if stackhiVar := stackVar.fieldVariable("hi"); stackhiVar != nil { + stackhi, _ = constant.Uint64Val(stackhiVar.Value) + } + } stkbarVar, _ := gvar.structMember("stkbar") stkbarVarPosFld := gvar.fieldVariable("stkbarPos") @@ -408,6 +416,7 @@ func (gvar *Variable) parseG() (*G, error) { variable: gvar, stkbarVar: stkbarVar, stkbarPos: int(stkbarPos), + stackhi: stackhi, } return g, nil }