// Set a breakpoint at every reachable location, as well as the return address. Without // the benefit of an AST we can't be sure we're not at a branching statement and thus // cannot accurately predict where we may end up. func (thread *Thread) cnext(curpc uint64, fde *frame.FrameDescriptionEntry) error { pcs := thread.dbp.lineInfo.AllPCsBetween(fde.Begin(), fde.End()) ret, err := thread.ReturnAddress() if err != nil { return err } pcs = append(pcs, ret) return thread.setNextTempBreakpoints(curpc, pcs) }
// Use the AST to determine potential next lines. func (thread *Thread) next(curpc uint64, fde *frame.FrameDescriptionEntry, file string, line int) error { lines, err := thread.dbp.ast.NextLines(file, line) if err != nil { if _, ok := err.(source.NoNodeError); !ok { return err } } ret, err := thread.ReturnAddress() if err != nil { return err } pcs := make([]uint64, 0, len(lines)) for i := range lines { pcs = append(pcs, thread.dbp.lineInfo.AllPCsForFileLine(file, lines[i])...) } var covered bool for i := range pcs { if fde.Cover(pcs[i]) { covered = true break } } if !covered { fn := thread.dbp.goSymTable.PCToFunc(ret) if fn != nil && fn.Name == "runtime.goexit" { g, err := thread.getG() if err != nil { return err } return GoroutineExitingError{goid: g.Id} } } pcs = append(pcs, ret) return thread.setNextTempBreakpoints(curpc, pcs) }
// Set breakpoints at every line, and the return address. Also look for // a deferred function and set a breakpoint there too. func (thread *Thread) next(curpc uint64, fde *frame.FrameDescriptionEntry, file string, line int) error { pcs := thread.dbp.lineInfo.AllPCsBetween(fde.Begin(), fde.End(), file) g, err := thread.GetG() if err != nil { return err } if g.DeferPC != 0 { f, lineno, _ := thread.dbp.goSymTable.PCToLine(g.DeferPC) for { lineno++ dpc, _, err := thread.dbp.goSymTable.LineToPC(f, lineno) if err == nil { // We want to avoid setting an actual breakpoint on the // entry point of the deferred function so instead create // a fake breakpoint which will be cleaned up later. thread.dbp.Breakpoints[g.DeferPC] = new(Breakpoint) defer func() { delete(thread.dbp.Breakpoints, g.DeferPC) }() if _, err = thread.dbp.SetTempBreakpoint(dpc); err != nil { return err } break } } } ret, err := thread.ReturnAddress() if err != nil { return err } var covered bool for i := range pcs { if fde.Cover(pcs[i]) { covered = true break } } if !covered { fn := thread.dbp.goSymTable.PCToFunc(ret) if fn != nil && fn.Name == "runtime.goexit" { g, err := thread.GetG() if err != nil { return err } return GoroutineExitingError{goid: g.Id} } } pcs = append(pcs, ret) return thread.setNextTempBreakpoints(curpc, pcs) }