github.com/undoio/delve@v1.9.0/pkg/proc/target_exec.go (about) 1 package proc 2 3 import ( 4 "bytes" 5 "debug/dwarf" 6 "errors" 7 "fmt" 8 "go/ast" 9 "go/token" 10 "path/filepath" 11 "strings" 12 13 "github.com/undoio/delve/pkg/astutil" 14 "github.com/undoio/delve/pkg/dwarf/reader" 15 ) 16 17 const maxSkipAutogeneratedWrappers = 5 // maximum recursion depth for skipAutogeneratedWrappers 18 19 // ErrNoSourceForPC is returned when the given address 20 // does not correspond with a source file location. 21 type ErrNoSourceForPC struct { 22 pc uint64 23 } 24 25 func (err *ErrNoSourceForPC) Error() string { 26 return fmt.Sprintf("no source for PC %#x", err.pc) 27 } 28 29 // Next continues execution until the next source line. 30 func (dbp *Target) Next() (err error) { 31 if _, err := dbp.Valid(); err != nil { 32 return err 33 } 34 if dbp.Breakpoints().HasSteppingBreakpoints() { 35 return fmt.Errorf("next while nexting") 36 } 37 38 if err = next(dbp, false, false); err != nil { 39 dbp.ClearSteppingBreakpoints() 40 return 41 } 42 43 return dbp.Continue() 44 } 45 46 // Continue continues execution of the debugged 47 // process. It will continue until it hits a breakpoint 48 // or is otherwise stopped. 49 func (dbp *Target) Continue() error { 50 if _, err := dbp.Valid(); err != nil { 51 return err 52 } 53 for _, thread := range dbp.ThreadList() { 54 thread.Common().CallReturn = false 55 thread.Common().returnValues = nil 56 } 57 dbp.Breakpoints().WatchOutOfScope = nil 58 dbp.clearHardcodedBreakpoints() 59 dbp.cctx.CheckAndClearManualStopRequest() 60 defer func() { 61 // Make sure we clear internal breakpoints if we simultaneously receive a 62 // manual stop request and hit a breakpoint. 63 if dbp.cctx.CheckAndClearManualStopRequest() { 64 dbp.StopReason = StopManual 65 dbp.clearHardcodedBreakpoints() 66 if dbp.KeepSteppingBreakpoints&HaltKeepsSteppingBreakpoints == 0 { 67 dbp.ClearSteppingBreakpoints() 68 } 69 } 70 }() 71 for { 72 if dbp.cctx.CheckAndClearManualStopRequest() { 73 dbp.StopReason = StopManual 74 dbp.clearHardcodedBreakpoints() 75 if dbp.KeepSteppingBreakpoints&HaltKeepsSteppingBreakpoints == 0 { 76 dbp.ClearSteppingBreakpoints() 77 } 78 return nil 79 } 80 dbp.ClearCaches() 81 trapthread, stopReason, contOnceErr := dbp.proc.ContinueOnce(dbp.cctx) 82 dbp.StopReason = stopReason 83 84 threads := dbp.ThreadList() 85 for _, thread := range threads { 86 if thread.Breakpoint().Breakpoint != nil { 87 thread.Breakpoint().Breakpoint.checkCondition(dbp, thread, thread.Breakpoint()) 88 } 89 } 90 91 if contOnceErr != nil { 92 // Attempt to refresh status of current thread/current goroutine, see 93 // Issue #2078. 94 // Errors are ignored because depending on why ContinueOnce failed this 95 // might very well not work. 96 if valid, _ := dbp.Valid(); valid { 97 if trapthread != nil { 98 _ = dbp.SwitchThread(trapthread.ThreadID()) 99 } else if curth := dbp.CurrentThread(); curth != nil { 100 dbp.selectedGoroutine, _ = GetG(curth) 101 } 102 } 103 if pe, ok := contOnceErr.(ErrProcessExited); ok { 104 dbp.exitStatus = pe.Status 105 } 106 return contOnceErr 107 } 108 if dbp.StopReason == StopLaunched { 109 dbp.ClearSteppingBreakpoints() 110 } 111 112 callInjectionDone, callErr := callInjectionProtocol(dbp, threads) 113 hcbpErr := dbp.handleHardcodedBreakpoints(trapthread, threads) 114 // callErr and hcbpErr check delayed until after pickCurrentThread, which 115 // must always happen, otherwise the debugger could be left in an 116 // inconsistent state. 117 118 if err := pickCurrentThread(dbp, trapthread, threads); err != nil { 119 return err 120 } 121 122 if callErr != nil { 123 return callErr 124 } 125 if hcbpErr != nil { 126 return hcbpErr 127 } 128 129 curthread := dbp.CurrentThread() 130 curbp := curthread.Breakpoint() 131 132 switch { 133 case curbp.Active && curbp.Stepping: 134 if curbp.SteppingInto { 135 // See description of proc.(*Process).next for the meaning of StepBreakpoints 136 if err := conditionErrors(threads); err != nil { 137 return err 138 } 139 if dbp.GetDirection() == Forward { 140 text, err := disassembleCurrentInstruction(dbp, curthread, 0) 141 if err != nil { 142 return err 143 } 144 var fn *Function 145 if loc, _ := curthread.Location(); loc != nil { 146 fn = loc.Fn 147 } 148 // here we either set a breakpoint into the destination of the CALL 149 // instruction or we determined that the called function is hidden, 150 // either way we need to resume execution 151 if err = setStepIntoBreakpoint(dbp, fn, text, sameGoroutineCondition(dbp.SelectedGoroutine())); err != nil { 152 return err 153 } 154 } else { 155 if err := dbp.ClearSteppingBreakpoints(); err != nil { 156 return err 157 } 158 return dbp.StepInstruction() 159 } 160 } else { 161 curthread.Common().returnValues = curbp.Breakpoint.returnInfo.Collect(dbp, curthread) 162 if err := dbp.ClearSteppingBreakpoints(); err != nil { 163 return err 164 } 165 dbp.StopReason = StopNextFinished 166 return conditionErrors(threads) 167 } 168 case curbp.Active: 169 onNextGoroutine, err := onNextGoroutine(dbp, curthread, dbp.Breakpoints()) 170 if err != nil { 171 return err 172 } 173 if onNextGoroutine && 174 (!isTraceOrTraceReturn(curbp.Breakpoint) || dbp.KeepSteppingBreakpoints&TracepointKeepsSteppingBreakpoints == 0) { 175 err := dbp.ClearSteppingBreakpoints() 176 if err != nil { 177 return err 178 } 179 } 180 if curbp.LogicalID() == unrecoveredPanicID { 181 dbp.ClearSteppingBreakpoints() 182 } 183 if curbp.LogicalID() != hardcodedBreakpointID { 184 dbp.StopReason = StopBreakpoint 185 } 186 if curbp.Breakpoint.WatchType != 0 { 187 dbp.StopReason = StopWatchpoint 188 } 189 return conditionErrors(threads) 190 default: 191 // not a manual stop, not on runtime.Breakpoint, not on a breakpoint, just repeat 192 } 193 if callInjectionDone { 194 // a call injection was finished, don't let a breakpoint with a failed 195 // condition or a step breakpoint shadow this. 196 dbp.StopReason = StopCallReturned 197 return conditionErrors(threads) 198 } 199 } 200 } 201 202 func isTraceOrTraceReturn(bp *Breakpoint) bool { 203 if bp.Logical == nil { 204 return false 205 } 206 return bp.Logical.Tracepoint || bp.Logical.TraceReturn 207 } 208 209 func conditionErrors(threads []Thread) error { 210 var condErr error 211 for _, th := range threads { 212 if bp := th.Breakpoint(); bp.Breakpoint != nil && bp.CondError != nil { 213 if condErr == nil { 214 condErr = bp.CondError 215 } else { 216 return fmt.Errorf("multiple errors evaluating conditions") 217 } 218 } 219 } 220 return condErr 221 } 222 223 // pick a new dbp.currentThread, with the following priority: 224 // - a thread with an active stepping breakpoint 225 // - a thread with an active breakpoint, prioritizing trapthread 226 // - trapthread 227 func pickCurrentThread(dbp *Target, trapthread Thread, threads []Thread) error { 228 for _, th := range threads { 229 if bp := th.Breakpoint(); bp.Active && bp.Stepping { 230 return dbp.SwitchThread(th.ThreadID()) 231 } 232 } 233 if bp := trapthread.Breakpoint(); bp.Active { 234 return dbp.SwitchThread(trapthread.ThreadID()) 235 } 236 for _, th := range threads { 237 if bp := th.Breakpoint(); bp.Active { 238 return dbp.SwitchThread(th.ThreadID()) 239 } 240 } 241 return dbp.SwitchThread(trapthread.ThreadID()) 242 } 243 244 func disassembleCurrentInstruction(p Process, thread Thread, off int64) ([]AsmInstruction, error) { 245 regs, err := thread.Registers() 246 if err != nil { 247 return nil, err 248 } 249 pc := regs.PC() + uint64(off) 250 return disassemble(p.Memory(), regs, p.Breakpoints(), p.BinInfo(), pc, pc+uint64(p.BinInfo().Arch.MaxInstructionLength()), true) 251 } 252 253 // stepInstructionOut repeatedly calls StepInstruction until the current 254 // function is neither fnname1 or fnname2. 255 // This function is used to step out of runtime.Breakpoint as well as 256 // runtime.debugCallV1. 257 func stepInstructionOut(dbp *Target, curthread Thread, fnname1, fnname2 string) error { 258 defer dbp.ClearCaches() 259 for { 260 if err := curthread.StepInstruction(); err != nil { 261 return err 262 } 263 loc, err := curthread.Location() 264 var locFnName string 265 if loc.Fn != nil { 266 locFnName = loc.Fn.Name 267 // Calls to runtime.Breakpoint are inlined in some versions of Go when 268 // inlining is enabled. Here we attempt to resolve any inlining. 269 dwarfTree, _ := loc.Fn.cu.image.getDwarfTree(loc.Fn.offset) 270 if dwarfTree != nil { 271 inlstack := reader.InlineStack(dwarfTree, loc.PC) 272 if len(inlstack) > 0 { 273 if locFnName2, ok := inlstack[0].Val(dwarf.AttrName).(string); ok { 274 locFnName = locFnName2 275 } 276 } 277 } 278 } 279 if err != nil || loc.Fn == nil || (locFnName != fnname1 && locFnName != fnname2) { 280 g, _ := GetG(curthread) 281 selg := dbp.SelectedGoroutine() 282 if g != nil && selg != nil && g.ID == selg.ID { 283 selg.CurrentLoc = *loc 284 } 285 return curthread.SetCurrentBreakpoint(true) 286 } 287 } 288 } 289 290 // Step will continue until another source line is reached. 291 // Will step into functions. 292 func (dbp *Target) Step() (err error) { 293 if _, err := dbp.Valid(); err != nil { 294 return err 295 } 296 if dbp.Breakpoints().HasSteppingBreakpoints() { 297 return fmt.Errorf("next while nexting") 298 } 299 300 if err = next(dbp, true, false); err != nil { 301 _ = dbp.ClearSteppingBreakpoints() 302 return err 303 } 304 305 if bpstate := dbp.CurrentThread().Breakpoint(); bpstate.Breakpoint != nil && bpstate.Active && bpstate.SteppingInto && dbp.GetDirection() == Backward { 306 dbp.ClearSteppingBreakpoints() 307 return dbp.StepInstruction() 308 } 309 310 return dbp.Continue() 311 } 312 313 // sameGoroutineCondition returns an expression that evaluates to true when 314 // the current goroutine is g. 315 func sameGoroutineCondition(g *G) ast.Expr { 316 if g == nil { 317 return nil 318 } 319 return astutil.Eql(astutil.Sel(astutil.PkgVar("runtime", "curg"), "goid"), astutil.Int(int64(g.ID))) 320 } 321 322 func frameoffCondition(frame *Stackframe) ast.Expr { 323 return astutil.Eql(astutil.PkgVar("runtime", "frameoff"), astutil.Int(frame.FrameOffset())) 324 } 325 326 // StepOut will continue until the current goroutine exits the 327 // function currently being executed or a deferred function is executed 328 func (dbp *Target) StepOut() error { 329 backward := dbp.GetDirection() == Backward 330 if _, err := dbp.Valid(); err != nil { 331 return err 332 } 333 if dbp.Breakpoints().HasSteppingBreakpoints() { 334 return fmt.Errorf("next while nexting") 335 } 336 337 selg := dbp.SelectedGoroutine() 338 curthread := dbp.CurrentThread() 339 340 topframe, retframe, err := topframe(selg, curthread) 341 if err != nil { 342 return err 343 } 344 345 success := false 346 defer func() { 347 if !success { 348 dbp.ClearSteppingBreakpoints() 349 } 350 }() 351 352 if topframe.Inlined { 353 if err := next(dbp, false, true); err != nil { 354 return err 355 } 356 357 success = true 358 return dbp.Continue() 359 } 360 361 sameGCond := sameGoroutineCondition(selg) 362 363 if backward { 364 if err := stepOutReverse(dbp, topframe, retframe, sameGCond); err != nil { 365 return err 366 } 367 368 success = true 369 return dbp.Continue() 370 } 371 372 deferpc, err := setDeferBreakpoint(dbp, nil, topframe, sameGCond, false) 373 if err != nil { 374 return err 375 } 376 377 if topframe.Ret == 0 && deferpc == 0 { 378 return errors.New("nothing to stepout to") 379 } 380 381 if topframe.Ret != 0 { 382 topframe, retframe := skipAutogeneratedWrappersOut(selg, curthread, &topframe, &retframe) 383 retFrameCond := astutil.And(sameGCond, frameoffCondition(retframe)) 384 bp, err := allowDuplicateBreakpoint(dbp.SetBreakpoint(0, retframe.Current.PC, NextBreakpoint, retFrameCond)) 385 if err != nil { 386 return err 387 } 388 if bp != nil { 389 configureReturnBreakpoint(dbp.BinInfo(), bp, topframe, retFrameCond) 390 } 391 } 392 393 if bp := curthread.Breakpoint(); bp.Breakpoint == nil { 394 curthread.SetCurrentBreakpoint(false) 395 } 396 397 success = true 398 return dbp.Continue() 399 } 400 401 // StepInstruction will continue the current thread for exactly 402 // one instruction. This method affects only the thread 403 // associated with the selected goroutine. All other 404 // threads will remain stopped. 405 func (dbp *Target) StepInstruction() (err error) { 406 thread := dbp.CurrentThread() 407 g := dbp.SelectedGoroutine() 408 if g != nil { 409 if g.Thread == nil { 410 // Step called on parked goroutine 411 if _, err := dbp.SetBreakpoint(0, g.PC, NextBreakpoint, 412 sameGoroutineCondition(dbp.SelectedGoroutine())); err != nil { 413 return err 414 } 415 return dbp.Continue() 416 } 417 thread = g.Thread 418 } 419 dbp.ClearCaches() 420 if ok, err := dbp.Valid(); !ok { 421 return err 422 } 423 err = thread.StepInstruction() 424 if err != nil { 425 return err 426 } 427 thread.Breakpoint().Clear() 428 err = thread.SetCurrentBreakpoint(true) 429 if err != nil { 430 return err 431 } 432 if tg, _ := GetG(thread); tg != nil { 433 dbp.selectedGoroutine = tg 434 } 435 dbp.StopReason = StopNextFinished 436 return nil 437 } 438 439 // Set breakpoints at every line, and the return address. Also look for 440 // a deferred function and set a breakpoint there too. 441 // If stepInto is true it will also set breakpoints inside all 442 // functions called on the current source line, for non-absolute CALLs 443 // a breakpoint of kind StepBreakpoint is set on the CALL instruction, 444 // Continue will take care of setting a breakpoint to the destination 445 // once the CALL is reached. 446 // 447 // Regardless of stepInto the following breakpoints will be set: 448 // - a breakpoint on the first deferred function with NextDeferBreakpoint 449 // kind, the list of all the addresses to deferreturn calls in this function 450 // and condition checking that we remain on the same goroutine 451 // - a breakpoint on each line of the function, with a condition checking 452 // that we stay on the same stack frame and goroutine. 453 // - a breakpoint on the return address of the function, with a condition 454 // checking that we move to the previous stack frame and stay on the same 455 // goroutine. 456 // 457 // The breakpoint on the return address is *not* set if the current frame is 458 // an inlined call. For inlined calls topframe.Current.Fn is the function 459 // where the inlining happened and the second set of breakpoints will also 460 // cover the "return address". 461 // 462 // If inlinedStepOut is true this function implements the StepOut operation 463 // for an inlined function call. Everything works the same as normal except 464 // when removing instructions belonging to inlined calls we also remove all 465 // instructions belonging to the current inlined call. 466 func next(dbp *Target, stepInto, inlinedStepOut bool) error { 467 backward := dbp.GetDirection() == Backward 468 selg := dbp.SelectedGoroutine() 469 curthread := dbp.CurrentThread() 470 topframe, retframe, err := topframe(selg, curthread) 471 if err != nil { 472 return err 473 } 474 475 if topframe.Current.Fn == nil { 476 return &ErrNoSourceForPC{topframe.Current.PC} 477 } 478 479 if backward && retframe.Current.Fn == nil { 480 return &ErrNoSourceForPC{retframe.Current.PC} 481 } 482 483 // sanity check 484 if inlinedStepOut && !topframe.Inlined { 485 panic("next called with inlinedStepOut but topframe was not inlined") 486 } 487 488 success := false 489 defer func() { 490 if !success { 491 dbp.ClearSteppingBreakpoints() 492 } 493 }() 494 495 ext := filepath.Ext(topframe.Current.File) 496 csource := ext != ".go" && ext != ".s" 497 var regs Registers 498 if selg != nil && selg.Thread != nil { 499 regs, err = selg.Thread.Registers() 500 if err != nil { 501 return err 502 } 503 } 504 505 sameGCond := sameGoroutineCondition(selg) 506 507 var firstPCAfterPrologue uint64 508 509 if backward { 510 firstPCAfterPrologue, err = FirstPCAfterPrologue(dbp, topframe.Current.Fn, false) 511 if err != nil { 512 return err 513 } 514 if firstPCAfterPrologue == topframe.Current.PC { 515 // We don't want to step into the prologue so we just execute a reverse step out instead 516 if err := stepOutReverse(dbp, topframe, retframe, sameGCond); err != nil { 517 return err 518 } 519 520 success = true 521 return nil 522 } 523 524 topframe.Ret, err = findCallInstrForRet(dbp, dbp.Memory(), topframe.Ret, retframe.Current.Fn) 525 if err != nil { 526 return err 527 } 528 } 529 530 text, err := disassemble(dbp.Memory(), regs, dbp.Breakpoints(), dbp.BinInfo(), topframe.Current.Fn.Entry, topframe.Current.Fn.End, false) 531 if err != nil && stepInto { 532 return err 533 } 534 535 var sameFrameCond ast.Expr 536 if sameGCond != nil { 537 sameFrameCond = astutil.And(sameGCond, frameoffCondition(&topframe)) 538 } 539 540 if stepInto && !backward { 541 err := setStepIntoBreakpoints(dbp, topframe.Current.Fn, text, topframe, sameGCond) 542 if err != nil { 543 return err 544 } 545 } 546 547 if !backward { 548 _, err = setDeferBreakpoint(dbp, text, topframe, sameGCond, stepInto) 549 if err != nil { 550 return err 551 } 552 } 553 554 // Add breakpoints on all the lines in the current function 555 pcs, err := topframe.Current.Fn.cu.lineInfo.AllPCsBetween(topframe.Current.Fn.Entry, topframe.Current.Fn.End-1, topframe.Current.File, topframe.Current.Line) 556 if err != nil { 557 return err 558 } 559 560 if backward { 561 // Ensure that pcs contains firstPCAfterPrologue when reverse stepping. 562 found := false 563 for _, pc := range pcs { 564 if pc == firstPCAfterPrologue { 565 found = true 566 break 567 } 568 } 569 if !found { 570 pcs = append(pcs, firstPCAfterPrologue) 571 } 572 } 573 574 if !stepInto { 575 // Removing any PC range belonging to an inlined call 576 frame := topframe 577 if inlinedStepOut { 578 frame = retframe 579 } 580 pcs, err = removeInlinedCalls(pcs, frame) 581 if err != nil { 582 return err 583 } 584 } 585 586 if !csource { 587 var covered bool 588 for i := range pcs { 589 if topframe.Current.Fn.Entry <= pcs[i] && pcs[i] < topframe.Current.Fn.End { 590 covered = true 591 break 592 } 593 } 594 595 if !covered { 596 fn := dbp.BinInfo().PCToFunc(topframe.Ret) 597 if selg != nil && fn != nil && fn.Name == "runtime.goexit" { 598 return nil 599 } 600 } 601 } 602 603 for _, pc := range pcs { 604 if _, err := allowDuplicateBreakpoint(dbp.SetBreakpoint(0, pc, NextBreakpoint, sameFrameCond)); err != nil { 605 dbp.ClearSteppingBreakpoints() 606 return err 607 } 608 } 609 610 if stepInto && backward { 611 err := setStepIntoBreakpointsReverse(dbp, text, topframe, sameGCond) 612 if err != nil { 613 return err 614 } 615 } 616 617 if !topframe.Inlined { 618 topframe, retframe := skipAutogeneratedWrappersOut(selg, curthread, &topframe, &retframe) 619 retFrameCond := astutil.And(sameGCond, frameoffCondition(retframe)) 620 621 // Add a breakpoint on the return address for the current frame. 622 // For inlined functions there is no need to do this, the set of PCs 623 // returned by the AllPCsBetween call above already cover all instructions 624 // of the containing function. 625 bp, _ := dbp.SetBreakpoint(0, retframe.Current.PC, NextBreakpoint, retFrameCond) 626 // Return address could be wrong, if we are unable to set a breakpoint 627 // there it's ok. 628 if bp != nil { 629 configureReturnBreakpoint(dbp.BinInfo(), bp, topframe, retFrameCond) 630 } 631 } 632 633 if bp := curthread.Breakpoint(); bp.Breakpoint == nil { 634 curthread.SetCurrentBreakpoint(false) 635 } 636 success = true 637 return nil 638 } 639 640 func setStepIntoBreakpoints(dbp *Target, curfn *Function, text []AsmInstruction, topframe Stackframe, sameGCond ast.Expr) error { 641 for _, instr := range text { 642 if instr.Loc.File != topframe.Current.File || instr.Loc.Line != topframe.Current.Line || !instr.IsCall() { 643 continue 644 } 645 646 if instr.DestLoc != nil { 647 if err := setStepIntoBreakpoint(dbp, curfn, []AsmInstruction{instr}, sameGCond); err != nil { 648 return err 649 } 650 } else { 651 // Non-absolute call instruction, set a StepBreakpoint here 652 if _, err := allowDuplicateBreakpoint(dbp.SetBreakpoint(0, instr.Loc.PC, StepBreakpoint, sameGCond)); err != nil { 653 return err 654 } 655 } 656 } 657 return nil 658 } 659 660 func setStepIntoBreakpointsReverse(dbp *Target, text []AsmInstruction, topframe Stackframe, sameGCond ast.Expr) error { 661 bpmap := dbp.Breakpoints() 662 // Set a breakpoint after every CALL instruction 663 for i, instr := range text { 664 if instr.Loc.File != topframe.Current.File || !instr.IsCall() || instr.DestLoc == nil || instr.DestLoc.Fn == nil { 665 continue 666 } 667 668 if instr.DestLoc.Fn.privateRuntime() { 669 continue 670 } 671 672 if nextIdx := i + 1; nextIdx < len(text) { 673 _, ok := bpmap.M[text[nextIdx].Loc.PC] 674 if !ok { 675 if _, err := allowDuplicateBreakpoint(dbp.SetBreakpoint(0, text[nextIdx].Loc.PC, StepBreakpoint, sameGCond)); err != nil { 676 return err 677 } 678 } 679 } 680 } 681 return nil 682 } 683 684 func FindDeferReturnCalls(text []AsmInstruction) []uint64 { 685 const deferreturn = "runtime.deferreturn" 686 deferreturns := []uint64{} 687 688 // Find all runtime.deferreturn locations in the function 689 // See documentation of Breakpoint.DeferCond for why this is necessary 690 for _, instr := range text { 691 if instr.IsCall() && instr.DestLoc != nil && instr.DestLoc.Fn != nil && instr.DestLoc.Fn.Name == deferreturn { 692 deferreturns = append(deferreturns, instr.Loc.PC) 693 } 694 } 695 return deferreturns 696 } 697 698 // Removes instructions belonging to inlined calls of topframe from pcs. 699 // If includeCurrentFn is true it will also remove all instructions 700 // belonging to the current function. 701 func removeInlinedCalls(pcs []uint64, topframe Stackframe) ([]uint64, error) { 702 dwarfTree, err := topframe.Call.Fn.cu.image.getDwarfTree(topframe.Call.Fn.offset) 703 if err != nil { 704 return pcs, err 705 } 706 for _, e := range reader.InlineStack(dwarfTree, 0) { 707 if e.Offset == topframe.Call.Fn.offset { 708 continue 709 } 710 for _, rng := range e.Ranges { 711 pcs = removePCsBetween(pcs, rng[0], rng[1]) 712 } 713 } 714 return pcs, nil 715 } 716 717 func removePCsBetween(pcs []uint64, start, end uint64) []uint64 { 718 out := pcs[:0] 719 for _, pc := range pcs { 720 if pc < start || pc >= end { 721 out = append(out, pc) 722 } 723 } 724 return out 725 } 726 727 func setStepIntoBreakpoint(dbp *Target, curfn *Function, text []AsmInstruction, cond ast.Expr) error { 728 if len(text) <= 0 { 729 return nil 730 } 731 732 // If the current function is already a runtime function then 733 // setStepIntoBreakpoint is allowed to step into unexported runtime 734 // functions. 735 stepIntoUnexportedRuntime := curfn != nil && strings.HasPrefix(curfn.Name, "runtime.") 736 737 instr := text[0] 738 739 if instr.DestLoc == nil { 740 // Call destination couldn't be resolved because this was not the 741 // current instruction, therefore the step-into breakpoint can not be set. 742 return nil 743 } 744 745 fn := instr.DestLoc.Fn 746 747 // Skip unexported runtime functions 748 if !stepIntoUnexportedRuntime && fn != nil && fn.privateRuntime() { 749 return nil 750 } 751 752 //TODO(aarzilli): if we want to let users hide functions 753 // or entire packages from being stepped into with 'step' 754 // those extra checks should be done here. 755 756 pc := instr.DestLoc.PC 757 758 // Skip InhibitStepInto functions for different arch. 759 if dbp.BinInfo().Arch.inhibitStepInto(dbp.BinInfo(), pc) { 760 return nil 761 } 762 763 fn, pc = skipAutogeneratedWrappersIn(dbp, fn, pc) 764 765 // We want to skip the function prologue but we should only do it if the 766 // destination address of the CALL instruction is the entry point of the 767 // function. 768 // Calls to runtime.duffzero and duffcopy inserted by the compiler can 769 // sometimes point inside the body of those functions, well after the 770 // prologue. 771 if fn != nil && fn.Entry == pc { 772 pc, _ = FirstPCAfterPrologue(dbp, fn, false) 773 } 774 775 // Set a breakpoint after the function's prologue 776 if _, err := allowDuplicateBreakpoint(dbp.SetBreakpoint(0, pc, NextBreakpoint, cond)); err != nil { 777 return err 778 } 779 780 return nil 781 } 782 783 func allowDuplicateBreakpoint(bp *Breakpoint, err error) (*Breakpoint, error) { 784 if err != nil { 785 //lint:ignore S1020 this is clearer 786 if _, isexists := err.(BreakpointExistsError); isexists { 787 return bp, nil 788 } 789 } 790 return bp, err 791 } 792 793 func isAutogenerated(loc Location) bool { 794 return loc.File == "<autogenerated>" && loc.Line == 1 795 } 796 797 func isAutogeneratedOrDeferReturn(loc Location) bool { 798 return isAutogenerated(loc) || (loc.Fn != nil && loc.Fn.Name == "runtime.deferreturn") 799 } 800 801 // skipAutogeneratedWrappers skips autogenerated wrappers when setting a 802 // step-into breakpoint. 803 // See genwrapper in: $GOROOT/src/cmd/compile/internal/gc/subr.go 804 func skipAutogeneratedWrappersIn(p Process, startfn *Function, startpc uint64) (*Function, uint64) { 805 if startfn == nil { 806 return nil, startpc 807 } 808 fn := startfn 809 for count := 0; count < maxSkipAutogeneratedWrappers; count++ { 810 if !fn.cu.isgo { 811 // can't exit Go 812 return startfn, startpc 813 } 814 text, err := Disassemble(p.Memory(), nil, p.Breakpoints(), p.BinInfo(), fn.Entry, fn.End) 815 if err != nil { 816 break 817 } 818 if len(text) == 0 { 819 break 820 } 821 if !isAutogenerated(text[0].Loc) { 822 return fn, fn.Entry 823 } 824 tgtfns := []*Function{} 825 // collect all functions called by the current destination function 826 for _, instr := range text { 827 switch { 828 case instr.IsCall(): 829 if instr.DestLoc == nil { 830 return startfn, startpc 831 } 832 if p.BinInfo().Arch.inhibitStepInto(p.BinInfo(), instr.DestLoc.PC) { 833 // ignored 834 continue 835 } 836 if instr.DestLoc.Fn == nil { 837 return startfn, startpc 838 } 839 // calls to non private runtime functions 840 if !instr.DestLoc.Fn.privateRuntime() { 841 tgtfns = append(tgtfns, instr.DestLoc.Fn) 842 } 843 case instr.IsJmp(): 844 // unconditional jumps to a different function that isn't a private runtime function 845 if instr.DestLoc != nil && instr.DestLoc.Fn != fn && !instr.DestLoc.Fn.privateRuntime() { 846 tgtfns = append(tgtfns, instr.DestLoc.Fn) 847 } 848 } 849 } 850 if len(tgtfns) != 1 { 851 // too many or not enough function calls 852 break 853 } 854 855 tgtfn := tgtfns[0] 856 if strings.TrimSuffix(tgtfn.BaseName(), "-fm") != strings.TrimSuffix(fn.BaseName(), "-fm") { 857 return startfn, startpc 858 } 859 fn = tgtfn 860 } 861 return startfn, startpc 862 } 863 864 // skipAutogeneratedWrappersOut skip autogenerated wrappers when setting a 865 // step out breakpoint. 866 // See genwrapper in: $GOROOT/src/cmd/compile/internal/gc/subr.go 867 // It also skips runtime.deferreturn frames (which are only ever on the stack on Go 1.18 or later) 868 func skipAutogeneratedWrappersOut(g *G, thread Thread, startTopframe, startRetframe *Stackframe) (topframe, retframe *Stackframe) { 869 topframe, retframe = startTopframe, startRetframe 870 if startTopframe.Ret == 0 { 871 return 872 } 873 if !isAutogeneratedOrDeferReturn(startRetframe.Current) { 874 return 875 } 876 retfn := thread.BinInfo().PCToFunc(startTopframe.Ret) 877 if retfn == nil { 878 return 879 } 880 if !retfn.cu.isgo { 881 return 882 } 883 var err error 884 var frames []Stackframe 885 if g == nil { 886 frames, err = ThreadStacktrace(thread, maxSkipAutogeneratedWrappers) 887 } else { 888 frames, err = g.Stacktrace(maxSkipAutogeneratedWrappers, 0) 889 } 890 if err != nil { 891 return 892 } 893 for i := 1; i < len(frames); i++ { 894 frame := frames[i] 895 if frame.Current.Fn == nil { 896 return 897 } 898 file, line := frame.Current.Fn.cu.lineInfo.PCToLine(frame.Current.Fn.Entry, frame.Current.Fn.Entry) 899 if !isAutogeneratedOrDeferReturn(Location{File: file, Line: line, Fn: frame.Current.Fn}) { 900 return &frames[i-1], &frames[i] 901 } 902 } 903 return 904 } 905 906 // setDeferBreakpoint is a helper function used by next and StepOut to set a 907 // breakpoint on the first deferred function. 908 func setDeferBreakpoint(p *Target, text []AsmInstruction, topframe Stackframe, sameGCond ast.Expr, stepInto bool) (uint64, error) { 909 // Set breakpoint on the most recently deferred function (if any) 910 var deferpc uint64 911 if topframe.TopmostDefer != nil && topframe.TopmostDefer.DwrapPC != 0 { 912 _, _, deferfn := topframe.TopmostDefer.DeferredFunc(p) 913 if deferfn != nil { 914 var err error 915 deferpc, err = FirstPCAfterPrologue(p, deferfn, false) 916 if err != nil { 917 return 0, err 918 } 919 } 920 } 921 if deferpc != 0 && deferpc != topframe.Current.PC { 922 bp, err := allowDuplicateBreakpoint(p.SetBreakpoint(0, deferpc, NextDeferBreakpoint, sameGCond)) 923 if err != nil { 924 return 0, err 925 } 926 if bp != nil && stepInto { 927 // If DeferReturns is set then the breakpoint will also be triggered when 928 // called from runtime.deferreturn. We only do this for the step command, 929 // not for next or stepout. 930 for _, breaklet := range bp.Breaklets { 931 if breaklet.Kind == NextDeferBreakpoint { 932 breaklet.DeferReturns = FindDeferReturnCalls(text) 933 break 934 } 935 } 936 } 937 } 938 939 return deferpc, nil 940 } 941 942 // findCallInstrForRet returns the PC address of the CALL instruction 943 // immediately preceding the instruction at ret. 944 func findCallInstrForRet(p Process, mem MemoryReadWriter, ret uint64, fn *Function) (uint64, error) { 945 text, err := disassemble(mem, nil, p.Breakpoints(), p.BinInfo(), fn.Entry, fn.End, false) 946 if err != nil { 947 return 0, err 948 } 949 var prevInstr AsmInstruction 950 for _, instr := range text { 951 if instr.Loc.PC == ret { 952 return prevInstr.Loc.PC, nil 953 } 954 prevInstr = instr 955 } 956 return 0, fmt.Errorf("could not find CALL instruction for address %#x in %s", ret, fn.Name) 957 } 958 959 // stepOutReverse sets a breakpoint on the CALL instruction that created the current frame, this is either: 960 // - the CALL instruction immediately preceding the return address of the 961 // current frame 962 // - the return address of the current frame if the current frame was 963 // created by a runtime.deferreturn run 964 // - the return address of the runtime.gopanic frame if the current frame 965 // was created by a panic 966 // 967 // This function is used to implement reversed StepOut 968 func stepOutReverse(p *Target, topframe, retframe Stackframe, sameGCond ast.Expr) error { 969 curthread := p.CurrentThread() 970 selg := p.SelectedGoroutine() 971 972 if selg != nil && selg.Thread != nil { 973 curthread = selg.Thread 974 } 975 976 callerText, err := disassemble(p.Memory(), nil, p.Breakpoints(), p.BinInfo(), retframe.Current.Fn.Entry, retframe.Current.Fn.End, false) 977 if err != nil { 978 return err 979 } 980 deferReturns := FindDeferReturnCalls(callerText) 981 982 var frames []Stackframe 983 if selg == nil { 984 frames, err = ThreadStacktrace(curthread, 3) 985 } else { 986 frames, err = selg.Stacktrace(3, 0) 987 } 988 if err != nil { 989 return err 990 } 991 992 var callpc uint64 993 994 if ok, panicFrame := isPanicCall(frames); ok { 995 if len(frames) < panicFrame+2 || frames[panicFrame+1].Current.Fn == nil { 996 if panicFrame < len(frames) { 997 return &ErrNoSourceForPC{frames[panicFrame].Current.PC} 998 } else { 999 return &ErrNoSourceForPC{frames[0].Current.PC} 1000 } 1001 } 1002 callpc, err = findCallInstrForRet(p, p.Memory(), frames[panicFrame].Ret, frames[panicFrame+1].Current.Fn) 1003 if err != nil { 1004 return err 1005 } 1006 } else { 1007 callpc, err = findCallInstrForRet(p, p.Memory(), topframe.Ret, retframe.Current.Fn) 1008 if err != nil { 1009 return err 1010 } 1011 1012 // check if the call instruction to this frame is a call to runtime.deferreturn 1013 if len(frames) > 0 { 1014 frames[0].Ret = callpc 1015 } 1016 if ok, pc := isDeferReturnCall(frames, deferReturns); ok && pc != 0 { 1017 callpc = pc 1018 } 1019 1020 } 1021 1022 _, err = allowDuplicateBreakpoint(p.SetBreakpoint(0, callpc, NextBreakpoint, sameGCond)) 1023 1024 return err 1025 } 1026 1027 // onNextGoroutine returns true if this thread is on the goroutine requested by the current 'next' command 1028 func onNextGoroutine(tgt *Target, thread Thread, breakpoints *BreakpointMap) (bool, error) { 1029 var breaklet *Breaklet 1030 breakletSearch: 1031 for i := range breakpoints.M { 1032 for _, blet := range breakpoints.M[i].Breaklets { 1033 if blet.Kind&steppingMask != 0 && blet.Cond != nil { 1034 breaklet = blet 1035 break breakletSearch 1036 } 1037 } 1038 } 1039 if breaklet == nil { 1040 return false, nil 1041 } 1042 // Internal breakpoint conditions can take multiple different forms: 1043 // Step into breakpoints: 1044 // runtime.curg.goid == X 1045 // Next or StepOut breakpoints: 1046 // runtime.curg.goid == X && runtime.frameoff == Y 1047 // Breakpoints that can be hit either by stepping on a line in the same 1048 // function or by returning from the function: 1049 // runtime.curg.goid == X && (runtime.frameoff == Y || runtime.frameoff == Z) 1050 // Here we are only interested in testing the runtime.curg.goid clause. 1051 w := onNextGoroutineWalker{tgt: tgt, thread: thread} 1052 ast.Walk(&w, breaklet.Cond) 1053 return w.ret, w.err 1054 } 1055 1056 type onNextGoroutineWalker struct { 1057 tgt *Target 1058 thread Thread 1059 ret bool 1060 err error 1061 } 1062 1063 func (w *onNextGoroutineWalker) Visit(n ast.Node) ast.Visitor { 1064 if binx, isbin := n.(*ast.BinaryExpr); isbin && binx.Op == token.EQL && exprToString(binx.X) == "runtime.curg.goid" { 1065 w.ret, w.err = evalBreakpointCondition(w.tgt, w.thread, n.(ast.Expr)) 1066 return nil 1067 } 1068 return w 1069 } 1070 1071 func (tgt *Target) clearHardcodedBreakpoints() { 1072 threads := tgt.ThreadList() 1073 for _, thread := range threads { 1074 if thread.Breakpoint().Breakpoint != nil && thread.Breakpoint().LogicalID() == hardcodedBreakpointID { 1075 thread.Breakpoint().Active = false 1076 thread.Breakpoint().Breakpoint = nil 1077 } 1078 } 1079 } 1080 1081 // handleHardcodedBreakpoints looks for threads stopped at a hardcoded 1082 // breakpoint (i.e. a breakpoint instruction, like INT 3, hardcoded in the 1083 // program's text) and sets a fake breakpoint on them with logical id 1084 // hardcodedBreakpointID. 1085 // It checks trapthread and all threads that have SoftExc returning true. 1086 func (tgt *Target) handleHardcodedBreakpoints(trapthread Thread, threads []Thread) error { 1087 mem := tgt.Memory() 1088 arch := tgt.BinInfo().Arch 1089 recorded, _ := tgt.Recorded() 1090 1091 isHardcodedBreakpoint := func(thread Thread, pc uint64) uint64 { 1092 for _, bpinstr := range [][]byte{arch.BreakpointInstruction(), arch.AltBreakpointInstruction()} { 1093 if bpinstr == nil { 1094 continue 1095 } 1096 buf := make([]byte, len(bpinstr)) 1097 pc2 := pc 1098 if arch.BreakInstrMovesPC() { 1099 pc2 -= uint64(len(bpinstr)) 1100 } 1101 _, _ = mem.ReadMemory(buf, pc2) 1102 if bytes.Equal(buf, bpinstr) { 1103 return uint64(len(bpinstr)) 1104 } 1105 } 1106 return 0 1107 } 1108 1109 stepOverBreak := func(thread Thread, pc uint64) { 1110 if arch.BreakInstrMovesPC() { 1111 return 1112 } 1113 if recorded { 1114 return 1115 } 1116 if bpsize := isHardcodedBreakpoint(thread, pc); bpsize > 0 { 1117 setPC(thread, pc+uint64(bpsize)) 1118 } 1119 } 1120 1121 setHardcodedBreakpoint := func(thread Thread, loc *Location) { 1122 bpstate := thread.Breakpoint() 1123 hcbp := &Breakpoint{} 1124 bpstate.Active = true 1125 bpstate.Breakpoint = hcbp 1126 hcbp.FunctionName = loc.Fn.Name 1127 hcbp.File = loc.File 1128 hcbp.Line = loc.Line 1129 hcbp.Addr = loc.PC 1130 hcbp.Logical = &LogicalBreakpoint{} 1131 hcbp.Logical.Name = HardcodedBreakpoint 1132 hcbp.Breaklets = []*Breaklet{&Breaklet{Kind: UserBreakpoint, LogicalID: hardcodedBreakpointID}} 1133 tgt.StopReason = StopHardcodedBreakpoint 1134 } 1135 1136 for _, thread := range threads { 1137 if thread.Breakpoint().Breakpoint != nil { 1138 continue 1139 } 1140 if (thread.ThreadID() != trapthread.ThreadID()) && !thread.SoftExc() { 1141 continue 1142 } 1143 1144 loc, err := thread.Location() 1145 if err != nil || loc.Fn == nil { 1146 continue 1147 } 1148 1149 g, _ := GetG(thread) 1150 1151 switch { 1152 case loc.Fn.Name == "runtime.breakpoint": 1153 if recorded, _ := tgt.Recorded(); recorded { 1154 setHardcodedBreakpoint(thread, loc) 1155 continue 1156 } 1157 stepOverBreak(thread, loc.PC) 1158 // In linux-arm64, PtraceSingleStep seems cannot step over BRK instruction 1159 // (linux-arm64 feature or kernel bug maybe). 1160 if !arch.BreakInstrMovesPC() { 1161 setPC(thread, loc.PC+uint64(arch.BreakpointSize())) 1162 } 1163 // Single-step current thread until we exit runtime.breakpoint and 1164 // runtime.Breakpoint. 1165 // On go < 1.8 it was sufficient to single-step twice on go1.8 a change 1166 // to the compiler requires 4 steps. 1167 if err := stepInstructionOut(tgt, thread, "runtime.breakpoint", "runtime.Breakpoint"); err != nil { 1168 return err 1169 } 1170 setHardcodedBreakpoint(thread, loc) 1171 case g == nil || tgt.fncallForG[g.ID] == nil: 1172 if isHardcodedBreakpoint(thread, loc.PC) > 0 { 1173 stepOverBreak(thread, loc.PC) 1174 setHardcodedBreakpoint(thread, loc) 1175 } 1176 } 1177 } 1178 return nil 1179 }