mirror of
				https://github.com/go-delve/delve.git
				synced 2025-10-31 18:57:18 +08:00 
			
		
		
		
	 60c58acb8e
			
		
	
	60c58acb8e
	
	
	
		
			
			Displays the return values of the current function when we step out of it after executing a step, next or stepout command. Implementation of this feature is tricky: when the function has returned the return variables are not in scope anymore. Implementing this feature requires evaluating variables that are out of scope, using a stack frame that doesn't exist anymore. We can't calculate the address of these variables when the next/step/stepout command is initiated either, because between that point and the time where the stepout breakpoint is actually hit the goroutine stack could grow and be moved to a different memory address.
		
			
				
	
	
		
			412 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			412 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package core
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"go/ast"
 | |
| 	"io"
 | |
| 	"sync"
 | |
| 
 | |
| 	"github.com/derekparker/delve/pkg/proc"
 | |
| )
 | |
| 
 | |
| // A SplicedMemory represents a memory space formed from multiple regions,
 | |
| // each of which may override previously regions. For example, in the following
 | |
| // core, the program text was loaded at 0x400000:
 | |
| // Start               End                 Page Offset
 | |
| // 0x0000000000400000  0x000000000044f000  0x0000000000000000
 | |
| // but then it's partially overwritten with an RW mapping whose data is stored
 | |
| // in the core file:
 | |
| // Type           Offset             VirtAddr           PhysAddr
 | |
| //                FileSiz            MemSiz              Flags  Align
 | |
| // LOAD           0x0000000000004000 0x000000000049a000 0x0000000000000000
 | |
| //                0x0000000000002000 0x0000000000002000  RW     1000
 | |
| // This can be represented in a SplicedMemory by adding the original region,
 | |
| // then putting the RW mapping on top of it.
 | |
| type SplicedMemory struct {
 | |
| 	readers []readerEntry
 | |
| }
 | |
| 
 | |
| type readerEntry struct {
 | |
| 	offset uintptr
 | |
| 	length uintptr
 | |
| 	reader proc.MemoryReader
 | |
| }
 | |
| 
 | |
| // Add adds a new region to the SplicedMemory, which may override existing regions.
 | |
| func (r *SplicedMemory) Add(reader proc.MemoryReader, off, length uintptr) {
 | |
| 	if length == 0 {
 | |
| 		return
 | |
| 	}
 | |
| 	end := off + length - 1
 | |
| 	newReaders := make([]readerEntry, 0, len(r.readers))
 | |
| 	add := func(e readerEntry) {
 | |
| 		if e.length == 0 {
 | |
| 			return
 | |
| 		}
 | |
| 		newReaders = append(newReaders, e)
 | |
| 	}
 | |
| 	inserted := false
 | |
| 	// Walk through the list of regions, fixing up any that overlap and inserting the new one.
 | |
| 	for _, entry := range r.readers {
 | |
| 		entryEnd := entry.offset + entry.length - 1
 | |
| 		switch {
 | |
| 		case entryEnd < off:
 | |
| 			// Entry is completely before the new region.
 | |
| 			add(entry)
 | |
| 		case end < entry.offset:
 | |
| 			// Entry is completely after the new region.
 | |
| 			if !inserted {
 | |
| 				add(readerEntry{off, length, reader})
 | |
| 				inserted = true
 | |
| 			}
 | |
| 			add(entry)
 | |
| 		case off <= entry.offset && entryEnd <= end:
 | |
| 			// Entry is completely overwritten by the new region. Drop.
 | |
| 		case entry.offset < off && entryEnd <= end:
 | |
| 			// New region overwrites the end of the entry.
 | |
| 			entry.length = off - entry.offset
 | |
| 			add(entry)
 | |
| 		case off <= entry.offset && end < entryEnd:
 | |
| 			// New reader overwrites the beginning of the entry.
 | |
| 			if !inserted {
 | |
| 				add(readerEntry{off, length, reader})
 | |
| 				inserted = true
 | |
| 			}
 | |
| 			overlap := entry.offset - off
 | |
| 			entry.offset += overlap
 | |
| 			entry.length -= overlap
 | |
| 			add(entry)
 | |
| 		case entry.offset < off && end < entryEnd:
 | |
| 			// New region punches a hole in the entry. Split it in two and put the new region in the middle.
 | |
| 			add(readerEntry{entry.offset, off - entry.offset, entry.reader})
 | |
| 			add(readerEntry{off, length, reader})
 | |
| 			add(readerEntry{end + 1, entryEnd - end, entry.reader})
 | |
| 			inserted = true
 | |
| 		default:
 | |
| 			panic(fmt.Sprintf("Unhandled case: existing entry is %v len %v, new is %v len %v", entry.offset, entry.length, off, length))
 | |
| 		}
 | |
| 	}
 | |
| 	if !inserted {
 | |
| 		newReaders = append(newReaders, readerEntry{off, length, reader})
 | |
| 	}
 | |
| 	r.readers = newReaders
 | |
| }
 | |
