mirror of
				https://github.com/go-delve/delve.git
				synced 2025-10-31 10:47:27 +08:00 
			
		
		
		
	 62fe792bfd
			
		
	
	62fe792bfd
	
	
	
		
			
			Our current frame caching strategy doesn't handle extended locations expressions correctly, disable it on variables that don't have a simple address.
		
			
				
	
	
		
			143 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			143 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package proc
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 
 | |
| 	"github.com/derekparker/delve/pkg/dwarf/op"
 | |
| )
 | |
| 
 | |
| const cacheEnabled = true
 | |
| 
 | |
| // MemoryReader is like io.ReaderAt, but the offset is a uintptr so that it
 | |
| // can address all of 64-bit memory.
 | |
| // Redundant with memoryReadWriter but more easily suited to working with
 | |
| // the standard io package.
 | |
| type MemoryReader interface {
 | |
| 	// ReadMemory is just like io.ReaderAt.ReadAt.
 | |
| 	ReadMemory(buf []byte, addr uintptr) (n int, err error)
 | |
| }
 | |
| 
 | |
| type MemoryReadWriter interface {
 | |
| 	MemoryReader
 | |
| 	WriteMemory(addr uintptr, data []byte) (written int, err error)
 | |
| }
 | |
| 
 | |
| type memCache struct {
 | |
| 	cacheAddr uintptr
 | |
| 	cache     []byte
 | |
| 	mem       MemoryReadWriter
 | |
| }
 | |
| 
 | |
| func (m *memCache) contains(addr uintptr, size int) bool {
 | |
| 	return addr >= m.cacheAddr && addr <= (m.cacheAddr+uintptr(len(m.cache)-size))
 | |
| }
 | |
| 
 | |
| func (m *memCache) ReadMemory(data []byte, addr uintptr) (n int, err error) {
 | |
| 	if m.contains(addr, len(data)) {
 | |
| 		copy(data, m.cache[addr-m.cacheAddr:])
 | |
| 		return len(data), nil
 | |
| 	}
 | |
| 
 | |
| 	return m.mem.ReadMemory(data, addr)
 | |
| }
 | |
| 
 | |
| func (m *memCache) WriteMemory(addr uintptr, data []byte) (written int, err error) {
 | |
| 	return m.mem.WriteMemory(addr, data)
 | |
| }
 | |
| 
 | |
| func cacheMemory(mem MemoryReadWriter, addr uintptr, size int) MemoryReadWriter {
 | |
| 	if !cacheEnabled {
 | |
| 		return mem
 | |
| 	}
 | |
| 	if size <= 0 {
 | |
| 		return mem
 | |
| 	}
 | |
| 	switch cacheMem := mem.(type) {
 | |
| 	case *memCache:
 | |
| 		if cacheMem.contains(addr, size) {
 | |
| 			return mem
 | |
| 		} else {
 | |
| 			cache := make([]byte, size)
 | |
| 			_, err := cacheMem.mem.ReadMemory(cache, addr)
 | |
| 			if err != nil {
 | |
| 				return mem
 | |
| 			}
 | |
| 			return &memCache{addr, cache, mem}
 | |
| 		}
 | |
| 	case *compositeMemory:
 | |
| 		return mem
 | |
| 	}
 | |
| 	cache := make([]byte, size)
 | |
| 	_, err := mem.ReadMemory(cache, addr)
 | |
| 	if err != nil {
 | |
| 		return mem
 | |
| 	}
 | |
| 	return &memCache{addr, cache, mem}
 | |
| }
 | |
| 
 | |
| // fakeAddress used by extractVarInfoFromEntry for variables that do not
 | |
| // have a memory address, we can't use 0 because a lot of code (likely
 | |
| // including client code) assumes that addr == 0 is nil
 | |
| const fakeAddress = 0xbeef0000
 | |
| 
 | |
| // compositeMemory represents a chunk of memory that is stored in CPU
 | |
| // registers or non-contiguously.
 | |
| //
 | |
| // When optimizations are enabled the compiler will store some variables
 | |
| // into registers and sometimes it will also store structs non-contiguously
 | |
| // with some fields stored into CPU registers and other fields stored in
 | |
| // memory.
 | |
| type compositeMemory struct {
 | |
| 	realmem MemoryReadWriter
 | |
| 	regs    op.DwarfRegisters
 | |
| 	pieces  []op.Piece
 | |
| 	data    []byte
 | |
| }
 | |
| 
 | |
| func newCompositeMemory(mem MemoryReadWriter, regs op.DwarfRegisters, pieces []op.Piece) *compositeMemory {
 | |
| 	cmem := &compositeMemory{realmem: mem, regs: regs, pieces: pieces, data: []byte{}}
 | |
| 	for _, piece := range pieces {
 | |
| 		if piece.IsRegister {
 | |
| 			reg := regs.Bytes(piece.RegNum)
 | |
| 			sz := piece.Size
 | |
| 			if sz == 0 && len(pieces) == 1 {
 | |
| 				sz = len(reg)
 | |
| 			}
 | |
| 			cmem.data = append(cmem.data, reg[:sz]...)
 | |
| 		} else {
 | |
| 			buf := make([]byte, piece.Size)
 | |
| 			mem.ReadMemory(buf, uintptr(piece.Addr))
 | |
| 			cmem.data = append(cmem.data, buf...)
 | |
| 		}
 | |
| 	}
 | |
| 	return cmem
 | |
| }
 | |
| 
 | |
| func (mem *compositeMemory) ReadMemory(data []byte, addr uintptr) (int, error) {
 | |
| 	addr -= fakeAddress
 | |
| 	if addr >= uintptr(len(mem.data)) || addr+uintptr(len(data)) > uintptr(len(mem.data)) {
 | |
| 		return 0, errors.New("read out of bounds")
 | |
| 	}
 | |
| 	copy(data, mem.data[addr:addr+uintptr(len(data))])
 | |
| 	return len(data), nil
 | |
| }
 | |
| 
 | |
| func (mem *compositeMemory) WriteMemory(addr uintptr, data []byte) (int, error) {
 | |
| 	//TODO(aarzilli): implement
 | |
| 	return 0, errors.New("can't write composite memory")
 | |
| }
 | |
| 
 | |
| // DereferenceMemory returns a MemoryReadWriter that can read and write the
 | |
| // memory pointed to by pointers in this memory.
 | |
| // Normally mem and mem.Dereference are the same object, they are different
 | |
| // only if this MemoryReadWriter is used to access memory outside of the
 | |
| // normal address space of the inferior process (such as data contained in
 | |
| // registers, or composite memory).
 | |
| func DereferenceMemory(mem MemoryReadWriter) MemoryReadWriter {
 | |
| 	switch mem := mem.(type) {
 | |
| 	case *compositeMemory:
 | |
| 		return mem.realmem
 | |
| 	}
 | |
| 	return mem
 | |
| }
 |