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