| 
 | |
| // ReadMemory implements MemoryReader.ReadMemory.
 | |
| func (r *SplicedMemory) ReadMemory(buf []byte, addr uintptr) (n int, err error) {
 | |
| 	started := false
 | |
| 	for _, entry := range r.readers {
 | |
| 		if entry.offset+entry.length < addr {
 | |
| 			if !started {
 | |
| 				continue
 | |
| 			}
 | |
| 			return n, fmt.Errorf("hit unmapped area at %v after %v bytes", addr, n)
 | |
| 		}
 | |
| 
 | |
| 		// Don't go past the region.
 | |
| 		pb := buf
 | |
| 		if addr+uintptr(len(buf)) > entry.offset+entry.length {
 | |
| 			pb = pb[:entry.offset+entry.length-addr]
 | |
| 		}
 | |
| 		pn, err := entry.reader.ReadMemory(pb, addr)
 | |
| 		n += pn
 | |
| 		if err != nil || pn != len(pb) {
 | |
| 			return n, err
 | |
| 		}
 | |
| 		buf = buf[pn:]
 | |
| 		addr += uintptr(pn)
 | |
| 		if len(buf) == 0 {
 | |
| 			// Done, don't bother scanning the rest.
 | |
| 			return n, nil
 | |
| 		}
 | |
| 	}
 | |
| 	if n == 0 {
 | |
| 		return 0, fmt.Errorf("offset %v did not match any regions", addr)
 | |
| 	}
 | |
| 	return n, nil
 | |
| }
 | |
| 
 | |
| // OffsetReaderAt wraps a ReaderAt into a MemoryReader, subtracting a fixed
 | |
| // offset from the address. This is useful to represent a mapping in an address
 | |
| // space. For example, if program text is mapped in at 0x400000, an
 | |
| // OffsetReaderAt with offset 0x400000 can be wrapped around file.Open(program)
 | |
| // to return the results of a read in that part of the address space.
 | |
| type OffsetReaderAt struct {
 | |
| 	reader io.ReaderAt
 | |
| 	offset uintptr
 | |
| }
 | |
| 
 | |
| func (r *OffsetReaderAt) ReadMemory(buf []byte, addr uintptr) (n int, err error) {
 | |
| 	return r.reader.ReadAt(buf, int64(addr-r.offset))
 | |
| }
 | |
| 
 | |
| type Process struct {
 | |
| 	bi                proc.BinaryInfo
 | |
| 	core              *Core
 | |
| 	breakpoints       proc.BreakpointMap
 | |
| 	currentThread     *Thread
 | |
| 	selectedGoroutine *proc.G
 | |
| 	common            proc.CommonProcess
 | |
| }
 | |
| 
 | |
| type Thread struct {
 | |
| 	th     *LinuxPrStatus
 | |
| 	fpregs []proc.Register
 | |
| 	p      *Process
 | |
| 	common proc.CommonThread
 | |
| }
 | |
| 
 | |
| var ErrWriteCore = errors.New("can not to core process")
 | |
| var ErrShortRead = errors.New("short read")
 | |
| var ErrContinueCore = errors.New("can not continue execution of core process")
 | |
| 
 | |
| func OpenCore(corePath, exePath string) (*Process, error) {
 | |
| 	core, err := readCore(corePath, exePath)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	p := &Process{
 | |
| 		core:        core,
 | |
| 		breakpoints: proc.NewBreakpointMap(),
 | |
| 		bi:          proc.NewBinaryInfo("linux", "amd64"),
 | |
| 	}
 | |
| 	for _, thread := range core.Threads {
 | |
| 		thread.p = p
 | |
| 	}
 | |
| 
 | |
| 	var wg sync.WaitGroup
 | |
| 	err = p.bi.LoadBinaryInfo(exePath, &wg)
 | |
| 	wg.Wait()
 | |
| 	if err == nil {
 | |
| 		err = p.bi.LoadError()
 | |
| 	}
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	for _, th := range p.core.Threads {
 | |
| 		p.currentThread = th
 | |
| 		break
 | |
| 	}
 | |
| 	p.selectedGoroutine, _ = proc.GetG(p.CurrentThread())
 | |
| 
 | |
| 	return p, nil
 | |
| }
 | |
