mirror of
https://github.com/beego/bee.git
synced 2024-11-15 08:30:54 +00:00
157 lines
4.5 KiB
Go
157 lines
4.5 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)
|
||
|
}
|
||
|
|
||
|
// MemoryReadWriter is an interface for reading or writing to
|
||
|
// the targets memory. This allows us to read from the actual
|
||
|
// target memory or possibly a cache.
|
||
|
type MemoryReadWriter interface {
|
||
|
MemoryReader
|
||
|
WriteMemory(addr uintptr, data []byte) (written int, err error)
|
||
|
}
|
||
|
|
||
|
type memCache struct {
|
||
|
loaded bool
|
||
|
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)) {
|
||
|
if !m.loaded {
|
||
|
_, err := m.mem.ReadMemory(m.cache, m.cacheAddr)
|
||
|
if err != nil {
|
||
|
return 0, err
|
||
|
}
|
||
|
m.loaded = true
|
||
|
}
|
||
|
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
|
||
|
}
|
||
|
case *compositeMemory:
|
||
|
return mem
|
||
|
}
|
||
|
return &memCache{false, addr, make([]byte, size), 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
|
||
|
}
|
||
|
|
||
|
// bufferMemoryReadWriter is dummy a MemoryReadWriter backed by a []byte.
|
||
|
type bufferMemoryReadWriter struct {
|
||
|
buf []byte
|
||
|
}
|
||
|
|
||
|
func (mem *bufferMemoryReadWriter) ReadMemory(buf []byte, addr uintptr) (n int, err error) {
|
||
|
copy(buf, mem.buf[addr-fakeAddress:][:len(buf)])
|
||
|
return len(buf), nil
|
||
|
}
|
||
|
|
||
|
func (mem *bufferMemoryReadWriter) WriteMemory(addr uintptr, data []byte) (written int, err error) {
|
||
|
copy(mem.buf[addr-fakeAddress:], data)
|
||
|
return len(data), nil
|
||
|
}
|