1package interp 2 3import ( 4 "context" 5 "errors" 6 "fmt" 7 "go/token" 8 "reflect" 9 "sort" 10 "sync" 11) 12 13var ( 14 // ErrNotLive indicates that the specified ID does not refer to a (live) Go 15 // routine. 16 ErrNotLive = errors.New("not live") 17 18 // ErrRunning indicates that the specified Go routine is running. 19 ErrRunning = errors.New("running") 20 21 // ErrNotRunning indicates that the specified Go routine is running. 22 ErrNotRunning = errors.New("not running") 23) 24 25var rNodeType = reflect.TypeOf((*node)(nil)).Elem() 26 27// A Debugger can be used to debug a Yaegi program. 28type Debugger struct { 29 interp *Interpreter 30 events func(*DebugEvent) 31 context context.Context 32 cancel context.CancelFunc 33 34 gWait *sync.WaitGroup 35 gLock *sync.Mutex 36 gID int 37 gLive map[int]*debugRoutine 38 39 result reflect.Value 40 err error 41} 42 43// go routine debug state. 44type debugRoutine struct { 45 id int 46 47 mode DebugEventReason 48 running bool 49 resume chan struct{} 50 51 fDepth int 52 fStep int 53} 54 55// node debug state. 56type nodeDebugData struct { 57 program *Program 58 breakOnLine bool 59 breakOnCall bool 60} 61 62// frame debug state. 63type frameDebugData struct { 64 g *debugRoutine 65 node *node 66 name string 67 kind frameKind 68 scope *scope 69} 70 71// frame kind. 72type frameKind int 73 74const ( 75 // interpreter root frame. 76 frameRoot frameKind = iota + 1 77 78 // function call frame. 79 frameCall 80 81 // closure capture frame. 82 frameClosure 83) 84 85// DebugOptions are the debugger options. 86type DebugOptions struct { 87 // If true, Go routine IDs start at 1 instead of 0. 88 GoRoutineStartAt1 bool 89} 90 91// A DebugEvent is an event generated by a debugger. 92type DebugEvent struct { 93 debugger *Debugger 94 reason DebugEventReason 95 frame *frame 96} 97 98// DebugFrame provides access to stack frame information while debugging a 99// program. 100type DebugFrame struct { 101 event *DebugEvent 102 frames []*frame 103} 104 105// DebugFrameScope provides access to scoped variables while debugging a 106// program. 107type DebugFrameScope struct { 108 frame *frame 109} 110 111// DebugVariable is the name and value of a variable from a debug session. 112type DebugVariable struct { 113 Name string 114 Value reflect.Value 115} 116 117// DebugGoRoutine provides access to information about a Go routine while 118// debugging a program. 119type DebugGoRoutine struct { 120 id int 121} 122 123// Breakpoint is the result of attempting to set a breakpoint. 124type Breakpoint struct { 125 // Valid indicates whether the breakpoint was successfully set. 126 Valid bool 127 128 // Position indicates the source position of the breakpoint. 129 Position token.Position 130} 131 132// DebugEventReason is the reason a debug event occurred. 133type DebugEventReason int 134 135const ( 136 // continue execution normally. 137 debugRun DebugEventReason = iota 138 139 // DebugPause is emitted when a pause request is completed. Can be used with 140 // Interrupt to request a pause. 141 DebugPause 142 143 // DebugBreak is emitted when a debug target hits a breakpoint. 144 DebugBreak 145 146 // DebugEntry is emitted when a debug target starts executing. Can be used 147 // with Step to produce a corresponding event when execution starts. 148 DebugEntry 149 150 // DebugStepInto is emitted when a stepInto request is completed. Can be 151 // used with Step or Interrupt to request a stepInto. 152 DebugStepInto 153 154 // DebugStepOver is emitted when a stepOver request is completed. Can be 155 // used with Step or Interrupt to request a stepOver. 156 DebugStepOver 157 158 // DebugStepOut is emitted when a stepOut request is completed. Can be used 159 // with Step or Interrupt to request a stepOut. 160 DebugStepOut 161 162 // DebugTerminate is emitted when a debug target terminates. Can be used 163 // with Interrupt to attempt to terminate the program. 164 DebugTerminate 165 166 // DebugEnterGoRoutine is emitted when a Go routine is entered. 167 DebugEnterGoRoutine 168 169 // DebugExitGoRoutine is emitted when a Go routine is exited. 170 DebugExitGoRoutine 171) 172 173// Debug initializes a debugger for the given program. 174// 175// The program will not start running until Step or Continue has been called. If 176// Step is called with DebugEntry, an entry event will be generated before the 177// first statement is executed. Otherwise, the debugger will behave as usual. 178func (interp *Interpreter) Debug(ctx context.Context, prog *Program, events func(*DebugEvent), opts *DebugOptions) *Debugger { 179 dbg := new(Debugger) 180 dbg.interp = interp 181 dbg.events = events 182 dbg.context, dbg.cancel = context.WithCancel(ctx) 183 dbg.gWait = new(sync.WaitGroup) 184 dbg.gLock = new(sync.Mutex) 185 dbg.gLive = make(map[int]*debugRoutine, 1) 186 187 if opts == nil { 188 opts = new(DebugOptions) 189 } 190 if opts.GoRoutineStartAt1 { 191 dbg.gID = 1 192 } 193 194 mainG := dbg.enterGoRoutine() 195 mainG.mode = DebugEntry 196 197 interp.debugger = dbg 198 interp.frame.debug = &frameDebugData{kind: frameRoot, g: mainG} 199 200 prog.root.Walk(func(n *node) bool { 201 n.setProgram(prog) 202 return true 203 }, nil) 204 205 go func() { 206 defer func() { interp.debugger = nil }() 207 defer events(&DebugEvent{reason: DebugTerminate}) 208 defer dbg.cancel() 209 210 <-mainG.resume 211 dbg.events(&DebugEvent{dbg, DebugEnterGoRoutine, interp.frame}) 212 dbg.result, dbg.err = interp.ExecuteWithContext(ctx, prog) 213 dbg.exitGoRoutine(mainG) 214 dbg.events(&DebugEvent{dbg, DebugExitGoRoutine, interp.frame}) 215 dbg.gWait.Wait() 216 }() 217 218 return dbg 219} 220 221// Wait blocks until all Go routines launched by the program have terminated. 222// Wait returns the results of `(*Interpreter).Execute`. 223func (dbg *Debugger) Wait() (reflect.Value, error) { 224 <-dbg.context.Done() 225 return dbg.result, dbg.err 226} 227 228// mark entry into a go routine. 229func (dbg *Debugger) enterGoRoutine() *debugRoutine { 230 g := new(debugRoutine) 231 g.resume = make(chan struct{}) 232 233 dbg.gWait.Add(1) 234 235 dbg.gLock.Lock() 236 g.id = dbg.gID 237 dbg.gID++ 238 dbg.gLive[g.id] = g 239 dbg.gLock.Unlock() 240 241 return g 242} 243 244// mark exit from a go routine. 245func (dbg *Debugger) exitGoRoutine(g *debugRoutine) { 246 dbg.gLock.Lock() 247 delete(dbg.gLive, g.id) 248 dbg.gLock.Unlock() 249 250 dbg.gWait.Done() 251} 252 253// get the state for a given go routine, if it's live. 254func (dbg *Debugger) getGoRoutine(id int) (*debugRoutine, bool) { 255 dbg.gLock.Lock() 256 g, ok := dbg.gLive[id] 257 dbg.gLock.Unlock() 258 return g, ok 259} 260 261// mark entry into a function call. 262func (dbg *Debugger) enterCall(nFunc, nCall *node, f *frame) { 263 if f.debug != nil { 264 f.debug.g.fDepth++ 265 return 266 } 267 268 f.debug = new(frameDebugData) 269 f.debug.g = f.anc.debug.g 270 f.debug.scope = nFunc.scope 271 272 switch nFunc.kind { 273 case funcLit: 274 f.debug.kind = frameCall 275 if nFunc.frame != nil { 276 nFunc.frame.debug.kind = frameClosure 277 nFunc.frame.debug.node = nFunc 278 } 279 280 case funcDecl: 281 f.debug.kind = frameCall 282 f.debug.name = nFunc.child[1].ident 283 } 284 285 if nCall != nil && nCall.anc.kind == goStmt { 286 f.debug.g = dbg.enterGoRoutine() 287 dbg.events(&DebugEvent{dbg, DebugEnterGoRoutine, f}) 288 } 289 290 f.debug.g.fDepth++ 291} 292 293// mark exit from a function call. 294func (dbg *Debugger) exitCall(nFunc, nCall *node, f *frame) { 295 _ = nFunc // ignore unused, so exitCall can have the same signature as enterCall 296 297 f.debug.g.fDepth-- 298 299 if nCall != nil && nCall.anc.kind == goStmt { 300 dbg.exitGoRoutine(f.debug.g) 301 dbg.events(&DebugEvent{dbg, DebugExitGoRoutine, f}) 302 } 303} 304 305// called by the interpreter prior to executing the node. 306func (dbg *Debugger) exec(n *node, f *frame) (stop bool) { 307 f.debug.node = n 308 309 if n != nil && n.pos == token.NoPos { 310 return false 311 } 312 313 g := f.debug.g 314 defer func() { g.running = true }() 315 316 e := &DebugEvent{dbg, g.mode, f} 317 switch { 318 case g.mode == DebugTerminate: 319 dbg.cancel() 320 return true 321 322 case n.shouldBreak(): 323 e.reason = DebugBreak 324 325 case g.mode == debugRun: 326 return false 327 328 case g.mode == DebugStepOut: 329 if g.fDepth >= g.fStep { 330 return false 331 } 332 333 case g.mode == DebugStepOver: 334 if g.fDepth > g.fStep { 335 return false 336 } 337 } 338 dbg.events(e) 339 340 g.running = false 341 select { 342 case <-g.resume: 343 return false 344 case <-dbg.context.Done(): 345 return true 346 } 347} 348 349// Continue continues execution of the specified Go routine. Continue returns 350// ErrNotLive if there is no Go routine with the corresponding ID, or if it is not 351// live. 352func (dbg *Debugger) Continue(id int) error { 353 g, ok := dbg.getGoRoutine(id) 354 if !ok { 355 return ErrNotLive 356 } 357 358 g.mode = debugRun 359 g.resume <- struct{}{} 360 return nil 361} 362 363// update the exec mode of this routine. 364func (g *debugRoutine) setMode(reason DebugEventReason) { 365 if g.mode == DebugTerminate { 366 return 367 } 368 369 if g.mode == DebugEntry && reason == DebugEntry { 370 return 371 } 372 373 switch reason { 374 case DebugStepInto, DebugStepOver, DebugStepOut: 375 g.mode, g.fStep = reason, g.fDepth 376 default: 377 g.mode = DebugPause 378 } 379} 380 381// Step issues a stepInto, stepOver, or stepOut request to a stopped Go routine. 382// Step returns ErrRunning if the Go routine is running. Step returns ErrNotLive 383// if there is no Go routine with the corresponding ID, or if it is not live. 384func (dbg *Debugger) Step(id int, reason DebugEventReason) error { 385 g, ok := dbg.getGoRoutine(id) 386 if !ok { 387 return ErrNotLive 388 } 389 390 if g.running { 391 return ErrRunning 392 } 393 394 g.setMode(reason) 395 g.resume <- struct{}{} 396 return nil 397} 398 399// Interrupt issues a stepInto, stepOver, or stepOut request to a running Go 400// routine. Interrupt returns ErrRunning if the Go routine is running. Interrupt 401// returns ErrNotLive if there is no Go routine with the corresponding ID, or if 402// it is not live. 403func (dbg *Debugger) Interrupt(id int, reason DebugEventReason) bool { 404 g, ok := dbg.getGoRoutine(id) 405 if !ok { 406 return false 407 } 408 409 g.setMode(reason) 410 return true 411} 412 413// Terminate attempts to terminate the program. 414func (dbg *Debugger) Terminate() { 415 dbg.gLock.Lock() 416 g := dbg.gLive 417 dbg.gLive = nil 418 dbg.gLock.Unlock() 419 420 for _, g := range g { 421 g.mode = DebugTerminate 422 close(g.resume) 423 } 424} 425 426// BreakpointTarget is the target of a request to set breakpoints. 427type BreakpointTarget func(*Debugger, func(*node)) 428 429// PathBreakpointTarget is used to set breapoints on compiled code by path. This 430// can be used to set breakpoints on code compiled with EvalPath, or source 431// packages loaded by Yaegi. 432func PathBreakpointTarget(path string) BreakpointTarget { 433 return func(dbg *Debugger, cb func(*node)) { 434 for _, r := range dbg.interp.roots { 435 f := dbg.interp.fset.File(r.pos) 436 if f != nil && f.Name() == path { 437 cb(r) 438 return 439 } 440 } 441 } 442} 443 444// ProgramBreakpointTarget is used to set breakpoints on a Program. 445func ProgramBreakpointTarget(prog *Program) BreakpointTarget { 446 return func(_ *Debugger, cb func(*node)) { 447 cb(prog.root) 448 } 449} 450 451// AllBreakpointTarget is used to set breakpoints on all compiled code. Do not 452// use with LineBreakpoint. 453func AllBreakpointTarget() BreakpointTarget { 454 return func(dbg *Debugger, cb func(*node)) { 455 for _, r := range dbg.interp.roots { 456 cb(r) 457 } 458 } 459} 460 461type breakpointSetup struct { 462 roots []*node 463 lines map[int]int 464 funcs map[string]int 465} 466 467// BreakpointRequest is a request to set a breakpoint. 468type BreakpointRequest func(*breakpointSetup, int) 469 470// LineBreakpoint requests a breakpoint on the given line. 471func LineBreakpoint(line int) BreakpointRequest { 472 return func(b *breakpointSetup, i int) { 473 b.lines[line] = i 474 } 475} 476 477// FunctionBreakpoint requests a breakpoint on the named function. 478func FunctionBreakpoint(name string) BreakpointRequest { 479 return func(b *breakpointSetup, i int) { 480 b.funcs[name] = i 481 } 482} 483 484// SetBreakpoints sets breakpoints for the given target. The returned array has 485// an entry for every request, in order. If a given breakpoint request cannot be 486// satisfied, the corresponding entry will be marked invalid. If the target 487// cannot be found, all entries will be marked invalid. 488func (dbg *Debugger) SetBreakpoints(target BreakpointTarget, requests ...BreakpointRequest) []Breakpoint { 489 // start with all breakpoints unverified 490 results := make([]Breakpoint, len(requests)) 491 492 // prepare all the requests 493 setup := new(breakpointSetup) 494 target(dbg, func(root *node) { 495 setup.roots = append(setup.roots, root) 496 setup.lines = make(map[int]int, len(requests)) 497 setup.funcs = make(map[string]int, len(requests)) 498 for i, rq := range requests { 499 rq(setup, i) 500 } 501 }) 502 503 // find breakpoints 504 for _, root := range setup.roots { 505 root.Walk(func(n *node) bool { 506 // function breakpoints 507 if len(setup.funcs) > 0 && n.kind == funcDecl { 508 // reset stale breakpoints 509 n.start.setBreakOnCall(false) 510 511 if i, ok := setup.funcs[n.child[1].ident]; ok && !results[i].Valid { 512 results[i].Valid = true 513 results[i].Position = dbg.interp.fset.Position(n.start.pos) 514 n.start.setBreakOnCall(true) 515 return true 516 } 517 } 518 519 // line breakpoints 520 if len(setup.lines) > 0 && n.pos.IsValid() && n.action != aNop && getExec(n) != nil { 521 // reset stale breakpoints 522 n.setBreakOnLine(false) 523 524 pos := dbg.interp.fset.Position(n.pos) 525 if i, ok := setup.lines[pos.Line]; ok && !results[i].Valid { 526 results[i].Valid = true 527 results[i].Position = pos 528 n.setBreakOnLine(true) 529 return true 530 } 531 } 532 533 return true 534 }, nil) 535 } 536 537 return results 538} 539 540// GoRoutines returns an array of live Go routines. 541func (dbg *Debugger) GoRoutines() []*DebugGoRoutine { 542 dbg.gLock.Lock() 543 r := make([]*DebugGoRoutine, 0, len(dbg.gLive)) 544 for id := range dbg.gLive { 545 r = append(r, &DebugGoRoutine{id}) 546 } 547 dbg.gLock.Unlock() 548 sort.Slice(r, func(i, j int) bool { return r[i].id < r[j].id }) 549 return r 550} 551 552// ID returns the ID of the Go routine. 553func (r *DebugGoRoutine) ID() int { return r.id } 554 555// Name returns "Goroutine {ID}". 556func (r *DebugGoRoutine) Name() string { return fmt.Sprintf("Goroutine %d", r.id) } 557 558// GoRoutine returns the ID of the Go routine that generated the event. 559func (evt *DebugEvent) GoRoutine() int { 560 if evt.frame.debug == nil { 561 return 0 562 } 563 return evt.frame.debug.g.id 564} 565 566// Reason returns the reason for the event. 567func (evt *DebugEvent) Reason() DebugEventReason { 568 return evt.reason 569} 570 571// Walk the stack trace frames. The root frame is included if and only if it is 572// the only frame. Closure frames are rolled up into the following call frame. 573func (evt *DebugEvent) walkFrames(fn func([]*frame) bool) { 574 if evt.frame == evt.frame.root { 575 fn([]*frame{evt.frame}) 576 return 577 } 578 579 var g *debugRoutine 580 if evt.frame.debug != nil { 581 g = evt.frame.debug.g 582 } 583 584 var frames []*frame 585 for f := evt.frame; f != nil && f != f.root && (f.debug == nil || f.debug.g == g); f = f.anc { 586 if f.debug == nil || f.debug.kind != frameCall { 587 frames = append(frames, f) 588 continue 589 } 590 591 if len(frames) > 0 { 592 if !fn(frames) { 593 return 594 } 595 } 596 597 frames = frames[:0] 598 frames = append(frames, f) 599 } 600 601 if len(frames) > 0 { 602 fn(frames) 603 } 604} 605 606// FrameDepth returns the number of call frames in the stack trace. 607func (evt *DebugEvent) FrameDepth() int { 608 if evt.frame == evt.frame.root { 609 return 1 610 } 611 612 var n int 613 evt.walkFrames(func([]*frame) bool { n++; return true }) 614 return n 615} 616 617// Frames returns the call frames in the range [start, end). 618func (evt *DebugEvent) Frames(start, end int) []*DebugFrame { 619 count := end - start 620 if count < 0 { 621 return nil 622 } 623 624 frames := []*DebugFrame{} 625 evt.walkFrames(func(f []*frame) bool { 626 df := &DebugFrame{evt, make([]*frame, len(f))} 627 copy(df.frames, f) 628 frames = append(frames, df) 629 return len(frames) < count 630 }) 631 return frames 632} 633 634// Name returns the name of the stack frame. For function calls to named 635// functions, this is the function name. 636func (f *DebugFrame) Name() string { 637 d := f.frames[0].debug 638 if d == nil { 639 return "<unknown>" 640 } 641 switch d.kind { 642 case frameRoot: 643 return "<init>" 644 case frameClosure: 645 return "<closure>" 646 case frameCall: 647 if d.name == "" { 648 return "<anonymous>" 649 } 650 return d.name 651 default: 652 return "<unknown>" 653 } 654} 655 656// Position returns the current position of the frame. This is effectively the 657// program counter/link register. May return `Position{}`. 658func (f *DebugFrame) Position() token.Position { 659 d := f.frames[0].debug 660 if d == nil || d.node == nil { 661 return token.Position{} 662 } 663 return f.event.debugger.interp.fset.Position(d.node.pos) 664} 665 666// Program returns the program associated with the current position of the 667// frame. May return nil. 668func (f *DebugFrame) Program() *Program { 669 d := f.frames[0].debug 670 if d == nil || d.node == nil { 671 return nil 672 } 673 674 return d.node.debug.program 675} 676 677// Scopes returns the variable scopes of the frame. 678func (f *DebugFrame) Scopes() []*DebugFrameScope { 679 s := make([]*DebugFrameScope, len(f.frames)) 680 for i, f := range f.frames { 681 s[i] = &DebugFrameScope{f} 682 } 683 return s 684} 685 686// IsClosure returns true if this is the capture scope of a closure. 687func (f *DebugFrameScope) IsClosure() bool { 688 return f.frame.debug != nil && f.frame.debug.kind == frameClosure 689} 690 691// Variables returns the names and values of the variables of the scope. 692func (f *DebugFrameScope) Variables() []*DebugVariable { 693 d := f.frame.debug 694 if d == nil || d.scope == nil { 695 return nil 696 } 697 698 index := map[int]string{} 699 scanScope(d.scope, index) 700 701 m := make([]*DebugVariable, 0, len(f.frame.data)) 702 for i, v := range f.frame.data { 703 if typ := v.Type(); typ.AssignableTo(rNodeType) || typ.Kind() == reflect.Ptr && typ.Elem().AssignableTo(rNodeType) { 704 continue 705 } 706 name, ok := index[i] 707 if !ok { 708 continue 709 } 710 711 m = append(m, &DebugVariable{name, v}) 712 } 713 return m 714} 715 716func scanScope(sc *scope, index map[int]string) { 717 for name, sym := range sc.sym { 718 if _, ok := index[sym.index]; ok { 719 continue 720 } 721 index[sym.index] = name 722 } 723 724 for _, ch := range sc.child { 725 if ch.def != sc.def { 726 continue 727 } 728 scanScope(ch, index) 729 } 730} 731