| 
 | |
| func (p *Process) BinInfo() *proc.BinaryInfo {
 | |
| 	return &p.bi
 | |
| }
 | |
| 
 | |
| func (p *Process) Recorded() (bool, string)                { return true, "" }
 | |
| func (p *Process) Restart(string) error                    { return ErrContinueCore }
 | |
| func (p *Process) Direction(proc.Direction) error          { return ErrContinueCore }
 | |
| func (p *Process) When() (string, error)                   { return "", nil }
 | |
| func (p *Process) Checkpoint(string) (int, error)          { return -1, ErrContinueCore }
 | |
| func (p *Process) Checkpoints() ([]proc.Checkpoint, error) { return nil, nil }
 | |
| func (p *Process) ClearCheckpoint(int) error               { return errors.New("checkpoint not found") }
 | |
| 
 | |
| func (thread *Thread) ReadMemory(data []byte, addr uintptr) (n int, err error) {
 | |
| 	n, err = thread.p.core.ReadMemory(data, addr)
 | |
| 	if err == nil && n != len(data) {
 | |
| 		err = ErrShortRead
 | |
| 	}
 | |
| 	return n, err
 | |
| }
 | |
| 
 | |
| func (thread *Thread) WriteMemory(addr uintptr, data []byte) (int, error) {
 | |
| 	return 0, ErrWriteCore
 | |
| }
 | |
| 
 | |
| func (t *Thread) Location() (*proc.Location, error) {
 | |
| 	f, l, fn := t.p.bi.PCToLine(t.th.Reg.Rip)
 | |
| 	return &proc.Location{PC: t.th.Reg.Rip, File: f, Line: l, Fn: fn}, nil
 | |
| }
 | |
| 
 | |
| func (t *Thread) Breakpoint() proc.BreakpointState {
 | |
| 	return proc.BreakpointState{}
 | |
| }
 | |
| 
 | |
| func (t *Thread) ThreadID() int {
 | |
| 	return int(t.th.Pid)
 | |
| }
 | |
| 
 | |
| func (t *Thread) Registers(floatingPoint bool) (proc.Registers, error) {
 | |
| 	r := &Registers{&t.th.Reg, nil}
 | |
| 	if floatingPoint {
 | |
| 		r.fpregs = t.fpregs
 | |
| 	}
 | |
| 	return r, nil
 | |
| }
 | |
| 
 | |
| func (t *Thread) Arch() proc.Arch {
 | |
| 	return t.p.bi.Arch
 | |
| }
 | |
| 
 | |
| func (t *Thread) BinInfo() *proc.BinaryInfo {
 | |
| 	return &t.p.bi
 | |
| }
 | |
| 
 | |
| func (t *Thread) StepInstruction() error {
 | |
| 	return ErrContinueCore
 | |
| }
 | |
| 
 | |
