github.com/cnboonhan/delve@v0.0.0-20230908061759-363f2388c2fb/pkg/proc/target.go (about) 1 package proc 2 3 import ( 4 "errors" 5 "fmt" 6 "go/constant" 7 "os" 8 "sort" 9 "strings" 10 11 "github.com/go-delve/delve/pkg/dwarf/op" 12 "github.com/go-delve/delve/pkg/goversion" 13 "github.com/go-delve/delve/pkg/logflags" 14 "github.com/go-delve/delve/pkg/proc/internal/ebpf" 15 ) 16 17 var ( 18 // ErrNotRecorded is returned when an action is requested that is 19 // only possible on recorded (traced) programs. 20 ErrNotRecorded = errors.New("not a recording") 21 22 // ErrNoRuntimeAllG is returned when the runtime.allg list could 23 // not be found. 24 ErrNoRuntimeAllG = errors.New("could not find goroutine array") 25 26 // ErrProcessDetached indicates that we detached from the target process. 27 ErrProcessDetached = errors.New("detached from the process") 28 ) 29 30 type LaunchFlags uint8 31 32 const ( 33 LaunchForeground LaunchFlags = 1 << iota 34 LaunchDisableASLR 35 ) 36 37 // Target represents the process being debugged. 38 type Target struct { 39 Process 40 41 proc ProcessInternal 42 recman RecordingManipulationInternal 43 44 pid int 45 CmdLine string 46 47 // StopReason describes the reason why the target process is stopped. 48 // A process could be stopped for multiple simultaneous reasons, in which 49 // case only one will be reported. 50 StopReason StopReason 51 52 // currentThread is the thread that will be used by next/step/stepout and to evaluate variables if no goroutine is selected. 53 currentThread Thread 54 55 // Goroutine that will be used by default to set breakpoint, eval variables, etc... 56 // Normally selectedGoroutine is currentThread.GetG, it will not be only if SwitchGoroutine is called with a goroutine that isn't attached to a thread 57 selectedGoroutine *G 58 59 // fncallForG stores a mapping of current active function calls. 60 fncallForG map[int64]*callInjection 61 62 asyncPreemptChanged bool // runtime/debug.asyncpreemptoff was changed 63 asyncPreemptOff int64 // cached value of runtime/debug.asyncpreemptoff 64 65 // gcache is a cache for Goroutines that we 66 // have read and parsed from the targets memory. 67 // This must be cleared whenever the target is resumed. 68 gcache goroutineCache 69 iscgo *bool 70 71 // exitStatus is the exit status of the process we are debugging. 72 // Saved here to relay to any future commands. 73 exitStatus int 74 75 // fakeMemoryRegistry contains the list of all compositeMemory objects 76 // created since the last restart, it exists so that registerized variables 77 // can be given a unique address. 78 fakeMemoryRegistry []*compositeMemory 79 fakeMemoryRegistryMap map[string]*compositeMemory 80 81 partOfGroup bool 82 } 83 84 type KeepSteppingBreakpoints uint8 85 86 const ( 87 HaltKeepsSteppingBreakpoints KeepSteppingBreakpoints = 1 << iota 88 TracepointKeepsSteppingBreakpoints 89 ) 90 91 // ErrProcessExited indicates that the process has exited and contains both 92 // process id and exit status. 93 type ErrProcessExited struct { 94 Pid int 95 Status int 96 } 97 98 func (pe ErrProcessExited) Error() string { 99 return fmt.Sprintf("Process %d has exited with status %d", pe.Pid, pe.Status) 100 } 101 102 // StopReason describes the reason why the target process is stopped. 103 // A process could be stopped for multiple simultaneous reasons, in which 104 // case only one will be reported. 105 type StopReason uint8 106 107 // String maps StopReason to string representation. 108 func (sr StopReason) String() string { 109 switch sr { 110 case StopUnknown: 111 return "unknown" 112 case StopLaunched: 113 return "launched" 114 case StopAttached: 115 return "attached" 116 case StopExited: 117 return "exited" 118 case StopBreakpoint: 119 return "breakpoint" 120 case StopHardcodedBreakpoint: 121 return "hardcoded breakpoint" 122 case StopManual: 123 return "manual" 124 case StopNextFinished: 125 return "next finished" 126 case StopCallReturned: 127 return "call returned" 128 case StopWatchpoint: 129 return "watchpoint" 130 default: 131 return "" 132 } 133 } 134 135 const ( 136 StopUnknown StopReason = iota 137 StopLaunched // The process was just launched 138 StopAttached // The debugger stopped the process after attaching 139 StopExited // The target process terminated 140 StopBreakpoint // The target process hit one or more software breakpoints 141 StopHardcodedBreakpoint // The target process hit a hardcoded breakpoint (for example runtime.Breakpoint()) 142 StopManual // A manual stop was requested 143 StopNextFinished // The next/step/stepout/stepInstruction command terminated 144 StopCallReturned // An injected call completed 145 StopWatchpoint // The target process hit one or more watchpoints 146 ) 147 148 // DisableAsyncPreemptEnv returns a process environment (like os.Environ) 149 // where asyncpreemptoff is set to 1. 150 func DisableAsyncPreemptEnv() []string { 151 env := os.Environ() 152 for i := range env { 153 if strings.HasPrefix(env[i], "GODEBUG=") { 154 // Go 1.14 asynchronous preemption mechanism is incompatible with 155 // debuggers, see: https://github.com/golang/go/issues/36494 156 env[i] += ",asyncpreemptoff=1" 157 } 158 } 159 return env 160 } 161 162 // newTarget returns an initialized Target object. 163 // The p argument can optionally implement the RecordingManipulation interface. 164 func (grp *TargetGroup) newTarget(p ProcessInternal, pid int, currentThread Thread, path, cmdline string) (*Target, error) { 165 entryPoint, err := p.EntryPoint() 166 if err != nil { 167 return nil, err 168 } 169 170 err = p.BinInfo().LoadBinaryInfo(path, entryPoint, grp.cfg.DebugInfoDirs) 171 if err != nil { 172 return nil, err 173 } 174 for _, image := range p.BinInfo().Images { 175 if image.loadErr != nil { 176 return nil, image.loadErr 177 } 178 } 179 180 t := &Target{ 181 Process: p, 182 proc: p, 183 fncallForG: make(map[int64]*callInjection), 184 currentThread: currentThread, 185 pid: pid, 186 CmdLine: cmdline, 187 } 188 189 if recman, ok := p.(RecordingManipulationInternal); ok { 190 t.recman = recman 191 } else { 192 t.recman = &dummyRecordingManipulation{} 193 } 194 195 g, _ := GetG(currentThread) 196 t.selectedGoroutine = g 197 198 t.Breakpoints().Logical = grp.LogicalBreakpoints 199 t.createUnrecoveredPanicBreakpoint() 200 t.createFatalThrowBreakpoint() 201 t.createPluginOpenBreakpoint() 202 203 t.gcache.init(p.BinInfo()) 204 t.fakeMemoryRegistryMap = make(map[string]*compositeMemory) 205 206 if grp.cfg.DisableAsyncPreempt { 207 setAsyncPreemptOff(t, 1) 208 } 209 210 return t, nil 211 } 212 213 // Pid returns the pid of the target process. 214 func (t *Target) Pid() int { 215 return t.pid 216 } 217 218 // IsCgo returns the value of runtime.iscgo 219 func (t *Target) IsCgo() bool { 220 if t.iscgo != nil { 221 return *t.iscgo 222 } 223 scope := globalScope(t, t.BinInfo(), t.BinInfo().Images[0], t.Memory()) 224 iscgov, err := scope.findGlobal("runtime", "iscgo") 225 if err == nil { 226 iscgov.loadValue(loadFullValue) 227 if iscgov.Unreadable == nil { 228 t.iscgo = new(bool) 229 *t.iscgo = constant.BoolVal(iscgov.Value) 230 return constant.BoolVal(iscgov.Value) 231 } 232 } 233 return false 234 } 235 236 // Valid returns true if this Process can be used. When it returns false it 237 // also returns an error describing why the Process is invalid (either 238 // ErrProcessExited or ErrProcessDetached). 239 func (t *Target) Valid() (bool, error) { 240 ok, err := t.proc.Valid() 241 if !ok && err != nil { 242 if pe, ok := err.(ErrProcessExited); ok { 243 pe.Status = t.exitStatus 244 err = pe 245 } 246 } 247 return ok, err 248 } 249 250 // SupportsFunctionCalls returns whether or not the backend supports 251 // calling functions during a debug session. 252 // Currently only non-recorded processes running on AMD64 support 253 // function calls. 254 func (t *Target) SupportsFunctionCalls() bool { 255 return t.Process.BinInfo().Arch.Name == "amd64" || (t.Process.BinInfo().Arch.Name == "arm64" && t.Process.BinInfo().GOOS != "windows") 256 } 257 258 // ClearCaches clears internal caches that should not survive a restart. 259 // This should be called anytime the target process executes instructions. 260 func (t *Target) ClearCaches() { 261 t.clearFakeMemory() 262 t.gcache.Clear() 263 for _, thread := range t.ThreadList() { 264 thread.Common().g = nil 265 } 266 } 267 268 // Restart will start the process group over from the location specified by the "from" locspec. 269 // This is only useful for recorded targets. 270 // Restarting of a normal process happens at a higher level (debugger.Restart). 271 func (grp *TargetGroup) Restart(from string) error { 272 if len(grp.targets) != 1 { 273 panic("multiple targets not implemented") 274 } 275 for _, t := range grp.targets { 276 t.ClearCaches() 277 } 278 t := grp.Selected 279 currentThread, err := t.recman.Restart(grp.cctx, from) 280 if err != nil { 281 return err 282 } 283 t.currentThread = currentThread 284 t.selectedGoroutine, _ = GetG(t.CurrentThread()) 285 if from != "" { 286 t.StopReason = StopManual 287 } else { 288 t.StopReason = StopLaunched 289 } 290 return nil 291 } 292 293 // SelectedGoroutine returns the currently selected goroutine. 294 func (t *Target) SelectedGoroutine() *G { 295 return t.selectedGoroutine 296 } 297 298 // SwitchGoroutine will change the selected and active goroutine. 299 func (t *Target) SwitchGoroutine(g *G) error { 300 if ok, err := t.Valid(); !ok { 301 return err 302 } 303 if g == nil { 304 return nil 305 } 306 if g.Thread != nil { 307 return t.SwitchThread(g.Thread.ThreadID()) 308 } 309 t.selectedGoroutine = g 310 return nil 311 } 312 313 // SwitchThread will change the selected and active thread. 314 func (t *Target) SwitchThread(tid int) error { 315 if ok, err := t.Valid(); !ok { 316 return err 317 } 318 if th, ok := t.FindThread(tid); ok { 319 t.currentThread = th 320 t.selectedGoroutine, _ = GetG(t.CurrentThread()) 321 return nil 322 } 323 return fmt.Errorf("thread %d does not exist", tid) 324 } 325 326 // setAsyncPreemptOff enables or disables async goroutine preemption by 327 // writing the value 'v' to runtime.debug.asyncpreemptoff. 328 // A value of '1' means off, a value of '0' means on. 329 func setAsyncPreemptOff(p *Target, v int64) { 330 if producer := p.BinInfo().Producer(); producer == "" || !goversion.ProducerAfterOrEqual(producer, 1, 14) { 331 return 332 } 333 logger := p.BinInfo().logger 334 scope := globalScope(p, p.BinInfo(), p.BinInfo().Images[0], p.Memory()) 335 // +rtype -var debug anytype 336 debugv, err := scope.findGlobal("runtime", "debug") 337 if err != nil { 338 logger.Warnf("could not find runtime/debug variable (or unreadable): %v", err) 339 return 340 } 341 if debugv.Unreadable != nil { 342 logger.Warnf("runtime/debug variable unreadable: %v", err, debugv.Unreadable) 343 return 344 } 345 asyncpreemptoffv, err := debugv.structMember("asyncpreemptoff") // +rtype int32 346 if err != nil { 347 logger.Warnf("could not find asyncpreemptoff field: %v", err) 348 return 349 } 350 asyncpreemptoffv.loadValue(loadFullValue) 351 if asyncpreemptoffv.Unreadable != nil { 352 logger.Warnf("asyncpreemptoff field unreadable: %v", asyncpreemptoffv.Unreadable) 353 return 354 } 355 p.asyncPreemptChanged = true 356 p.asyncPreemptOff, _ = constant.Int64Val(asyncpreemptoffv.Value) 357 358 err = scope.setValue(asyncpreemptoffv, newConstant(constant.MakeInt64(v), scope.Mem), "") 359 if err != nil { 360 logger.Warnf("could not set asyncpreemptoff %v", err) 361 } 362 } 363 364 // createUnrecoveredPanicBreakpoint creates the unrecoverable-panic breakpoint. 365 func (t *Target) createUnrecoveredPanicBreakpoint() { 366 panicpcs, err := FindFunctionLocation(t.Process, "runtime.startpanic", 0) 367 if _, isFnNotFound := err.(*ErrFunctionNotFound); isFnNotFound { 368 panicpcs, err = FindFunctionLocation(t.Process, "runtime.fatalpanic", 0) 369 } 370 if err == nil { 371 bp, err := t.SetBreakpoint(unrecoveredPanicID, panicpcs[0], UserBreakpoint, nil) 372 if err == nil { 373 bp.Logical.Name = UnrecoveredPanic 374 bp.Logical.Variables = []string{"runtime.curg._panic.arg"} 375 } 376 } 377 } 378 379 // createFatalThrowBreakpoint creates the a breakpoint as runtime.fatalthrow. 380 func (t *Target) createFatalThrowBreakpoint() { 381 setFatalThrow := func(pcs []uint64, err error) { 382 if err == nil { 383 bp, err := t.SetBreakpoint(fatalThrowID, pcs[0], UserBreakpoint, nil) 384 if err == nil { 385 bp.Logical.Name = FatalThrow 386 } 387 } 388 } 389 setFatalThrow(FindFunctionLocation(t.Process, "runtime.throw", 0)) 390 setFatalThrow(FindFunctionLocation(t.Process, "runtime.fatal", 0)) 391 setFatalThrow(FindFunctionLocation(t.Process, "runtime.winthrow", 0)) 392 setFatalThrow(FindFunctionLocation(t.Process, "runtime.fatalsignal", 0)) 393 } 394 395 // createPluginOpenBreakpoint creates a breakpoint at the return instruction 396 // of plugin.Open (if it exists) that will try to enable suspended 397 // breakpoints. 398 func (t *Target) createPluginOpenBreakpoint() { 399 retpcs, _ := findRetPC(t, "plugin.Open") 400 for _, retpc := range retpcs { 401 bp, err := t.SetBreakpoint(0, retpc, PluginOpenBreakpoint, nil) 402 if err != nil { 403 t.BinInfo().logger.Errorf("could not set plugin.Open breakpoint: %v", err) 404 } else { 405 bp.Breaklets[len(bp.Breaklets)-1].callback = t.pluginOpenCallback 406 } 407 } 408 } 409 410 // CurrentThread returns the currently selected thread which will be used 411 // for next/step/stepout and for reading variables, unless a goroutine is 412 // selected. 413 func (t *Target) CurrentThread() Thread { 414 return t.currentThread 415 } 416 417 type UProbeTraceResult struct { 418 FnAddr int 419 GoroutineID int 420 IsRet bool 421 InputParams []*Variable 422 ReturnParams []*Variable 423 } 424 425 func (t *Target) GetBufferedTracepoints() []*UProbeTraceResult { 426 var results []*UProbeTraceResult 427 tracepoints := t.proc.GetBufferedTracepoints() 428 convertInputParamToVariable := func(ip *ebpf.RawUProbeParam) *Variable { 429 v := &Variable{} 430 v.RealType = ip.RealType 431 v.Len = ip.Len 432 v.Base = ip.Base 433 v.Addr = ip.Addr 434 v.Kind = ip.Kind 435 436 if v.RealType == nil { 437 v.Unreadable = errors.New("type not supported by ebpf") 438 return v 439 } 440 441 cachedMem := CreateLoadedCachedMemory(ip.Data) 442 compMem, _ := CreateCompositeMemory(cachedMem, t.BinInfo().Arch, op.DwarfRegisters{}, ip.Pieces, ip.RealType.Common().ByteSize) 443 v.mem = compMem 444 445 // Load the value here so that we don't have to export 446 // loadValue outside of proc. 447 v.loadValue(loadFullValue) 448 449 return v 450 } 451 for _, tp := range tracepoints { 452 r := &UProbeTraceResult{} 453 r.FnAddr = tp.FnAddr 454 r.GoroutineID = tp.GoroutineID 455 r.IsRet = tp.IsRet 456 for _, ip := range tp.InputParams { 457 v := convertInputParamToVariable(ip) 458 r.InputParams = append(r.InputParams, v) 459 } 460 for _, ip := range tp.ReturnParams { 461 v := convertInputParamToVariable(ip) 462 r.ReturnParams = append(r.ReturnParams, v) 463 } 464 results = append(results, r) 465 } 466 return results 467 } 468 469 // ResumeNotify specifies a channel that will be closed the next time 470 // Continue finishes resuming the targets. 471 func (grp *TargetGroup) ResumeNotify(ch chan<- struct{}) { 472 grp.cctx.ResumeChan = ch 473 } 474 475 // RequestManualStop attempts to stop all the processes' threads. 476 func (grp *TargetGroup) RequestManualStop() error { 477 grp.cctx.StopMu.Lock() 478 defer grp.cctx.StopMu.Unlock() 479 grp.cctx.manualStopRequested = true 480 return grp.Selected.proc.RequestManualStop(grp.cctx) 481 } 482 483 const ( 484 FakeAddressBase = 0xbeef000000000000 485 fakeAddressUnresolv = 0xbeed000000000000 // this address never resolves to memory 486 ) 487 488 // newCompositeMemory creates a new compositeMemory object and registers it. 489 // If the same composite memory has been created before it will return a 490 // cached object. 491 // This caching is primarily done so that registerized variables don't get a 492 // different address every time they are evaluated, which would be confusing 493 // and leak memory. 494 func (t *Target) newCompositeMemory(mem MemoryReadWriter, regs op.DwarfRegisters, pieces []op.Piece, descr *locationExpr, size int64) (int64, *compositeMemory, error) { 495 var key string 496 if regs.CFA != 0 && len(pieces) > 0 { 497 // key is created by concatenating the location expression with the CFA, 498 // this combination is guaranteed to be unique between resumes. 499 buf := new(strings.Builder) 500 fmt.Fprintf(buf, "%#x ", regs.CFA) 501 op.PrettyPrint(buf, descr.instr, t.BinInfo().Arch.RegnumToString) 502 key = buf.String() 503 504 if cmem := t.fakeMemoryRegistryMap[key]; cmem != nil { 505 return int64(cmem.base), cmem, nil 506 } 507 } 508 509 cmem, err := newCompositeMemory(mem, t.BinInfo().Arch, regs, pieces, size) 510 if err != nil { 511 return 0, cmem, err 512 } 513 t.registerFakeMemory(cmem) 514 if key != "" { 515 t.fakeMemoryRegistryMap[key] = cmem 516 } 517 return int64(cmem.base), cmem, nil 518 } 519 520 func (t *Target) registerFakeMemory(mem *compositeMemory) (addr uint64) { 521 t.fakeMemoryRegistry = append(t.fakeMemoryRegistry, mem) 522 addr = FakeAddressBase 523 if len(t.fakeMemoryRegistry) > 1 { 524 prevMem := t.fakeMemoryRegistry[len(t.fakeMemoryRegistry)-2] 525 addr = uint64(alignAddr(int64(prevMem.base+uint64(len(prevMem.data))), 0x100)) // the call to alignAddr just makes the address look nicer, it is not necessary 526 } 527 mem.base = addr 528 return addr 529 } 530 531 func (t *Target) findFakeMemory(addr uint64) *compositeMemory { 532 i := sort.Search(len(t.fakeMemoryRegistry), func(i int) bool { 533 mem := t.fakeMemoryRegistry[i] 534 return addr <= mem.base || (mem.base <= addr && addr < (mem.base+uint64(len(mem.data)))) 535 }) 536 if i != len(t.fakeMemoryRegistry) { 537 mem := t.fakeMemoryRegistry[i] 538 if mem.base <= addr && addr < (mem.base+uint64(len(mem.data))) { 539 return mem 540 } 541 } 542 return nil 543 } 544 545 func (t *Target) clearFakeMemory() { 546 for i := range t.fakeMemoryRegistry { 547 t.fakeMemoryRegistry[i] = nil 548 } 549 t.fakeMemoryRegistry = t.fakeMemoryRegistry[:0] 550 t.fakeMemoryRegistryMap = make(map[string]*compositeMemory) 551 } 552 553 // dwrapUnwrap checks if fn is a dwrap wrapper function and unwraps it if it is. 554 func (t *Target) dwrapUnwrap(fn *Function) *Function { 555 if fn == nil { 556 return nil 557 } 558 if !strings.Contains(fn.Name, "·dwrap·") && !fn.trampoline { 559 return fn 560 } 561 if unwrap := t.BinInfo().dwrapUnwrapCache[fn.Entry]; unwrap != nil { 562 return unwrap 563 } 564 text, err := disassemble(t.Memory(), nil, t.Breakpoints(), t.BinInfo(), fn.Entry, fn.End, false) 565 if err != nil { 566 return fn 567 } 568 for _, instr := range text { 569 if instr.IsCall() && instr.DestLoc != nil && instr.DestLoc.Fn != nil && !instr.DestLoc.Fn.privateRuntime() { 570 t.BinInfo().dwrapUnwrapCache[fn.Entry] = instr.DestLoc.Fn 571 return instr.DestLoc.Fn 572 } 573 } 574 return fn 575 } 576 577 func (t *Target) pluginOpenCallback(Thread, *Target) (bool, error) { 578 logger := logflags.DebuggerLogger() 579 for _, lbp := range t.Breakpoints().Logical { 580 if isSuspended(t, lbp) { 581 err := enableBreakpointOnTarget(t, lbp) 582 if err != nil { 583 logger.Debugf("could not enable breakpoint %d: %v", lbp.LogicalID, err) 584 } else { 585 logger.Debugf("suspended breakpoint %d enabled", lbp.LogicalID) 586 } 587 } 588 } 589 return false, nil 590 } 591 592 func isSuspended(t *Target, lbp *LogicalBreakpoint) bool { 593 for _, bp := range t.Breakpoints().M { 594 if bp.LogicalID() == lbp.LogicalID { 595 return false 596 } 597 } 598 return true 599 } 600 601 type dummyRecordingManipulation struct { 602 } 603 604 // Recorded always returns false for the native proc backend. 605 func (*dummyRecordingManipulation) Recorded() (bool, string) { return false, "" } 606 607 // ChangeDirection will always return an error in the native proc backend, only for 608 // recorded traces. 609 func (*dummyRecordingManipulation) ChangeDirection(dir Direction) error { 610 if dir != Forward { 611 return ErrNotRecorded 612 } 613 return nil 614 } 615 616 // GetDirection will always return Forward. 617 func (*dummyRecordingManipulation) GetDirection() Direction { return Forward } 618 619 // When will always return an empty string and nil, not supported on native proc backend. 620 func (*dummyRecordingManipulation) When() (string, error) { return "", nil } 621 622 // Checkpoint will always return an error on the native proc backend, 623 // only supported for recorded traces. 624 func (*dummyRecordingManipulation) Checkpoint(string) (int, error) { return -1, ErrNotRecorded } 625 626 // Checkpoints will always return an error on the native proc backend, 627 // only supported for recorded traces. 628 func (*dummyRecordingManipulation) Checkpoints() ([]Checkpoint, error) { return nil, ErrNotRecorded } 629 630 // ClearCheckpoint will always return an error on the native proc backend, 631 // only supported in recorded traces. 632 func (*dummyRecordingManipulation) ClearCheckpoint(int) error { return ErrNotRecorded } 633 634 // Restart will always return an error in the native proc backend, only for 635 // recorded traces. 636 func (*dummyRecordingManipulation) Restart(*ContinueOnceContext, string) (Thread, error) { 637 return nil, ErrNotRecorded 638 } 639 640 var ErrWaitForNotImplemented = errors.New("waitfor not implemented") 641 642 func (waitFor *WaitFor) Valid() bool { 643 return waitFor != nil && waitFor.Name != "" 644 }