| func (t *Thread) Blocked() bool {
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| func (t *Thread) SetCurrentBreakpoint() error {
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (t *Thread) Common() *proc.CommonThread {
 | |
| 	return &t.common
 | |
| }
 | |
| 
 | |
| func (p *Process) Breakpoints() *proc.BreakpointMap {
 | |
| 	return &p.breakpoints
 | |
| }
 | |
| 
 | |
| func (p *Process) ClearBreakpoint(addr uint64) (*proc.Breakpoint, error) {
 | |
| 	return nil, proc.NoBreakpointError{Addr: addr}
 | |
| }
 | |
| 
 | |
| func (p *Process) ClearInternalBreakpoints() error {
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (p *Process) ContinueOnce() (proc.Thread, error) {
 | |
| 	return nil, ErrContinueCore
 | |
| }
 | |
| 
 | |
| func (p *Process) StepInstruction() error {
 | |
| 	return ErrContinueCore
 | |
| }
 | |
| 
 | |
| func (p *Process) RequestManualStop() error {
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (p *Process) CheckAndClearManualStopRequest() bool {
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| func (p *Process) CurrentThread() proc.Thread {
 | |
| 	return p.currentThread
 | |
| }
 | |
| 
 | |
| func (p *Process) Detach(bool) error {
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (p *Process) Exited() bool {
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| func (p *Process) Common() *proc.CommonProcess {
 | |
| 	return &p.common
 | |
| }
 | |
| 
 | |
| func (p *Process) Pid() int {
 | |
| 	return p.core.Pid
 | |
| }
 | |
| 
 | |
| func (p *Process) ResumeNotify(chan<- struct{}) {
 | |
| }
 | |
| 
 | |
| func (p *Process) SelectedGoroutine() *proc.G {
 | |
| 	return p.selectedGoroutine
 | |
| }
 | |
| 
 | |
| func (p *Process) SetBreakpoint(addr uint64, kind proc.BreakpointKind, cond ast.Expr) (*proc.Breakpoint, error) {
 | |
| 	return nil, ErrWriteCore
 | |
| }
 | |
| 
 | |
| func (p *Process) SwitchGoroutine(gid int) error {
 | |
| 	g, err := proc.FindGoroutine(p, gid)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if g == nil {
 | |
| 		// user specified -1 and selectedGoroutine is nil
 | |
| 		return nil
 | |
| 	}
 | |
| 	if g.Thread != nil {
 | |
| 		return p.SwitchThread(g.Thread.ThreadID())
 | |
| 	}
 | |
| 	p.selectedGoroutine = g
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (p *Process) SwitchThread(tid int) error {
 | |
| 	if th, ok := p.core.Threads[tid]; ok {
 | |
| 		p.currentThread = th
 | |
| 		p.selectedGoroutine, _ = proc.GetG(p.CurrentThread())
 | |
| 		return nil
 | |
| 	}
 | |
| 	return fmt.Errorf("thread %d does not exist", tid)
 | |
| }
 | |
| 
 | |
| func (p *Process) ThreadList() []proc.Thread {
 | |
| 	r := make([]proc.Thread, 0, len(p.core.Threads))
 | |
| 	for _, v := range p.core.Threads {
 | |
| 		r = append(r, v)
 | |
| 	}
 | |
| 	return r
 | |
| }
 | |
| 
 | |
| func (p *Process) FindThread(threadID int) (proc.Thread, bool) {
 | |
| 	t, ok := p.core.Threads[threadID]
 | |
| 	return t, ok
 | |
| }
 | |
| 
 | |
| type Registers struct {
 | |
| 	*LinuxCoreRegisters
 | |
| 	fpregs []proc.Register
 | |
| }
 | |
| 
 | |
| func (r *Registers) Slice() []proc.Register {
 | |
| 	var regs = []struct {
 | |
| 		k string
 | |
| 		v uint64
 | |
| 	}{
 | |
| 		{"Rip", r.Rip},
 | |
| 		{"Rsp", r.Rsp},
 | |
| 		{"Rax", r.Rax},
 | |
| 		{"Rbx", r.Rbx},
 | |
| 		{"Rcx", r.Rcx},
 | |
| 		{"Rdx", r.Rdx},
 | |
| 		{"Rdi", r.Rdi},
 | |
| 		{"Rsi", r.Rsi},
 | |
| 		{"Rbp", r.Rbp},
 | |
| 		{"R8", r.R8},
 | |
| 		{"R9", r.R9},
 | |
| 		{"R10", r.R10},
 | |
| 		{"R11", r.R11},
 | |
| 		{"R12", r.R12},
 | |
| 		{"R13", r.R13},
 | |
| 		{"R14", r.R14},
 | |
| 		{"R15", r.R15},
 | |
| 		{"Orig_rax", r.Orig_rax},
 | |
| 		{"Cs", r.Cs},
 | |
| 		{"Eflags", r.Eflags},
 | |
| 		{"Ss", r.Ss},
 | |
| 		{"Fs_base", r.Fs_base},
 | |
| 		{"Gs_base", r.Gs_base},
 | |
| 		{"Ds", r.Ds},
 | |
| 		{"Es", r.Es},
 | |
| 		{"Fs", r.Fs},
 | |
| 		{"Gs", r.Gs},
 | |
| 	}
 | |
| 	out := make([]proc.Register, 0, len(regs))
 | |
| 	for _, reg := range regs {
 | |
| 		if reg.k == "Eflags" {
 | |
| 			out = proc.AppendEflagReg(out, reg.k, reg.v)
 | |
| 		} else {
 | |
| 			out = proc.AppendQwordReg(out, reg.k, reg.v)
 | |
| 		}
 | |
| 	}
 | |
| 	out = append(out, r.fpregs...)
 | |
| 	return out
 | |
| }
 |